How can duplicate the data records based on period? - sql

I have a data set as periodic. However, these periods are not consecutive. My data pattern is like that
Period Customer_No Date Product
1 111 01.01.2017 X
3 111 05.09.2017 Y
8 111 02.05.2018 Z
6 222 02.02.2017 X
9 222 06.04.2017 Z
12 222 05.09.2018 B
15 222 02.01.2019 A
End of the period should be 15 for all customers. I want to create consecutive periods based on customers and fill them with previous data like below:
Period Customer_No Date Product
1 111 01.01.2017 X
2 111 01.01.2017 X
3 111 05.09.2017 Y
4 111 05.09.2017 Y
5 111 05.09.2017 Y
6 111 05.09.2017 Y
7 111 05.09.2017 Y
8 111 02.05.2018 Z
9 111 02.05.2018 Z
10 111 02.05.2018 Z
11 111 02.05.2018 Z
12 111 02.05.2018 Z
13 111 02.05.2018 Z
14 111 02.05.2018 Z
15 111 02.05.2018 Z
6 222 02.02.2017 X
7 222 02.02.2017 X
8 222 02.02.2017 X
9 222 06.04.2017 Z
10 222 06.04.2017 Z
11 222 06.04.2017 Z
12 222 05.09.2018 B
13 222 05.09.2018 B
14 222 05.09.2018 B
15 222 02.01.2019 A
create table tbl_cust(period int,Customer_No int, Date date, Product varchar)
insert into tbl_cust values(1,111,'01.01.2017','X')
insert into tbl_cust values(3,111,'05.09.2017','Y')
insert into tbl_cust values(8,111,'02.05.2018','Z')
insert into tbl_cust values(6,222,'02.02.2017','X')
insert into tbl_cust values(9,222,'06.04.2017','Z')
insert into tbl_cust values(12,222,'05.09.2018','B')
insert into tbl_cust values(15,222,'02.01.2019','A')

You can use a recursive CTE to generate the rows that you want. Then you need to fill them in with the most recent data. What you really want is lag(ignore nulls), but SQL Server does not support that functionality.
There are only up to 15 rows per customer, so apply is a reasonable alternative:
with cte as (
select min(period) as period, customer_no
from tbl_cust
group by customer_no
union all
select period + 1, customer_no
from cte
where period < 15
)
select cte.period, cte.customer_no, c.date, c.product
from cte cross apply
(select top (1) c.*
from tbl_cust c
where c.customer_no = cte.customer_no and
c.period <= cte.period
order by c.period desc
) c
order by cte.customer_no, cte.period;
Here is a db<>fiddle.

You can try this.
select ID as period, Customer_No, [Date], Product from
(VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12),(13),(14),(15)) P(ID)
OUTER APPLY( SELECT *, ROW_NUMBER() OVER(PARTITION BY Customer_No ORDER BY period desc) RN
FROM tbl_cust C WHERE C.period <= P.ID ) X
WHERE X.RN = 1
ORDER BY Customer_No, ID
Result:
period Customer_No Date Product
----------- ----------- ---------- -------
1 111 2017-01-01 X
2 111 2017-01-01 X
3 111 2017-05-09 Y
4 111 2017-05-09 Y
5 111 2017-05-09 Y
6 111 2017-05-09 Y
7 111 2017-05-09 Y
8 111 2018-02-05 Z
9 111 2018-02-05 Z
10 111 2018-02-05 Z
11 111 2018-02-05 Z
12 111 2018-02-05 Z
13 111 2018-02-05 Z
14 111 2018-02-05 Z
15 111 2018-02-05 Z
6 222 2017-02-02 X
7 222 2017-02-02 X
8 222 2017-02-02 X
9 222 2017-06-04 Z
10 222 2017-06-04 Z
11 222 2017-06-04 Z
12 222 2018-05-09 B
13 222 2018-05-09 B
14 222 2018-05-09 B
15 222 2019-02-01 A

Related

Count only original seconds with Oracle SQL

