How to group set of records in log table based on status changes - sql

I have a large activity table that contains all actions taken on a case. some of these actions change the status of the case. Some actions do not change the status of the case, and essentially should be the status of the previous non-null record.
sample:
caseID | datetime | action | status
1 1/1/2020 a OPEN
1 1/2/2020 B NULL
1 1/3/2020 G CLOSED
1 1/5/2020 T REOPEN
1 1/6/2020 H NULL
1 1/7/2020 H NULL
1 1/9/2020 G CLOSED
1 1/10/2020 J CLOSED
1 1/15/2020 P CLOSED
the output i am trying to achieve is to group and attach a "session" number to the set of dates that contain the date range from open OR reopen TO close. The idea here is that if the dateto is NULL, then that is the current status:
CaseID | status | datefrom | dateto | session
1 OPEN 1/1/2020 1/3/2020 1
1 CLOSED 1/3/2020 1/5/2020 1
1 REOPEN 1/5/2020 1/9/2020 2
1 CLOSED 1/9/2020 NULL 2
i am using SQL 2014 enterprise edition and have been wracking my brain on this for days...any help would be much appreciated. I have found some hints on stackoverflow, but nothing that fully satisfies the needed output.
EDIT: here is a better example of the data:
caseID | datetime | action | status
1 1/1/2020 a OPEN
1 1/2/2020 B REOPEN
1 1/3/2020 G CLOSED
1 1/5/2020 T REOPEN
1 1/6/2020 H NULL
1 1/7/2020 H NULL
1 1/9/2020 G CLOSED
1 1/10/2020 J CLOSED
1 1/15/2020 P CLOSED
1 1/16/2020 P WORKABLE
1 1/17/2020 P NULL
1 1/18/2020 P WORKABLE
1 1/19/2020 P WORKABLE
1 1/20/2020 P CLOSED
1 2/1/2020 o NULL
EXPECTED OUTPUT:
CaseID | status | datefrom | dateto | session
1 OPEN 1/1/2020 1/3/2020 1
1 CLOSED 1/3/2020 1/5/2020 1
1 REOPEN 1/5/2020 1/9/2020 2
1 CLOSED 1/9/2020 1/16/2020 2
1 WORKABLE 1/16/2020 1/20/2020 3
1 CLOSED 1/20/2020 NULL 3

This answers the original version of the question.
I'm not sure if this meets all your requirements, but it produces the results you specify:
Factor out the NULL values.
Assign session based on the number of "open"s or "reopens" up to a row
Aggregate:
So:
select caseid, session, status, min(datetime),
lead(min(datetime)) over (partition by caseid order by min(datetime))
from (select t.*,
sum(case when status in ('OPEN', 'REOPEN') then 1 else 0 end) over (partition by caseid order by datetime) as session
from t
where status is not null
) t
group by caseid, session, status
order by caseid, min(datetime);
Here is a db<>fiddle illustrating that this interpretation works for the data you have provided.

Related

Reverse track forced records relationships based on user-defined tagging

