Gaps and Islands with continuous ranges from previous row - sql

I have the following set of Data
SET
START
END
QTY
A
1
10
10
A
11
20
10
A
21
30
10
B
51
60
10
B
61
70
10
B
81
90
10
B
91
100
10
C
101
200
100
C
201
300
100
C
401
500
100
And wanted to have the following result:
SET
START
END
TOTAL_QTY
A
1
30
30
B
51
70
20
B
81
100
20
C
101
300
200
C
401
500
100
So it will check previous "End" range and if it's a continuous from the previous "Start" range then it will be grouped into one "Start - End" ranges with the Sum of Qty.
I don't know how this can be achieved with Oracle SQL, can anyone help?

select "SET"
,min("START") as "START"
,max("END") as "END"
,sum(QTY) as QTY
from (
select t.*
,count(mrk) over(partition by "SET" order by "START") as grp
from (
select t.*
,case when "START" - lag("END") over(partition by "SET" order by "START") > 1 then 1 end as mrk
from t
) t
) t
group by "SET", grp
SET
START
END
QTY
A
1
30
30
B
51
70
20
B
81
100
20
C
101
300
200
C
401
500
100
Fiddle

Related

Find max value over the next 7 days for each group

I have a SQL table:
id
date
value
1
01/01/2019
50
1
01/13/2019
24
1
01/19/2019
53
2
01/05/2019
50
2
01/11/2019
24
2
01/24/2019
53
I want to create a new column that computes that max value over the next 14 days grouped by id. If the difference between the date in the current row and the next is greater than 14, return None or Null.
The new table will be:
id
date
value
max_14
1
01/01/2019
50
50
1
01/13/2019
24
53
1
01/19/2019
53
None
2
01/05/2019
50
50
2
01/11/2019
24
53
2
01/24/2019
53
None
You can use a sub-query for this:
select t.*, (
select max(value)
from t as x
where x.id = t.id
and x.date >= t.date
and x.date < dateadd(day, 14, t.date)
)
from t

Cumulative sum in SQL using window function

QTY
STOCK
RNK
ID KEY
CUM SUM
40
35
1
1
35
20
35
2
1
0
15
35
3
1
0
58
35
4
1
0
18
35
5
1
0
40
35
1
2
35
20
35
2
2
0
15
35
3
2
0
CUM SUM should be MIN(QTY, STOCK-SUM(all rows in cumsum before the current row)) for every other row and for 1st row it should be MIN(QTY, STOCK-SUM(0))=> MIN(QTY,STOCK)
QTY
STOCK
RNK
ID KEY
CUM SUM
40
35
1
1
5
20
35
2
1
-10
15
35
3
1
-30
58
35
4
1
-7
18
35
5
1
-24
40
35
1
2
5
20
35
2
2
-10
15
35
3
2
-30
After, I tried I am getting the above output
SELECT sum(qty-stock) over (
partition by ID KEY
ORDER BY rnk
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
) as CUM SUM
FROM TABLE
Need to get correct cumsum value using a window function in the existing table
You may use a rolling SUM() here, using SUM() as an analytic function:
SELECT *, SUM(QTY - STOCK) OVER (PARTITION BY ID_KEY ORDER BY RNK) AS CUM_SUM
FROM yourTable
ORDER BY ID_KEY, RNK;

Oracle - Getting the rows w/ condition of two columns having minimum values