I have a table with this structure and data, with start and stop positions of an audio/video. I have to count the original seconds and discard the not original ones.
E.g.
CUSTOMER_ID ITEM_ID CHAPTER_ID START_POSITION END_POSITION
A 123456 1 6 0 97
B 123456 1 6 97 498
C 123456 1 6 498 678
D 123456 1 6 678 1332
E 123456 1 6 1180 1190
F 123456 1 6 1190 1206
G 123456 1 6 1364 1529
H 123456 1 6 1530 1531
Original Data
Lines "E" and "F" does not represent original seconds because "D" line starts at 678 and finishes with 1332 so I need to create a new set of lines like this:
CUSTOMER_ID ITEM_ID CHAPTER_ID START_POSITION END_POSITION
A 123456 1 6 0 97
B 123456 1 6 97 498
C 123456 1 6 498 678
D 123456 1 6 678 1332
E 123456 1 6 1364 1529
F 123456 1 6 1530 1531
New Result Set
Can you help mw with this?
If I am following you correctly, you can use not exists to filter out rows whose range is contained in the range of another row:
select t.*
from mytable t
where not exists (
select 1
from mytable t1
where
t1.customer_id = t.customer_id
and t1.start_position < t.start_position
and t1.end_position > t.end_position
)
You can use the self join as follows:
Select distinct t.*
from your_table t
Left Join your_table tt
On t.customer_id = tt.customer_id
And t.item_id = tt.item_id
And t.chapter_id = tt.chapter_id
And t.rowid <> tt.rowid
And t.start_position between tt.start_position and tt.end_position - 1
Where tt.rowid is null

T SQL CTE Previous Row Calculation

I'm using SQL Server 2016.
I have the below table:
SKU Shop Week ShopPrioirty Replen Open_Stk Open_Stk Calc
111 100 1 1 0 17 NULL
111 200 1 2 2 NULL NULL
111 300 1 3 0 NULL NULL
111 400 1 4 0 NULL NULL
222 100 2 1 5 17 NULL
222 200 2 2 5 NULL NULL
222 300 2 3 5 NULL NULL
222 400 2 4 5 NULL NULL
This is the desired result:
SKU Shop Week ShopPrioirty Replen Open_Stk Open_Stk Calc
111 100 1 1 0 17 17
111 200 1 2 2 NULL 17
111 300 1 3 0 NULL 15
111 400 1 4 0 NULL 15
222 100 2 1 20 17 17
222 200 2 2 15 NULL 12
222 300 2 3 12 NULL 7
222 400 2 4 10 NULL 2
I need to update the 'Open_Stk Calc' based on the previous row:
'Open_Stk Calc' - IIF('Replen'<=IIF('Open_Stk'>=0,'Open_Stk',0),'Replen',0)
I am using a CTE to update a row based on a calculation of the previous rows. This is my SQL:
;WITH CTE AS
(
SELECT
SKU,
[Shop],
[Week],
[Store_Priority],
[Replen],
[Open_Stk],
[Open_Stk Calc],
FIRST_VALUE([Open_Stk]) OVER ( PARTITION BY [SKU] ,[Week] ORDER BY [Store_Priority] ROWS UNBOUNDED PRECEDING)
-
ISNULL(SUM(IIF([Replen] <= IIF([Open_Stk]>=0,[Open_Stk],0),[Replen],0))
OVER (PARTITION BY [SKU] ,[Week] ORDER BY [Store_Priority] ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING), 0) AS CurrentStock
FROM [tblTEST])
UPDATE CTE
SET [Open_Stk Calc] = CurrentStock
However, this produces the following result:
SKU Shop Week ShopPrioirty Replen Open_Stk Open_Stk Calc
111 100 1 1 0 17 17
111 200 1 2 2 NULL 17
111 300 1 3 0 NULL 17
111 400 1 4 0 NULL 17
And not the desired result - where have I gone wrong?
As one can see in the MS documentation, the OVER clauses supports specific kind of functions:
Ranking functions
Aggregate functions
Analytic functions
NEXT VALUE FOR function
None of them include IIF, as Luis Cazares noted in their comment.
Your code indicates you do have a clue about what you are doing - maybe you forgot to put your IIF inside a SUM?

How to find all (group-based) rows containing MIN/MAX values for a particular column in SQL Server?