I have this table where the tagging [Tag_To] is updated by an algorithm based on Year and Period of coverage. My current task (in question) is to update the Status given the Year.
ID Year Method Period_From Period_To SeqNo Tag_To Status
-----------------------------------------------------------------------------------
10 2019 A 2019-01-01 2019-12-31 1
11 2019 B 2019-01-01 2019-06-30 2 1
12 2019 B 2019-07-01 2019-12-31 3 1
13 2019 C 2019-01-01 2019-06-30 4 2
14 2020 A 2020-01-01 2020-12-31 1
15 2020 B 2020-01-01 2020-06-30 2 1
16 2020 B 2020-07-01 2020-12-31 3 1
17 2020 C 2020-01-01 2020-12-31 4 2,3
18 2021 A 2021-01-01 2021-12-31 1
19 2021 B 2021-01-01 2021-12-31 2 1
20 2021 C 2021-07-01 2021-12-31 3 2
The SeqNo is applied per Year and the Tag_To is done based on period of coverage.
11 and 12 are tagged to 10 since B follows A and their period falls within 10 period coverage.
13 is tagged to 11 since C follows B and the period...
15 and 16 to 14
Also note that 17 is tagged to 15 and 16 (2,3) because 17's coverage spans across the 2 periods of 15 and 16 combined
and so on...
The objective is to update the Status by Year such that each path is considered Closed if the path already has Methods A, B and C (there are actually more methods, but to simplify). Status should be Open for paths that haven't completed the methods.
From the example above, there are 5 paths:
10(A)-->11(B)-->13(C) = Closed
10(A)-->12(B)-->??? = Open
14(A)-->15(B)-->17(C) = Closed
14(A)-->16(B)-->17(C) = Closed
18(A)-->19(B)-->20(C) = Closed
Therefore the status update should be:
ID Year Method Period_From Period_To SeqNo Tag_To Status
-----------------------------------------------------------------------------------
10 2019 A 2019-01-01 2019-12-31 1 Open
11 2019 B 2019-01-01 2019-06-30 2 1 Closed
12 2019 B 2019-07-01 2019-12-31 3 1 Open
13 2019 C 2019-01-01 2019-06-30 4 2 Closed
14 2020 A 2020-01-01 2020-12-31 1 Closed
15 2020 B 2020-01-01 2020-06-30 2 1 Closed
16 2020 B 2020-07-01 2020-12-31 3 1 Closed
17 2020 C 2020-01-01 2020-12-31 4 2,3 Closed
18 2021 A 2021-01-01 2021-12-31 1 Closed
19 2021 B 2021-01-01 2021-12-31 2 1 Closed
20 2021 C 2021-07-01 2021-12-31 3 2 Closed
I hope I have explained everything clearly. Would really appreciate if anyone could help.
Just to update viewers that I have managed to solve this on my own although the solution is super non-dynamic and quite inefficient, it pretty much did the job for me. Here's what I did.
UPDATE Table SET
Status =
CASE WHEN Method = 'B'
AND NOT EXISTS ( SELECT * FROM Table P INNER JOIN
(
SELECT VALUE AS Tag_To
FROM Table AV
CROSS APPLY STRING_SPLIT(AV.Tag_To, ',')
WHERE AV.Method = 'C'
) C ON P.Sequence_No = C.Tag_To
WHERE P.ID = AValue.ID
)
THEN 'Open'
WHEN Method = 'A'
AND NOT EXISTS ( SELECT * FROM Table P INNER JOIN
(
SELECT VALUE AS Tag_To
FROM Table AV
CROSS APPLY STRING_SPLIT(AV.Tag_To, ',')
WHERE AV.Method = 'B'
) C ON P.Sequence_No = C.Tag_To
WHERE P.ID = AValue.ID
)
THEN 'Open'
ELSE 'Closed'
END
FROM Table AValue
WHERE Year = #Year
;WITH CTE AS
(
SELECT
ROW_NUMBER() OVER(PARTITION BY A.Method ORDER BY A.Sequence_No ASC) SN,
A.ID,
A.Method,
A.Sequence_No,
A.Tag_To,
A.Period_From,
A.Period_To,
A.Status
FROM Table A
LEFT JOIN
(
SELECT VALUE AS Tag_To
FROM Table AV
CROSS APPLY STRING_SPLIT(AV.Tag_To, ',')
WHERE Year = #Year
) B ON A.Sequence_No = B.Tag_To
WHERE Year = #Year
),
CTE2 AS
(
SELECT DISTINCT SN FROM CTE
WHERE Status = 'Open'
)
UPDATE Table SET
Status = 'Open'
FROM Table
INNER JOIN CTE ON Table.ID = CTE.ID
INNER JOIN CTE2 ON CTE.SN = CTE2.SN
Yeah, it's ugly but, hey, it did the job! :)

SQL - How to group/count items by age and status on every date of a year - part 2