I am a newbie to PLSQL. I would like to ask for your help.
I have a table below.
Item
Week
Qty
DMD_WK
DMD_QTY
ACC_DMD
WIP
H00978A510
26
300
26
0
0
1
H00978A510
26
300
27
0
0
2
H00978A510
26
300
28
300
300
3
H00978A510
26
300
29
100
400
3
H00978A510
26
300
30
100
500
4
first of all, I want to filter the records that has QTY < ACC_DMD. so the result will be below(I'm okay w/ this part)
Item
Week
Qty
DMD_WK
DMD_QTY
ACC_DMD
WIP
H00978A510
26
300
29
100
400
3
H00978A510
26
300
30
100
500
4
then, I need to get the row having the minimum DMD_WK and also having the minimum WIP grouped by item, week and qty after the filter being applied(Need help on this part)
so that the query will result in this:
Item
Week
Qty
DMD_WK
DMD_QTY
ACC_DMD
WIP
H00978A510
26
300
29
100
400
3
Hoping for your time and help, thanks in advance.
SELECT *
FROM YOURTABLE t
WHERE
DMD_WK = (SELECT MIN(tin.DMD_WK)
FROM YOURTABLE tin
WHERE tin.ITEM = t.ITEM AND tin.QTY < tin.ACC_DMD
GROUP BY tin.ITEM )
AND WIP = (SELECT MIN(tin.WIP)
FROM YOURTABLE tin
WHERE tin.ITEM = t.ITEM AND tin.QTY < tin.ACC_DMD
GROUP BY tin.ITEM )

Properly 'Joining' two Cross Applies

I've got a query with three Cross-Applies that gather data from three different tables. The first Cr-Ap assists the 2nd and 3rd Cr-Ap's. It finds the most recent ID of a certain refill for a 'cartridge', the higher the ID the more recent the refill.
The second and third Cr-Ap's gather the SUMS of items that have been refilled and items that have been dispensed under the most recent Refill.
If I run the query for Cr-Ap 2 or 3 separately the output would look something like:
ID Amount
1 100
2 1000
3 100
4 0
5 0
etc
Amount would be either the amount of dispensed or refilled items.
Only I don't want to run these queries separately, I want them next to each other.
So what I want is a table that looks like this:
ID Refill Dispense
1 100 1
2 1000 5
3 100 7
4 0 99
5 0 3
etc
My gut tells me to do
INNER JOIN crossaply2 ON crossapply3.ID = crossapply2.ID
But this doesn't work. I'm still new to SQL so I don't exactly know what I can and can't join, what I do know is that you can use crossapply as a join (sorta?). I think that might be what I need to do here, I just don't know how.
But that's not it, there's another complication, there are certain refills where nothing gets dispensed. In these scenarios the crossapply I wrote for dispenses won't return anything for that refillID. With nothing I don't mean NULL, I mean it just skips the refillID. But I'd like to see a 0 in those cases. Because it just skips over those ID's I can't get COALESCE or ISNULL to work, this might also complicate the joining of these two applies. Because an INNER JOIN would skip any line where there is no Dispensed amount, even though there is a Refilled amount Id like to see.
Here is my code:
-- Dispensed SUM and Refilled SUM combined
SELECT [CartridgeRefill].[FK_CartridgeRegistration_Id]
,Refills.Refilled
,Dispenses.Dispensed
FROM [CartridgeRefill]
CROSS APPLY(
SELECT MAX([CartridgeRefill].[Id]) AS RecentRefillID
FROM [CartridgeRefill]
GROUP BY [CartridgeRefill].[FK_CartridgeRegistration_Id]
) AS RecentRefill
CROSS APPLY(
SELECT [CartridgeRefill].[FK_CartridgeRegistration_Id] AS RefilledID
,SUM([CartridgeRefillMedication].[Amount]) AS Refilled
FROM [CartridgeRefillMedication]
INNER JOIN [CartridgeRefill] ON [CartridgeRefillMedication].[FK_CartridgeRefill_Id] = [CartridgeRefill].[Id]
WHERE [CartridgeRefillMedication].[FK_CartridgeRefill_Id] = RecentRefill.RecentRefillID
GROUP BY [CartridgeRefill].[FK_CartridgeRegistration_Id]
) AS Refills
CROSS APPLY(
SELECT [CartridgeRefill].[FK_CartridgeRegistration_Id] AS DispensedID
,SUM([CartridgeDispenseAttempt].[Amount]) AS Dispensed
FROM [CartridgeDispenseAttempt]
INNER JOIN [CartridgeRefill] ON [CartridgeDispenseAttempt].[FK_CartridgeRefill_Id] = [CartridgeRefill].[Id]
WHERE [CartridgeDispenseAttempt].[FK_CartridgeRefill_Id] = RecentRefill.RecentRefillID
GROUP BY [CartridgeRefill].[FK_CartridgeRegistration_Id]
) AS Dispenses
GO
The output of this code is as follows:
1 300 1
1 300 1
1 200 194
1 200 194
1 200 8
1 200 8
1 0 39
1 0 39
1 100 14
1 100 14
1 200 1
1 200 1
1 0 28
1 0 28
1 1000 102
1 1000 102
1 1000 557
1 1000 557
1 2000 92
1 2000 92
1 100 75
1 100 75
1 100 100
1 100 100
1 100 51
1 100 51
1 600 28
1 600 28
1 200 47
1 200 47
1 200 152
1 200 152
1 234 26
1 234 26
1 0 227
1 0 227
1 10 6
1 10 6
1 300 86
1 300 86
1 0 194
1 0 194
1 500 18
1 500 18
1 1000 51
1 1000 51
1 1000 56
1 1000 56
1 500 48
1 500 48
1 0 10
1 0 10
1 1500 111
1 1500 111
1 56 79
1 56 79
1 100 6
1 100 6
1 44 134
1 44 134
1 1000 488
1 1000 488
1 100 32
1 100 32
1 100 178
1 100 178
1 500 672
1 500 672
1 200 26
1 200 26
1 500 373
1 500 373
1 100 10
1 100 10
1 900 28
1 900 28
2 900 28
2 900 28
2 900 28
etc
It is total nonsense that I can't do much with, it goes on for about 20k lines and goes through all the ID's, eventually.
Any help is more than appreciated :)
Looks like overcomplicated a bit.
Try
WITH cr AS (
SELECT [FK_CartridgeRegistration_Id]
,MAX([CartridgeRefill].[Id]) RecentRefillID
FROM [CartridgeRefill]
GROUP BY [FK_CartridgeRegistration_Id]
)
SELECT cr.[FK_CartridgeRegistration_Id], Refills.Refilled, Dispenses.Dispensed
FROM cr
CROSS APPLY(
SELECT SUM(crm.[Amount]) AS Refilled
FROM [CartridgeRefillMedication] crm
WHERE crm.[FK_CartridgeRefill_Id] = cr.RecentRefillID
) AS Refills
CROSS APPLY(
SELECT SUM(cda.[Amount]) AS Dispensed
FROM [CartridgeDispenseAttempt] cda
WHERE cda.[FK_CartridgeRefill_Id] = cr.RecentRefillID
) AS Dispenses;

Enumerate records in table on multiple columns

ID descr points
----------------------
1000 24 100
1000 24 40
1000 25 100
1000 25 40
2000 24 100
2000 25 100
2000 26 100
Above is my table. I want to add/update column enumerating records on basis of ID and descr. How can I do that?
Below is the result I am looking for.
ID descr points order#
-------------------------------
1000 24 100 1
1000 24 40 2
1000 25 100 1
1000 25 40 2
2000 24 100 1
2000 25 100 2
2000 26 100 3
You can use the ANSI standard function `row_number():
select id, descr, points,
row_number() over (partition by id, descr order by points desc) as ordernum
from t;