I have temporary table (#TempPackages) which looks like this:
EntryId (PK) PackageId SubProductID SubProductSequence
1 1111 17 3
2 1111 28 4
3 1111 33 1
4 1111 67 5
5 1111 122 2
6 2222 18 4
7 2222 29 5
8 2222 33 9
9 2222 103 7
10 2222 99 11
11 3333 256 5
12 3333 333 6
13 3333 789 3
14 3333 1023 2
15 3333 9845 1
I need a query which will give me the rows with the minimum/maximum SubProductSequence value for each unique PackageId. For the table above, the query would return this:
EntryId (PK) PackageId SubProductID SubProductSequence
3 1111 33 1
4 1111 67 5
6 2222 18 4
10 2222 99 11
12 3333 333 6
15 3333 9845 1
The EntryId column was something that I added while trying to solve this, as it gives me a unique column to join the same table on to (to ensure I still only end up with 15 rows in my joined table).
I tried this - just to get the MIN():
SELECT
*
FROM
#TempPackages p1
INNER JOIN
#TempPackages p2 ON p1.EntryId = p2.EntryId
AND p1.SubProductSequence = (
SELECT
MIN(SubProductSequence)
FROM
#DeparturesToUpdate)
Obviously this is wrong, because the INNER JOIN is superfluous and the SELECT MIN() clause is wrong as it selects the rows with the minimum overall sequence numbers, not the minimum sequence numbers per package.
Any suggestions about the best way to do this?
One way is to use the ROW_NUMBER() function:
SELECT
EntryId
, PackageId
, SubProductID
, SubProductSequence
FROM
( SELECT
EntryId
, PackageId
, SubProductID
, SubProductSequence
, ROW_NUMBER() OVER (PARTITION BY PackageId
ORDER BY SubProductSequence ASC)
AS rna
, ROW_NUMBER() OVER (PARTITION BY PackageId
ORDER BY SubProductSequence DESC)
AS rnd
FROM
#TempPackages
) AS tmp
WHERE
rna = 1
OR rnd = 1 ;
ROW_NUMBER() is a ranking function that is used with OVER clause. What it basically does in this case, is it groups the rows with same PackageId (that is done with the PARTITION BY PackageId), then orders them by SubProductSequence (ascending or descending) and assigns a row_number, starting from 1 for each packageId.
So, the subquery would return this, if it was run alone:
EntryId (PK) PackageId SubProductID SubProductSequence rna rnd
3 1111 33 1 1 5
5 1111 122 2 2 4
1 1111 17 3 3 3
2 1111 28 4 4 2
4 1111 67 5 5 1
6 2222 18 4 1 5
7 2222 29 5 2 4
9 2222 103 7 3 3
8 2222 33 9 4 2
10 2222 99 11 5 1
15 3333 9845 1 1 5
14 3333 1023 2 2 4
13 3333 789 3 3 3
11 3333 256 5 4 2
12 3333 333 6 5 1
The WHERE condition added in the external query is obvious afterwards.
Improving on Bohemian's idea -
;WITH MinMax AS
(SELECT PackageId ,
MIN(SubProductSequence) [Min],
MAX(SubProductSequence) [Max]
FROM #TempPackages
GROUP BY PackageId )
SELECT EntryId, SubProductSequence, TP.PackageId, SubProductID FROM #TempPkges TP
INNER JOIN MinMax MM ON TP.PackageId = MM.PackageId
AND (SubProductSequence = MM.[Min] OR SubProductSequence = MM.[Max])
And then, you can add your own ORDER BY
WITH t1 AS
(SELECT PackageId,MIN(SubProductSequence) minm,MAX(SubProductSequence) maxm
FROM ##TempPackages
GROUPBY PackageId
)
SELECT pk.EntryId, pk.PackageId,pk.SubProductID, pk.SubProductSequence
FROM ##TempPackages pk INNER JOIN t1
ON pk.PackageId = t1.PackageId
WHERE pk.SubProductSequence = t1.minm OR
pk.SubProductSequence = t1.maxm

Update or Select into ORACLE

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

How to vertically flip data in BQL?

Title. For example, I have below data:
Key1 Key2 Cost Qty_LIFO Date
Red A 2 19 1/4/2018
Red A 3 18 1/3/2018
Red C 4 7 1/2/2018
Red A 5 16 1/1/2018
Blu B 21 91 1/4/2018
Blu B 31 81 1/3/2018
Blu D 41 70 1/2/2018
Blu D 51 60 1/1/2018
The goal is to transform the data to look like below. Flip the quantity column, while also taking into account the Keys/categories
Key1 Key2 Cost Qty_FIFO Date
Red A 2 16 1/4/2018
Red A 3 18 1/3/2018
Red C 4 7 1/2/2018
Red A 5 19 1/1/2018
Blu B 21 81 1/4/2018
Blu B 31 91 1/3/2018
Blu D 41 60 1/2/2018
Blu D 51 70 1/1/2018
or like this (Qty_FIFO is flipped and added to the first example at the top):
Key1 Key2 Cost Qty_LIFO Qty_FIFO Date
Red A 2 19 16 1/4/2018
Red A 3 18 18 1/3/2018
Red C 4 7 7 1/2/2018
Red A 5 16 19 1/1/2018
Blu B 21 91 81 1/4/2018
Blu B 31 81 91 1/3/2018
Blu D 41 70 60 1/2/2018
Blu D 51 60 70 1/1/2018
The purpose of this is to calculate LIFO and FIFO costs.
I need to take Qty_LIFO column (which is sorted by date, descending), flip it vertically (so the data becomes Date ASC), and re-add it to the table without changing the sorting of the Costs column.
Basically, I need to pair the newest Cost data with the oldest Qty data and continue from there.
This is a hack, which only works if you have access to row_number()
CREATE TABLE existing_qry(
Key1 VARCHAR(3) NOT NULL
,Key2 VARCHAR(1) NOT NULL
,Cost INTEGER NOT NULL
,Qty_LIFO INTEGER NOT NULL
,Date DATE NOT NULL
);
INSERT INTO existing_qry(Key1,Key2,Cost,Qty_LIFO,Date) VALUES ('Red','A',2,19,'1/4/2018');
INSERT INTO existing_qry(Key1,Key2,Cost,Qty_LIFO,Date) VALUES ('Red','A',3,18,'1/3/2018');
INSERT INTO existing_qry(Key1,Key2,Cost,Qty_LIFO,Date) VALUES ('Red','C',4,7,'1/2/2018');
INSERT INTO existing_qry(Key1,Key2,Cost,Qty_LIFO,Date) VALUES ('Red','A',5,16,'1/1/2018');
INSERT INTO existing_qry(Key1,Key2,Cost,Qty_LIFO,Date) VALUES ('Blu','B',21,91,'1/4/2018');
INSERT INTO existing_qry(Key1,Key2,Cost,Qty_LIFO,Date) VALUES ('Blu','B',31,81,'1/3/2018');
INSERT INTO existing_qry(Key1,Key2,Cost,Qty_LIFO,Date) VALUES ('Blu','D',41,70,'1/2/2018');
INSERT INTO existing_qry(Key1,Key2,Cost,Qty_LIFO,Date) VALUES ('Blu','D',51,60,'1/1/2018');
with cte as (
select
*
, row_number() over(partition by key1 order by date ASC) rn_asc
, row_number() over(partition by key1 order by date DESC) rn_desc
from existing_qry
)
select
t.Key1, t.Key2, t.Cost, t.Qty_LIFO, flip.Qty_LIFO as Qty_FIFO, t.Date, t.rn_asc, t.rn_desc
from cte as t
inner join cte as flip on t.key1 = flip.key1 and t.rn_asc = flip.rn_desc
It calculates 2 numbers for each row in opposite date order, then aligns rows by requiring these to be equal through a self join. This has the impact of reversing the LIFO numbers (or "flipping" that column).
Key1 Key2 Cost Qty_LIFO Qty_FIFO Date rn_asc rn_desc
---- ------ ------ ------ ---------- ---------- --------------------- -------- ---------
1 Blu B 21 91 60 04.01.2018 00:00:00 4 1
2 Blu B 31 81 70 03.01.2018 00:00:00 3 2
3 Blu D 41 70 81 02.01.2018 00:00:00 2 3
4 Blu D 51 60 91 01.01.2018 00:00:00 1 4
5 Red A 2 19 16 04.01.2018 00:00:00 4 1
6 Red A 3 18 7 03.01.2018 00:00:00 3 2
7 Red C 4 7 18 02.01.2018 00:00:00 2 3
8 Red A 5 16 19 01.01.2018 00:00:00 1 4
https://rextester.com/LQBVD29253