This question is similar to a different question I asked and had answered here: SQL - How to group/count items by age and status on every date of a year? but I can't figure out this new problem. I need help with a query to group the data from the example table below into the desired results below. The goal is to total the number of tickets in a given state, by group, at the end of each sequential date starting at a specified date and ending on the current date.
Example Data Table (tickets):
ticket_id | opened | assigned | in_work | closed | assigned_group
---------------------------------------------------------------------------
1 1/1/2020 1/2/2020 1/5/2020 1/5/2020 Network
2 1/2/2020 1/3/2020 1/3/2020 1/5/2020 Software
3 1/2/2020 1/5/2020 Hardware
4 1/2/2020 Network
5 1/3/2020 1/4/2020 1/5/2020 Software
6 1/3/2020 Network
... and more continuing in similar pattern
Desired Result:
Date | assigned_group | num_open | num_assigned | num_in_work | num_closed |
---------------------------------------------------------------------------
1/1/2020 Network 1 0 0 0
1/1/2020 Software 0 0 0 0
1/1/2020 Hardware 0 0 0 0
1/2/2020 Network 1 1 0 0
1/2/2020 Software 1 0 0 0
1/2/2020 Hardware 1 0 0 0
1/3/2020 Network 2 1 0 0
1/3/2020 Software 1 0 1 0
1/3/2020 Hardware 1 0 0 0
1/4/2020 Network 2 1 0 0
1/4/2020 Software 0 1 1 0
1/4/2020 Hardware 1 0 0 0
1/5/2020 Network 2 0 0 1
1/5/2020 Software 0 0 1 1
1/5/2020 Hardware 0 1 0 0
... continuing to present date
Thank you!
You can unpivot and use a cumulative sum:
with da as (
select opened as date, assigned_group, 1 as open_inc, 0 as assigned_inc, 0 as in_work_inc, 0 as closed_inc
from t
union all
select assigned, assigned_group, -1, 1, 0, 0
from t
union all
select in_work, assigned_group, 0 -1, 1, 0
from t
union all
select closed, assigned_group, 0, 0, -1, 1
from t
)
select date, assigned_group,
sum(sum(open_inc)) over (partition by assigned_group order by date) as num_opens,
sum(sum(assigned_inc)) over (partition by assigned_group order by date) as num_assigned,
sum(sum(in_work_inc)) over (partition by assigned_group order by date) as num_in_work,
sum(sum(closed_inc)) over (partition by assigned_group order by date) as num_closed
from da
group by date, assigned_group
order by date, assigned_group;

Return product if there is no match in other table [duplicate]

This question already has answers here:
Select rows which are not present in other table
(4 answers)
Closed 2 years ago.
I have two tables:
Product_Table
ProductID Name Date
1 ABC 2020-02-14
2 XYZ 2020-03-05
Productbreak_Table
BreakID Product_id Begin End
34 1 2020-01-01 2020-01-30
35 1 2020-02-01 2020-02-20
36 2 2020-01-15 2020-01-31
37 2 2020-02-15 2020-03-01
My goal is to get just the products whose Date are not between the Begin and End dates of the productbreak_table
Result should be:
ProductID Name
2 XYZ
You would use not exists:
select p.*
from products p
where not exists (select 1
from productbreak pb
where pb.productid = p.productid and
p.date between pb.begin and pb.end
);

How can we join three tables?

I have three tables :
TABLE1
D_ID O_COUNT STATUS P_ID Q_ID
1 1 close 111 L_1
2 1 Resolved 111 L_1
3 1 close 111 L_1
4 0 open 111 L_1
5 0 new 111 L_1
6 1 close 111 L_1
TABLE2_DL
D_ID D_LID C_ID E_ID Q_ID P_ID
1 11 3 23 L_1 111
2 12 3 24 L_1 111
3 13 3 25 L_1 111
4 14 3 26 L_1 111
5 15 3 21 L_1 111
TABLE3_TED
EXEC_ID EXEC_STAT TEAM C_ID
24 FAILED Pharma 3
28 FAILED Pharma1 3
29 FAILED Pharma 3
25 FAILED Pharma 3
26 FAILED Pharma1 3
27 FAILED Pharma2 3
This is the code i've written.
select D_ID, D_LID, E_ID,ted.EXEC_STAT,ted.TEAM,
DL.P_ID,DL.Q_ID,ted.C_ID,
(case
when upper(ted.EXEC_STAT) like 'FAIL%' then 1
else 0
end) as FAILED,
(select O_COUNT from TABLE1 dd
where dd.D_ID = dl.D_ID
and dd.P_ID=dl.P_ID
and dd.Q_ID=DL.Q_ID) OPENCOUNT,
(select Status from TABLE1 dd
where dd.d_id = dl.d_id
and dd.p_id=dl.p_id
and dd.q_id=DL.Q_ID) Status
from TABLE2_DL DL,TABLE3_TED ted
where DL.D_ID in (select d_id from TABLE1 dd
where dd.d_id = dl.d_id
and dd.p_id=dl.p_id
and dd.q_id=DL.Q_ID
)
and dl.E_ID = ted.EXEC_ID;
Result:
2 12 24 FAILED Pharma 111 L_1 003 1 1 Resolved
3 13 25 FAILED Pharma 111 L_1 003 1 1 close
4 14 26 FAILED Pharma1 111 L_1 003 1 0 open
this query fetches the records for me. But what i really want is all the records to be fetched from TABLE3_TED and selected records from the other two tables. I tried joining table1 and TABLE2_DL first and aliased that query and then joining that query with TABLE3_TED but i still don't get all the records from TABLE3_TED. Please help.
P.S. I'm new to this portal and i didn't know how to create tables. Can someone please help me with that as well? Thanks in advance. :)
I might be reading this wrong, but it sounds like you're looking for an OUTER JOIN. Try doing the following:
Select DD.D_Id,
DD.D_LId,
DL.E_Id,
TED.EXEC_STAT,
TED.TEAM,
DL.P_Id,
DL.Q_Id,
TED.C_Id,
Case When Upper(TED.EXEC_STAT) Like 'FAIL%'
Then 1
Else 0
End As FAILED,
T1.O_COUNT As OPENCOUNT,
T1.Status
From Table3_TED TED
Left Join Table2_DL DL On DL.E_Id = TED.EXEC_Id
Left Join Table1 DD On DD.D_Id = DL.D_Id
And DD.P_Id = DL.P_Id
And DD.Q_Id = DL.Q_Id

