I am using Oracle Forms and Reports. I am a beginner.
I have a table invoice having fields INVNO,INVDATE.
Invoice table:
Invno Invdate
INVNO1 03/03/2017
Another table passing having these fields :
Passing table
Invno debitcode credit amount sln
INVNO1 debit1 credit1 100 1
INVNO1 debit2 credit2 200 2
INVNO1 debit3 credit3 150 3
INVNO1 debit4 credit4 250 4
Number of records for debit and credit may vary and maximum number of records is 7.
In a form I want to display a horizontal row of record depending on number of records
Invoice No Debit1 Credit1 Amount Debit2 credit2 Amount ..Debit4 Credit4
Forms can't do anything "dynamic", so you'll have to hardcode all 7 credit/debit values, such as this (I've done it up to 3 values). I'd suggest you to create a view based on such a query:
SQL> with passing (invno, debit, credit, amount, sln) as
2 (select 'invno1', 'debit1', 'credit1', 100, 1 from dual union
3 select 'invno1', 'debit2', 'credit2', 200, 2 from dual union
4 select 'invno1', 'debit3', 'credit3', 150, 3 from dual union
5 select 'invno1', 'debit4', 'credit4', 250, 4 from dual)
6 select invno,
7 max(decode(sln, 1, debit)) debit1,
8 max(decode(sln, 1, credit)) credit1,
9 max(decode(sln, 1, amount)) amount1,
10 --
11 max(decode(sln, 2, debit)) debit2,
12 max(decode(sln, 2, credit)) credit2,
13 max(decode(sln, 2, amount)) amount2,
14 --
15 max(decode(sln, 3, debit)) debit3,
16 max(decode(sln, 3, credit)) credit3,
17 max(decode(sln, 3, amount)) amount3
18 from passing
19 group by invno;
INVNO DEBIT1 CREDIT1 AMOUNT1 DEBIT2 CREDIT2 AMOUNT2 DEBIT3 CREDIT3 AMOUNT3
------ ------ ------- ---------- ------ ------- ---------- ------ ------- ----------
invno1 debit1 credit1 100 debit2 credit2 200 debit3 credit3 150
As of Reports: well, the simplest option is to reuse query (or a view, as I've suggested) you used in Forms. Or, if you feel like it, create a matrix report which is capable of doing it dynamically.
P.S. Oh, yes - forgot to ask: title says "error". Which error? You didn't specify it.
Related
I'm new to PL SQL and have to write a function, which has customer_id as an input and has to output a product_name of the best selling product for that customer_id.
The schema looks like this:
I found a lot of simple examples where it includes two tables, but I can't seem to find one where you have to do multiple joins and use a function, while selecting only the best selling product.
I could paste a lot of very bad code here and how I tried to approach this, but this seems to be a bit over my head for current knowledge, since I've been learning PL SQL for less than 3 days now and got this task.
With some sample data (minimal column set):
SQL> select * from products order by product_id;
PRODUCT_ID PRODUCT_NAME
---------- ----------------
1 BMW
2 Audi
SQL> select * From order_items;
PRODUCT_ID CUSTOM QUANTITY UNIT_PRICE
---------- ------ ---------- ----------
1 Little 100 1
1 Little 200 2
2 Foot 300 3
If we check some totals:
SQL> select o.product_id,
2 o.customer_id,
3 sum(o.quantity * o.unit_price) total
4 from order_items o
5 group by o.product_id, o.customer_id;
PRODUCT_ID CUSTOM TOTAL
---------- ------ ----------
2 Little 400
1 Little 100
2 Foot 900
SQL>
It says that
for customer Little, product 2 was sold with total = 400 - that's our choice for Little
for customer Little, product 1 was sold with total = 100
for customer Foot, product 2 was sold with total = 900 - that's our choice for Foot
Query might then look like this:
temp CTE calculates totals per each customer
rank_them CTE ranks them in descending order per each customer; row_number so that you get only one product, even if there are ties
finally, select the one that ranks as the highest
SQL> with
2 temp as
3 (select o.product_id,
4 o.customer_id,
5 sum(o.quantity * o.unit_price) total
6 from order_items o
7 group by o.product_id, o.customer_id
8 ),
9 rank_them as
10 (select t.customer_id,
11 t.product_id,
12 row_number() over (partition by t.customer_id order by t.total desc) rn
13 from temp t
14 )
15 select * From rank_them;
CUSTOM PRODUCT_ID RN
------ ---------- ----------
Foot 2 1 --> for Foot, product 2 ranks as the highest
Little 2 1 --> for Little, product 1 ranks as the highest
Little 1 2
SQL>
Moved to a function:
SQL> create or replace function f_product (par_customer_id in order_items.customer_id%type)
2 return products.product_name%type
3 is
4 retval products.product_name%type;
5 begin
6 with
7 temp as
8 (select o.product_id,
9 o.customer_id,
10 sum(o.quantity * o.unit_price) total
11 from order_items o
12 group by o.product_id, o.customer_id
13 ),
14 rank_them as
15 (select t.customer_id,
16 t.product_id,
17 row_number() over (partition by t.customer_id order by t.total desc) rn
18 from temp t
19 )
20 select p.product_name
21 into retval
22 from rank_them r join products p on p.product_id = r.product_id
23 where r.customer_id = par_customer_id
24 and r.rn = 1;
25
26 return retval;
27 end;
28 /
Function created.
SQL>
Testing:
SQL> select f_product ('Little') result from dual;
RESULT
--------------------------------------------------------------------------------
Audi
SQL> select f_product ('Foot') result from dual;
RESULT
--------------------------------------------------------------------------------
Audi
SQL>
Now, you can improve it so that you'd care about no data found issue (when customer didn't buy anything), ties (but you'd then return a collection or a refcursor instead of a scalar value) etc.
[EDIT] I'm sorry, ORDERS table has to be included into the temp CTE; your data model is correct, you don't have to do anything about it - my query was wrong (small screen + late hours issue; not a real excuse, just saying).
So:
with
temp as
(select i.product_id,
o.customer_id,
sum(i.quantity * i.unit_price) total
from order_items i join orders o on o.order_id = i.order_id
group by i.product_id, o.customer_id
),
The rest of my code is - otherwise - unmodified.
I believe there is probably an easy way to solve these problems with pivots or partitions but I can't seem to find the proper solutions. I have a round about solution for problem 1 by using a long list of select sum()s and a long solution for problem 2 where I just select the count(*) from table B where id = id from table A multiple times in a (select) blocks but if I have a large number of IDs both of those solution equal very long SQL that gets very tedious and I'm sure there is a better way it is just eluding me.
I would really like solutions that would allow me to include a large set of multiple IDs or supply the solution with a table of IDs to evaluate.
Problem 1:
Table:
------------------
ID DESC YEAR
1 A 2021
1 B 2021
1 C 2021
2 A 2021
2 B 2021
2 C 2021
3 A 2019
3 B 2019
I would like to have the count of the ID's for each DESC by year.
Expected Result:
------------------
Year CountA CountB CountC
2019 1 1 0
2021 2 2 2
Problem 2:
Table A:
------------------
ID DESC
1 A
2 B
3 C
Table B:
------------------
SET ID
10 1
10 1
12 1
13 2
14 3
I would like to see (1) how many of each ID from Table A can be found in each SET in Table B and (2) how many of each ID from Table A can be found in each SET in Table B and not in any other SET of Table B (unique matches).
Expected Result 1:
------------------
ID Count10 Count12 Count13 Count14
1 2 1 0 0
2 0 0 1 0
3 0 0 0 1
Expected Result 2:
------------------
ID UniqueCount10 UniqueCount12 UniqueCount13 UniqueCount14
1 0 0 0 0
2 0 0 1 0
3 0 0 0 1
Thank you for any and all assistance.
All three problems can be solved with pivoting (calling Problem 2 "two different problems"), although it is not clear what purpose Result 2 would serve (in the second problem; see my comments to you).
Note that desc and set are reserved keywords, and year is a keyword, so they shouldn't be used as column names. I changed to descr, set_ (with an underscore) and yr. Also, I do not use double-quotes for column names in the output; all-caps column names are just fine.
In the second problem it is not clear why you need Table A. Could you have some id values that don't appear at all in Table B, but you still want them in the final output? If so, you will need to change my semi-joins to outer joins; left as an exercise, since it's a different (and much more basic) type of question.
In the first problem, you must pivot the result of a subquery, which selects only the relevant columns from the base table. There is no such need for the second problem (unless your tables have other columns that should not be considered - left for you to figure out).
Problem 1
Data:
create table tbl (id, descr, yr) as
select 1, 'A', 2021 from dual union all
select 1, 'B', 2021 from dual union all
select 1, 'C', 2021 from dual union all
select 2, 'A', 2021 from dual union all
select 2, 'B', 2021 from dual union all
select 2, 'C', 2021 from dual union all
select 3, 'A', 2019 from dual union all
select 3, 'B', 2019 from dual
;
Query and output:
select *
from (select descr, yr from tbl)
pivot (count(*) for descr in ('A' as count_a, 'B' as count_b, 'C' as count_c))
order by yr
;
YR COUNT_A COUNT_B COUNT_C
---- ------- ------- -------
2019 1 1 0
2021 2 2 2
Problem 2
Data:
create table table_a (id, descr) as
select 1, 'A' from dual union all
select 2, 'B' from dual union all
select 3, 'C' from dual
;
create table table_b (set_, id) as
select 10, 1 from dual union all
select 10, 1 from dual union all
select 12, 1 from dual union all
select 13, 2 from dual union all
select 14, 3 from dual
;
Part 1 - Query and result:
select *
from table_b
pivot (count(*) for set_ in (10 as count_10, 12 as count_12,
13 as count_13, 14 as count_14))
where id in (select id from table_a) -- is this needed?
order by id -- if needed
;
ID COUNT_10 COUNT_12 COUNT_13 COUNT_14
-- -------- -------- -------- --------
1 2 1 0 0
2 0 0 1 0
3 0 0 0 1
Part 2 - Query and result:
select *
from (
select id, case count(distinct set_) when 1 then max(set_) end as set_
from table_b
where id in (select id from table_a) -- is this needed?
group by id
)
pivot (count(*) for set_ in (10 as unique_ct_10, 12 as unique_ct_12,
13 as unique_ct_13, 14 as unique_ct_14))
order by id -- if needed
;
ID UNIQUE_CT_10 UNIQUE_CT_12 UNIQUE_CT_13 UNIQUE_CT_14
-- ------------ ------------ ------------ ------------
1 0 0 0 0
2 0 0 1 0
3 0 0 0 1
In this last part of the second problem, you might as well just take the subquery and run it separately - what's the purpose of pivoting its output?
I have a dataset in which one column is Branch-ID and other one is Branch Manager and it looks as follows in the given url.
dataset
I want to combine the branch managers into one single column based on the branch-id. For example if Bob and Sandra are two different branch-managers but have the same branch id which is branch-id=1, then we should concatenate them together as Bob-Sandra and place them in a separately created column.
I have attached the expected output for the above dataset. expected_output_dataset
I am currently using Oracle Analytics Cloud Professional Version.
I don't know Oracle Analytics, but - if it has anything to do with an Oracle database and its capabilities, then listagg helps.
Sample data in lines #1 - 10; query you might be interested in begins at line #11.
SQL> with test (account_id, branch_id, branch_manager) as
2 (select 1, 123, 'Sandra' from dual union all
3 select 3, 124, 'Martha' from dual union all
4 select 4, 125, 'John' from dual union all
5 select 6, 126, 'Andrew' from dual union all
6 select 7, 126, 'Mathew' from dual union all
7 select 2, 123, 'Michael' from dual union all
8 select 5, 125, 'David' from dual union all
9 select 8, 126, 'Mark' from dual
10 )
11 select a.account_id, a.branch_id, a.branch_manager,
12 b.concatenated_column
13 from test a join (select branch_id,
14 listagg(branch_manager, '-') within group (order by null) concatenated_column
15 from test
16 group by branch_id
17 ) b on b.branch_id = a.branch_id;
ACCOUNT_ID BRANCH_ID BRANCH_ CONCATENATED_COLUMN
---------- ---------- ------- -------------------------
1 123 Sandra Michael-Sandra
3 124 Martha Martha
4 125 John David-John
6 126 Andrew Andrew-Mark-Mathew
7 126 Mathew Andrew-Mark-Mathew
2 123 Michael Michael-Sandra
5 125 David David-John
8 126 Mark Andrew-Mark-Mathew
8 rows selected.
SQL>
Suppose I have a table with 10 records/tuples. Now I want to update an attribute of 6th record with the same attribute of 1st record, 2nd-7th, 3rd-8th, 4th-9th, 5th-10th in a go i.e. without using cursor/loop. Use of any number of temporary table is allowed. What is the strategy to do so?
PostgreSQL (and probably other RDBMSes) let you use self-joins in UPDATE statements just as you can in SELECT statements:
UPDATE tbl
SET attr = t2.attr
FROM tbl t2
WHERE tbl.id = t2.id + 5
AND tbl.id >= 6
This would be easy with an update-with-join but Oracle doesn't do that and the closest substitute can be very tricky to get to work. Here is the easiest way. It involves a subquery to get the new value and a correlated subquery in the where clause. It looks complicated but the set subquery should be self-explanatory.
The where subquery really only has one purpose: it connects the two tables, much as the on clause would do if we could do a join. Except that the field used from the main table (the one being updated) must be a key field. As it turns out, with the self "join" being performed below, they are both the same field, but it is required.
Add to the where clause other restraining criteria, as shown.
update Tuples t1
set t1.Attr =(
select t2.Attr
from Tuples t2
where t2.Attr = t1.Attr - 5 )
where exists(
select t2.KeyVal
from Tuples t2
where t1.KeyVal = t2.KeyVal)
and t1.Attr > 5;
SqlFiddle is pulling a hissy fit right now so here the data used:
create table Tuples(
KeyVal int not null primary key,
Attr int
);
insert into Tuples
select 1, 1 from dual union all
select 2, 2 from dual union all
select 3, 3 from dual union all
select 4, 4 from dual union all
select 5, 5 from dual union all
select 6, 6 from dual union all
select 7, 7 from dual union all
select 8, 8 from dual union all
select 9, 9 from dual union all
select 10, 10 from dual;
The table starts out looking like this:
KEYVAL ATTR
------ ----
1 1
2 2
3 3
4 4
5 5
6 6
7 7
8 8
9 9
10 10
with this result:
KEYVAL ATTR
------ ----
1 1
2 2
3 3
4 4
5 5
6 1
7 2
8 3
9 4
10 5
I created a table with the following attributes :
TABLE "VENDORACCOUNT"
( "VEN_ACCOUNTID"
"VEN_REGNO"
"VEN_TXDATE"
"VEN_INVOICE_REFNO"
"TOTALAMOUNT"
"PAID_TOVEN"
"BALANCE"
)
I take the TOTALAMOUNT column value through a POPUP LOV from another table on the basis of its VEN_INVOICE_REFNO value. Here the scenario is that the TOTALAMOUNT column value is subtracted from the PAID_TOVEN column value. But the next time I select the TOTALAMOUNT value it does not show me the updated value. It shows me the old value as shown in the report below.
Query of Report:
select "VEN_ACCOUNTID",
"VEN_REGNO" ,
"VEN_TXDATE" ,
"VEN_INVOICE_REFNO" as ,
"TOTALAMOUNT" as ,
"PAID_TOVEN" as ,
TOTALAMOUNT-PAID_TOVEN as "Balance"
from "VENDORACCOUNT"
In the above report I want that whenever I do the second entry it should show me the subtracted or updated value ie 1800 instead of 2800 and 4550 instead of 9550 respectively. So the next time I can subtract the amount from 1800 and 4550.
I created this trigger
create or replace trigger "VENDORACCOUNT_T2"
BEFORE
insert or update or delete on "VENDORACCOUNT"
for each row
begin
DECLARE new_balance INT;
DECLARE new_total INT;
DECLARE new_paid INT;
SELECT balance INTO old_balance,
total INTO old_total,
PAID_TOVEN INTO new_paid
FROM vendoraccount
WHERE ven_regno = new.ven_regno
AND VEN_INVOICE_REFNO = new.VEN_INVOICE_REFNO;
UPDATE vendoraccount SET TOTALAMOUNT = old_total + old_balance - new_paid,
balance = TOTALAMOUNT - new_paid
WHERE VEN_REGNO= new.VEN_REGNO
AND VEN_INVOICE_REFNO = new.VEN_INVOICE_REFNO;
end;
and am getting this error:
ERROR: PLS-00103: 'Encountered the symbol "DECLARE" when expecting one
of the following: begin function pragma procedure subtype type current curs'
I think you don't need a trigger. You can create a view based on the below query and then create the RECORD GROUP for your LOV based on the VIEW. The query would look like-
select accid,regno,inv_refno,LAG(bal,1,totalamount) OVER (PARTITION BY regno ORDER BY accid) "TOTALAMOUNT", paid_toven, bal
from (with temp_data as
(select 2 accid,3 regno, 16 inv_refno, 2800 totalamount, 1000 paid_toven from dual
union
select 3 accid,3 regno, 16 inv_refno, 2800 totalamount, 2000 paid_toven from dual
union
select 4 accid,8 regno, 22 inv_refno, 9550 totalamount, 5000 paid_toven from dual
union
select 5 accid,8 regno, 22 inv_refno, 9550 totalamount, 5000 paid_toven from dual
union
select 6 accid,8 regno, 22 inv_refno, 9550 totalamount, 8000 paid_toven from dual)
select accid,regno,inv_refno,totalamount,paid_toven,totalamount-paid_toven bal
from temp_data);
The output is -
ACCID REGNO INV_REFNO TOTALAMOUNT PAID_TOVEN BAL
----- ----- --------- ----------- ---------- ---
2 3 16 2800 1000 1800
3 3 16 1800 2000 800
4 8 22 9550 5000 4550
5 8 22 4550 5000 4550
6 8 22 4550 8000 1550
So based on your table the query would be-
select accid,regno,inv_refno,LAG(bal,1,totalamount) OVER (PARTITION BY regno ORDER BY accid) "TOTALAMOUNT", paid_toven, bal
from (select accid,regno,inv_refno,totalamount,paid_toven,totalamount-paid_toven bal
from VENDORACCOUNT);
The query does pretty much what you want, but your sample data does not look right. This is actually one example of a running total.