This question already has answers here:
Fetch the rows which have the Max value for a column for each distinct value of another column
(35 answers)
Select First Row of Every Group in sql [duplicate]
(2 answers)
Return row with the max value of one column per group [duplicate]
(3 answers)
SQL: getting the max value of one column and the corresponding other columns [duplicate]
(2 answers)
Closed 12 months ago.
I have a query where I might get only one or possibly multiple records for each CONTROLNBR + LINENBR combo -
SELECT ACCTNBR, BOOKDATE, CONTROLNBR, LINENBR, TRANDETSEQ,
ING1, ING1AMT, ING2, ING2AMT, ING3, ING3AMT, ING4, ING4AMT
FROM TABLE1 WHERE COSTCENTER = '9999' ORDER BY CONTROLNBR, LINENBR,
TRANDETSEQ
Some data returned might be -
12345 01-FEB-19 3771 1 5 R4 13 Y1 41 Y3 1
12345 01-FEB-19 3771 1 11 R4 21 Y1 41 Y3 9
12345 01-FEB-19 3771 1 12 R4 21 Y1 41 Y3 17
12345 01-FEB-19 3771 1 18 R4 21 Y1 61 Y3 45
12345 01-FEB-19 3772 3 5 R2 3 Y3 38 L1 8
12345 01-FEB-19 3773 1 5 N1 8 W1 2 L1 42
12345 01-FEB-19 3773 1 12 N1 10 W1 4 L1 45
24568 01-FEB-19 3786 2 4 L1 28 G2 40 N1 13
24568 01-FEB-19 3786 2 8 L1 28 G2 45 N1 18
24568 01-FEB-19 3786 2 12 L1 28 G2 48 N1 18
In each case, I would just need the line with the highest TRANDETSEQ number for each CONTROLNBR + LINENBR combo. So I would only want -
12345 01-FEB-19 3771 1 18 R4 21 Y1 61 Y3 45
12345 01-FEB-19 3772 3 5 R2 3 Y3 38 L1 8
12345 01-FEB-19 3773 1 12 N1 10 W1 4 L1 45
24568 01-FEB-19 3786 2 12 L1 28 G2 48 N1 18
Is there some function or something like LAST or MAX that might work here?
Rank them, and then fetch row(s) that rank as the highest.
Sample data:
SQL> with test (acctnbr, controlnbr, linenbr, trandatseq) as
2 (select 12345, 3771, 1, 5 from dual union all
3 select 12345, 3771, 1, 11 from dual union all
4 select 12345, 3771, 1, 12 from dual union all
5 select 12345, 3771, 1, 18 from dual union all
6 --
7 select 12345, 3772, 3, 5 from dual union all
8 --
9 select 12345, 3773, 1, 5 from dual union all
10 select 12345, 3773, 1, 12 from dual union all
11 --
12 select 24568, 3786, 2, 4 from dual union all
13 select 24568, 3786, 2, 12 from dual
14 ),
Query begins here:
15 temp as
16 -- sort them
17 (select t.*,
18 rank() over (partition by acctnbr, controlnbr , linenbr order by trandatseq desc) rnk
19 from test t
20 )
21 -- finally, fetch rows that rank as "highest"
22 select acctnbr, controlnbr, linenbr, trandatseq
23 from temp
24 where rnk = 1;
ACCTNBR CONTROLNBR LINENBR TRANDATSEQ
---------- ---------- ---------- ----------
12345 3771 1 18
12345 3772 3 5
12345 3773 1 12
24568 3786 2 12
SQL>
Related
Input Output ResultsHope you are doing good.
I am stuck in a requirement where I need to have records distributed into multiple records based on the duration I get it from a linking table.
Suppose I have a volume of 100 and duration I am getting is 20 months linking table then my output should have 20 records of each 5(100/20). Could you please help me with the query how to do this SQL.
The WITH clause is here just to generate some sample data and, as such, it is not a part of the answer.
You can join the tables ON PRODUCT columns, limit the iterations using LEVEL <= DURATION, group the data and show the amount either as Min, Max or Avg of COST/DURATION rounded to two decimals. I put all of the data in the select list. Here is the complete code with the result. Regards...
WITH
t_duration AS
(
Select 'A' "PRODUCT", 10 "DURATION" From Dual Union All
Select 'B' "PRODUCT", 6 "DURATION" From Dual Union All
Select 'C' "PRODUCT", 4 "DURATION" From Dual
),
t_cost AS
(
Select 'A' "PRODUCT", 100 "COST" From Dual Union All
Select 'B' "PRODUCT", 50 "COST" From Dual Union All
Select 'C' "PRODUCT", 40 "COST" From Dual
)
SELECT
LEVEL "MONTH_ORDER_NUMBER",
d.PRODUCT "PRODUCT",
d.DURATION "DURATION",
c.COST "COST",
Round(Avg(c.COST / d.DURATION), 2) "AVG_MONTHLY_AMOUNT",
Round(Max(c.COST / d.DURATION), 2) "MAX_MONTHLY_AMOUNT",
Round(Min(c.COST / d.DURATION), 2) "MIN_MONTHLY_AMOUNT"
FROM
t_duration d
INNER JOIN
t_cost c ON(c.PRODUCT = d.PRODUCT)
CONNECT BY LEVEL <= d.DURATION
GROUP BY
d.PRODUCT, d.DURATION, c.COST, LEVEL
ORDER BY
d.PRODUCT, LEVEL
--
-- R e s u l t
--
-- MONTH_ORDER_NUMBER PRODUCT DURATION COST AVG_MONTHLY_AMOUNT MAX_MONTHLY_AMOUNT MIN_MONTHLY_AMOUNT
-- ------------------ ------- ---------- ---------- ------------------ ------------------ ------------------
-- 1 A 10 100 10 10 10
-- 2 A 10 100 10 10 10
-- 3 A 10 100 10 10 10
-- 4 A 10 100 10 10 10
-- 5 A 10 100 10 10 10
-- 6 A 10 100 10 10 10
-- 7 A 10 100 10 10 10
-- 8 A 10 100 10 10 10
-- 9 A 10 100 10 10 10
-- 10 A 10 100 10 10 10
-- 1 B 6 50 8.33 8.33 8.33
-- 2 B 6 50 8.33 8.33 8.33
-- 3 B 6 50 8.33 8.33 8.33
-- 4 B 6 50 8.33 8.33 8.33
-- 5 B 6 50 8.33 8.33 8.33
-- 6 B 6 50 8.33 8.33 8.33
-- 1 C 4 40 10 10 10
-- 2 C 4 40 10 10 10
-- 3 C 4 40 10 10 10
-- 4 C 4 40 10 10 10
That looks as if ntile would do the job (at least, that's how I understood the question).
Here's a table with 100 rows (that's your "volume of 100").
SQL> create table test (id) as
2 select level from dual connect by level <= 100;
Table created.
You'd then pass 20 (that's "duration of 20 months") to ntile and get the result - see the grp column, having 20 groups, each of them having 5 rows:
SQL> select id, ntile(20) over (order by id) grp
2 from test
3 order by id;
ID GRP
---------- ----------
1 1
2 1
3 1
4 1
5 1
6 2
7 2
8 2
9 2
10 2
11 3
12 3
13 3
14 3
15 3
<snip>
91 19
92 19
93 19
94 19
95 19
96 20
97 20
98 20
99 20
100 20
100 rows selected.
SQL>
[EDIT, based on new information]
With sample tables you posted:
SQL> with
2 duration (product, duration) as
3 (select 'A', 10 from dual union all
4 select 'B', 6 from dual union all
5 select 'C', 4 from dual
6 ),
7 cost (product, cost) as
8 (select 'A', 100 from dual union all
9 select 'B', 50 from dual union all
10 select 'C', 40 from dual
11 )
query would look like this:
12 select d.product,
13 c.cost / d.duration as amount
14 from duration d join cost c on c.product = d.product
15 cross join table(cast(multiset(select level from dual
16 connect by level <= d.duration
17 ) as sys.odcinumberlist))
18 order by d.product;
PRODUCT AMOUNT
---------- ----------
A 10
A 10
A 10
A 10
A 10
A 10
A 10
A 10
A 10
A 10
B 8,33333333
B 8,33333333
B 8,33333333
B 8,33333333
B 8,33333333
B 8,33333333
C 10
C 10
C 10
C 10
20 rows selected.
SQL>
I have the following script :-
SELECT
quoteid,
tariff_length,
cost
FROM
tblquotesnew q
LEFT JOIN
tbltariffsnew t
ON q.tariff_id = t.tariff
which may return something like:-
quoteid tariff_length cost
310 4 12
311 6 16
Is it possible to INSERT rows into a seperate table, where the number of rows inserted is based tariff_length?
So, using the above, the insertion table (tblcommnew) would look like
commid quoteid cost
1 310 12
2 310 12
3 310 12
4 310 12
5 311 16
6 311 16
7 311 16
8 311 16
9 311 16
10 311 16
Here's one option:
SQL> with test (quoteid, tariff_length, cost) as
2 (select 310, 4, 12 from dual union
3 select 311, 6, 16 from dual
4 )
5 select rownum as commid, quoteid, cost
6 from test,
7 table(cast(multiset(select level from dual
8 connect by level <= tariff_length
9 ) as sys.odcinumberlist));
COMMID QUOTEID COST
---------- ---------- ----------
1 310 12
2 310 12
3 310 12
4 310 12
5 311 16
6 311 16
7 311 16
8 311 16
9 311 16
10 311 16
10 rows selected.
SQL>
A slight variation on #Littlefoot's approach is to use an XMLTable to generate the combinations:
with tblquotesnew (quoteid, tariff_length, cost) as (
select 310, 4, 12 from dual
union all select 311, 6, 16 from dual
)
select rownum as commid, quoteid, cost
from tblquotesnew
cross join xmltable ('1 to xs:integer($n)' passing tariff_length as "n");
COMMID QUOTEID COST
---------- ---------- ----------
1 310 12
2 310 12
3 310 12
4 310 12
5 311 16
6 311 16
7 311 16
8 311 16
9 311 16
10 311 16
As an insert you then just do:
insert into tblcommnew (commid, quoteid, cost)
select rownum, quoteid, cost
from tblquotesnew
cross join xmltable ('1 to xs:integer($n)' passing tariff_length as "n");
10 rows inserted.
How To Arrange Table Data in Parent_Id And Child_Id Wise Via Sql-Server Query?
My Query
select CLevel_Id,Category_Name,Parent_Id,Child_Id,Level_Id from Category_Level
order by Parent_Id asc
Current Output
CLevel_Id Category_Name Parent_Id Child_Id Level_Id
12 Jewelry 1 0 1
14 Rings 2 1 2
15 Men-Rings 3 2 3
17 Women-Rings 4 2 3
18 Earrings 5 1 2
20 Women-Earings 6 5 3
1013 Metal-Fashion 7 3 4
1015 Diamond-Fashion 8 4 4
1016 Semi-Set 9 6 4
Expected Output
CLevel_Id Category_Name Parent_Id Child_Id Level_Id
12 Jewelry 1 0 1
14 Rings 2 1 2
15 Men-Rings 3 2 3
1013 Metal-Fashion 7 3 4
17 Women-Rings 4 2 3
1015 Diamond-Fashion 8 4 4
18 Earrings 5 1 2
20 Women-Earings 6 5 3
1016 Semi-Set 9 6 4
please help me
use a Recursive CTE.
Assuming you have max 9 per level. Using single digit as the seq level. If you have more than 9, you will need to use 2 digits like 01, 02 etc
; with
rcte as
(
-- Anchor member, seq = 1
select *, seq = convert(varchar(100), '1')
from Category_Level
where Child_Id = 0
union all
-- recursive member, concatenate to the seq
select c.*, seq = convert(varchar(100),
r.seq
+ convert(varchar(10), row_number() over (partition by r.seq
order by c.Child_Id)))
from Category_Level c
inner join rcte r on c.Child_Id = r.Parent_Id
)
select *
from rcte
order by seq
/* RESULT
CLevel_Id Category_Name Parent_Id Child_Id Level_Id seq
----------- -------------------- ----------- ----------- ----------- -------
12 Jewelry 1 0 1 1
14 Rings 2 1 2 11
15 Men-Rings 3 2 3 111
1013 Metal-Fashion 7 3 4 1111
17 Women-Rings 4 2 3 112
1015 Diamond-Fashion 8 4 4 1121
18 Earrings 5 1 2 12
20 Women-Earings 6 5 3 121
1016 Semi-Set 9 6 4 1211
(9 rows affected)
*/
I am using the following statement;
SELECT RESV_ID, BOOKING_CUS_ID, ACC_ID,
(SELECT F.FLI_PRICE FROM FLIGHT F WHERE F.FLI_ID = R.IN_FLIGHT_ID) AS DEPART_FLIGHT_PRICE,
(SELECT F1.FLI_PRICE FROM FLIGHT F1 WHERE F1.FLI_ID = R.OUT_FLIGHT_ID) AS RETURN_FLIGHT_PRICE,
(SELECT AC.ACC_PRICEPN FROM ACCOMMODATION AC WHERE AC.ACC_ID = R.ACC_ID) AS ACCOMMODATION_PRICE
FROM HOLIDAY_RESERVATION R;
to yield the following results;
RESV_ID BOOKING_CUS_ID ACC_ID DEPART_FLIGHT_PRICE RETURN_FLIGHT_PRICE ACCOMMODATION_PRICE
---------- -------------- ---------- ------------------- ------------------- -------------------
1 1 2 520 450 350
2 3 4 250 150 150
3 5 6 290 300 450
4 7 7 399 450 650
5 9 365 345
6 11 558 460
7 13 250 250
8 15 550 550
9 17 25 250
10 19 19 450
10 rows selected.
Question:
How do I sum up the price fields, SOME PRICES ARE NOT AVAILABLE because a reservation was either made for accommodation only or flight only, hence both values will not be present always and this is where the issue lies
DEPART_FLIGHT_PRICE RETURN_FLIGHT_PRICE ACCOMMODATION_PRICE
Furthermore:
I wish to insert or update the SUM of those three values into a SUBTOTAL in the reservation table, perhaps by using select into or update, I have spent a whole day trying to do this but my skills are limited. any help will be greatly appreciated.
Flight table
FLI_ID FLI_CO FLI_AIRCRA DEPT_AIRPORT ARRV_AIRPORT DEPT_TIME ARRV_TIME FLI_PRICE
1 BD425 Boeing 707 1 12 18-MAR-12 02.24.00 AM 18-MAR-12 06.24.00 AM 520
2 LX345 Beriev 30 6 7 20-MAR-12 03.30.00 PM 20-MAR-12 04.20.00 PM 250
3 NZ4445 Boeing 720 9 14 25-MAR-12 09.00.00 AM 25-MAR-12 05.00.00 PM 290
4 TP351 Boeing 767 10 15 25-MAR-12 11.25.00 AM 25-MAR-12 03.35.00 PM 399
5 BA472 Boeing 720 5 14 26-MAR-12 01.05.00 PM 26-MAR-12 04.15.00 PM 365
Accommodation
ACC_ID ACC_TYPE_CODE ACC_DESC ACC_PRICEPN ACC_ROOMS RESORT_ID ACC_ADDR CITY_ID
1 1 Three bedroom bungalow near theme park 500 3 1
2 1 Two bedroom bungalow next to disney house 350 2 1
3 1 One bedroom bungalow with lake view 250 2 2
4 2 One bedroom chalet near the lake 150 1 2
5 2 Four bedroom chalet near the tree house 600 4 3
Reservation
RESV_ID EMP_ID BOOKING_CUS_ID RESV_DATE HOLIDAY_S HOLIDAY_E IN_FLIGHT_ID OUT_FLIGHT_ID IN_FLIGHT_SEATS_NO OUT_FLIGHT_SEATS_NO ACC_ID SUBTOTAL
1 338 1 16-FEB-12 18-MAR-12 20-APR-12 1 11 2 2 2
2 335 3 10-JAN-12 20-MAR-12 22-APR-12 2 12 2 2 4
3 338 5 05-MAR-12 25-MAR-12 26-APR-12 3 13 2 2 6
4 328 7 02-JAN-12 25-MAR-12 25-APR-12 4 14 2 2 7
5 311 9 20-JAN-12 26-MAR-12 21-APR-12 5 15 2 2
6 317 11 07-JAN-12 27-MAR-12 22-APR-12 6 16 2 2
7 344 13 29-FEB-12 15-MAR-12 12-APR-12 7 17 2 2
8 326 15 11-JAN-12 18-MAR-12 12-APR-14 8 18 2 2
9 329 17 16-JAN-12 19-MAR-12 17-APR-12 25
10 323 19 18-FEB-12 20-MAR-12 21-APR-12 19
Okay I managed to yield the results that i wanted
SELECT HR.RESV_ID, F_IN.FLI_ID, F_IN.FLI_PRICE, F_OUT.FLI_ID, F_OUT.FLI_PRICE, AC.ACC_ID, AC.ACC_PRICEPN, NVL(F_IN.FLI_PRICE,0)+NVL(F_OUT.FLI_PRICE,0)+NVL(AC.ACC_PRICEPN,0) AS TOTAL
FROM HOLIDAY_RESERVATION HR
LEFT JOIN FLIGHT F_IN ON HR.IN_FLIGHT_ID = F_IN.FLI_ID
LEFT JOIN FLIGHT F_OUT ON HR.OUT_FLIGHT_ID = F_OUT.FLI_ID
LEFT JOIN ACCOMMODATION AC ON HR.ACC_ID = AC.ACC_ID
ORDER BY HR.RESV_ID;
YIELDS
RESV_ID FLI_ID FLI_PRICE FLI_ID FLI_PRICE ACC_ID ACC_PRICEPN TOTAL
---------- ---------- ---------- ---------- ---------- ---------- ----------- ----------
1 1 500 11 555 2 350 1405
2 2 150 12 253 4 150 553
3 3 300 13 345 6 450 1095
4 4 450 14 343 7 650 1443
5 5 345 15 242 587
6 6 460 16 460 920
7 7 250 17 250 500
8 8 550 18 550 1100
9 25 250 250
10 19 450 450
And the following statement is to update the reservation table.
Thanks to Leigh Riffel from DBA stackxchange for the following code
UPDATE HOLIDAY_RESERVATION R SET SUBTOTAL =
NVL((SELECT F.FLI_PRICE FROM FLIGHT F WHERE F.FLI_ID = R.IN_FLIGHT_ID), 0) +
NVL((SELECT F.FLI_PRICE FROM FLIGHT F WHERE F.FLI_ID = R.OUT_FLIGHT_ID), 0) +
NVL((SELECT AC.ACC_PRICEPN FROM ACCOMMODATION AC WHERE AC.ACC_ID = R.ACC_ID), 0);
Now the subtotal is populated with the values obtained from the sum performed above >>
RESV_ID EMP_ID BOOKING_CUS_ID RESV_DATE HOLIDAY_S HOLIDAY_E IN_FLIGHT_ID OUT_FLIGHT_ID IN_FLIGHT_SEATS_NO OUT_FLIGHT_SEATS_NO ACC_ID SUBTOTAL
---------- ---------- -------------- --------- --------- --------- ------------ ------------- ------------------ ------------------- ---------- ----------
1 338 1 16-FEB-12 18-MAR-12 20-APR-12 1 11 2 2 2 1405
2 335 3 10-JAN-12 20-MAR-12 22-APR-12 2 12 2 2 4 553
3 338 5 05-MAR-12 25-MAR-12 26-APR-12 3 13 2 2 6 1095
4 328 7 02-JAN-12 25-MAR-12 25-APR-12 4 14 2 2 7 1443
5 311 9 20-JAN-12 26-MAR-12 21-APR-12 5 15 2 2 587
6 317 11 07-JAN-12 27-MAR-12 22-APR-12 6 16 2 2 920
7 344 13 29-FEB-12 15-MAR-12 12-APR-12 7 17 2 2 500
8 326 15 11-JAN-12 18-MAR-12 12-APR-14 8 18 2 2 1100
9 329 17 16-JAN-12 19-MAR-12 17-APR-12 25 250
10 323 19 18-FEB-12 20-MAR-12 21-APR-12 19 450
Subsequently the code was added to a trigger (which was the original intention)
CREATE OR REPLACE TRIGGER HR_SUBTOTAL
BEFORE INSERT OR UPDATE ON HOLIDAY_RESERVATION
FOR EACH ROW
BEGIN
SELECT
NVL((SELECT F.Fli_Price FROM Flight F WHERE F.Fli_ID = :new.In_Flight_ID), 0) +
NVL((SELECT F.Fli_Price FROM Flight F WHERE F.Fli_ID = :new.Out_Flight_ID), 0) +
NVL((SELECT AC.Acc_PricePn FROM Accomodation AC WHERE AC.Acc_ID = :new.Acc_ID), 0)
INTO :new.Subtotal
FROM dual;
END;
/
For the SUM, assuming you want to treat NULL values as 0, you'd just need to do an NVL on the numbers
NVL( DEPART_FLIGHT_PRICE, 0 ) +
NVL( RETURN_FLIGHT_PRICE, 0 ) +
NVL( ACCOMMODATION_PRICE, 0 )
As for the UPDATE, it sounds like you just need a correlated UPDATE statement.
UPDATE reservation r
SET subtotal = (SELECT (SELECT NVL( DEPART_FLIGHT_PRICE, 0 ) +
NVL( RETURN_FLIGHT_PRICE, 0 ) +
NVL( ACCOMMODATION_PRICE, 0 )
FROM (SELECT RESV_ID,
BOOKING_CUS_ID,
ACC_ID,
(SELECT F.FLI_PRICE
FROM FLIGHT F
WHERE F.FLI_ID = R.IN_FLIGHT_ID) AS DEPART_FLIGHT_PRICE,
(SELECT F1.FLI_PRICE
FROM FLIGHT F1
WHERE F1.FLI_ID = R.OUT_FLIGHT_ID) AS RETURN_FLIGHT_PRICE,
(SELECT AC.ACC_PRICEPN
FROM ACCOMMODATION AC
WHERE AC.ACC_ID = R.ACC_ID) AS ACCOMMODATION_PRICE
FROM dual));
You are asking:
How do I sum up the price fields, as you can see some of them can have nulls.
DEPART_FLIGHT_PRICE RETURN_FLIGHT_PRICE ACCOMMODATION_PRICE
Just enclose them in NVL function as follows:
NVL(DEPART_FLIGHT_PRICE, 0)
and then sum them up.
For the second part, what you need is a MERGE statement. A good example is at http://www.oracle-developer.net/display.php?id=203
I have a table bill with column name bill_id. bill_id value range is from 1 to 40. And I have rows like
bill_id
-----------
1
3
6
8
2
21
34
35
26
40
How can I find the missing elements (4, 5, 7, 9, 10, etc.)?
SQL> create table bill (bill_id)
2 as
3 select 1 from dual union all
4 select 3 from dual union all
5 select 6 from dual union all
6 select 8 from dual union all
7 select 2 from dual union all
8 select 21 from dual union all
9 select 34 from dual union all
10 select 35 from dual union all
11 select 26 from dual union all
12 select 40 from dual
13 /
Table created.
SQL> with all_possible_bill_ids as
2 ( select level bill_id
3 from dual
4 connect by level <= 40
5 )
6 select bill_id
7 from all_possible_bill_ids
8 minus
9 select bill_id
10 from bill
11 /
BILL_ID
----------
4
5
7
9
10
11
12
13
14
15
16
17
18
19
20
22
23
24
25
27
28
29
30
31
32
33
36
37
38
39
30 rows selected.
Regards,
Rob.