sql Query on effective date

I would like to get report for drink purchased in whole month but price of the drink can change any time in month and I would like to get report for a month with price change
I have two tables
SELECT [ID]
,[DrinkID]
,[UserID]
,[qty]
,[DateTaken]
FROM [Snacks].[dbo].[DrinkHistory]
SELECT [ID]
,[DrinkID]
,[UserID]
,[qty]
,[DateTaken]
FROM [Snacks].[dbo].[DrinkHistory]
[DrinkHistory]:
ID DrinkID UserID qty DateTaken
----------------------------------------------------------------------
1 1 1 1 2014-05-10
2 1 1 2 2014-05-15
3 2 1 1 2014-06-01
4 2 1 4 2014-06-01
5 1 1 3 2014-05-20
6 1 1 4 2014-05-30
[DrinkPricesEffect]:
PriceID DrinkID DrinkPrice PriceEffectiveDate IsCurrent
-----------------------------------------------------------------------------------
1 1 10.00 2014-05-01 1
2 1 20.00 2014-05-20 1
3 2 9.00 2014-06-01 1
4 2 8.00 2014-01-01 1
5 1 30.00 2014-05-25 1
6 1 40.00 2014-05-28 1
I would like to have result as under date taken between 2014-05-1 to 2014-05-31
DrinkId Qty Price DateTaken PriceEffectiveDate
-----------------------------------------------------------------------
1 1 10 2014-05-10 2014-05-01
1 2 10 2014-05-15 2014-05-01
1 3 20 2014-05-20 2014-05-20
1 4 40 2014-05-30 2014-05-28
Is there any who can give me some idea or write query for me?
If your drink price can change any time in a month you could additionaly save the price for each purchase. I would add a column [PricePaid] to the table [DrinkHistory].
When adding a record to [DrinkHistory], the price for the drink at the moment is known, but later it might change so you save the current price to the history...
Then for your result you could just display the Whole [DrinkHistory]
SELECT * FROM DrinkHistory;
This should work:
Select
DH.DrinkId,
DH.Qty,
DPE.DrinkPrice AS Price,
DH.DateTaken,
DPE.PriceEffectiveDate
FROM DrinkHistory DH
JOIN DrinkPricesEffect DPE ON DPE.PriceID =
(
Select Top 1 PriceID FROM
(
Select PriceID,RANK() OVER(ORDER BY PriceEffectiveDate DESC ) AS rnk
FROM DrinkPricesEffect
WHERE DH.DrinkId = DrinkId AND
DH.DateTaken >= PriceEffectiveDate
)SubQ WHERE rnk = 1
)
WHERE DH.DateTaken Between '2014-05-01' AND '2014-05-30'
Here you can find the SQL Fiddle link: http://sqlfiddle.com/#!6/5f8fb/26/0