SQL Server - MyCTE query based on 24 hour period (next day) - sql

I have this bit of code:
;WITH MyCTE AS
(
SELECT *,
ROW_NUMBER() OVER(PARTITION BY CardUser ORDER BY CardTableID) AS NewVariation
FROM CardChecker
)
UPDATE MyCTE
SET Status = NewVariation
which currently updates the status column, however what I want to happen is over a 24 hour period, the status starts again the next day at 1, and counts again based on the CardUser like specified above:
Current data and what happens:
2 aaa 1 2015-06-25 08:00:00.000 123 1 NULL
3 ccc 1 2015-06-25 00:00:00.000 124 1 NULL
4 aaa 1 2015-06-25 17:30:00.000 125 2 NULL
5 aaa 1 2015-06-26 17:30:00.000 125 *3* NULL
what I want to happen:
2 aaa 1 2015-06-25 08:00:00.000 123 1 NULL
3 ccc 1 2015-06-25 00:00:00.000 124 1 NULL
4 aaa 1 2015-06-25 17:30:00.000 125 2 NULL
5 aaa 1 2015-06-26 17:30:00.000 125 *1* NULL
im not quite sure how I could add this to the above query so would it be possible for someone to point me in the right direction?
the main problem is the EventTime field contains both the date and the time, so adding it is as a PARTITION means the status would always be 1 based on the time parameter of the field
thanks for the help
Current CardTable structure:
CREATE TABLE CardTable (CardTableID INT IDENTITY (1,1) NOT NULL,
CardUser VARCHAR(50),
CardNumber VARCHAR(50),
EventTime DATETIME,
Status INT)

You can CONVERT() the EventTime to DATE type and then PARTITION:
;WITH MyCTE AS
(
SELECT Status,
ROW_NUMBER() OVER(PARTITION BY CardUser, CONVERT(DATE, EventTime)
ORDER BY CardTableID) AS NewVariation
FROM CardChecker
)
UPDATE MyCTE
SET Status = NewVariation
Your query basically unnecessarily updating entire table everytime. If EventTime is current date time of the system, having a flag to mark already updated status would improve the performance.
;WITH MyCTE AS
(
SELECT Status,
ROW_NUMBER() OVER(PARTITION BY CardUser, CONVERT(DATE, EventTime)
ORDER BY CardTableID) AS NewVariation
FROM CardChecker
WHERE Status IS NULL OR
CONVERT(DATE, EventTime) = CONVERT(DATE, GETDATE())
)
UPDATE MyCTE
SET Status = NewVariation

Related

How to get the row for the current date?

Pretend today 2022-10-24
case 1
id
productCode
version
startDate
endDate
1
AAA
1
2022-10-01
2022-10-28
2
AAA
2
2022-10-29
NULL
case 1 depend on table above, I want to return only 1 row at id 1, why cause today 2022-10-24 still between startDate and endDate
case 2
id
productCode
version
startDate
endDate
1
AAA
1
2022-10-01
2022-10-28
2
AAA
2
2022-10-01
NULL
case 2 depends on table above. I want to return only 1 row at id 2. Why cause when startDate has the same value between id 1 & 2, so choose endDate with NULL value.
I am still confused about how to implement this with query.
I want to make for one query logic. When running query so when use case 1 return id 1 and when I use for case 2 return id 2.
As I mention in the comments, seems you just need some simple >= and <(=) logic (while handling NULLs) and a "Top 1 per group":
WITH CTE AS(
SELECT id,
productCode,
version,
startDate,
endDate,
ROW_NUMBER() OVER (PARTITION BY productCode ORDER BY Version DESC) AS RN --Guessed the required partition and order clauses
FROM dbo.YourTable
WHERE startDate <= CONVERT(date,GETDATE())
AND (endDate >= CONVERT(date,GETDATE()) OR endDate IS NULL))
SELECT id,
productCode,
version,
startDate,
endDate
FROM CTE
WHERE RN = 1;

sql date difference with multiple variables

I'm trying to get the number of days difference in dates between the effdate status 0 that follows the most recent status 1
the code below yields the following results
SELECT * FROM
(SELECT FILEKEY, STATUS, EFFDATE FROM ASTATUSHIST
UNION
SELECT FILEKEY, ASTATUS, ASTATUSEFFDATE FROM USERS ) A
ORDER BY 1, 3 DESC
130 0 2019-10-25 00:00:00.000
130 0 2017-03-01 00:00:00.000
130 0 2017-01-01 00:00:00.000
130 1 2005-02-01 00:00:00.000
130 0 2001-03-03 00:00:00.000
130 0 2000-01-30 00:00:00.000
130 0 2000-01-01 00:00:00.000
this code combines 2 tables to get the complete history for a given user.
Ideally I could produce something that looks like this:
130 4352
or
125 null
where the null is filekey without a status 1 or a filekey with a status 1 but without a following status 0
Thanks
In all supported versions of SQL Server, you can use window functions:
with t as (
<your query here>
)
select t.*,
datediff(day, date, next_date) as days_diff
from (select t.*,
row_number() over (partition by filekey, status order by date desc) as seqnum,
lead(date) over (partition by filekey order by date) as next_date
from t
) t
where seqnum = 1;

For multiple rows with some identical fields, keep the one with updated values, and mark the others

For multiple rows with identical features, I hope two add few marks/new columns in the original table.
The original table is as below:
ID Start_date End_Date Amount
1 2005-01-01 2010-01-01 5
1 2000-07-01 2009-06-01 10
1 2017-08-01 2018-03-01 30
I wish to keep one record with the earliest start date, latest end date, added amount and an indicator to tell me to use this record. For the others, just use the indicator to tell me not to use.
The updated table should be as below:
ID Start_date End_Date Amount Amount_new Usable Start End
1 2005-01-01 2010-01-01 5 45 0 2000-07-01 2018-03-01
1 2000-07-01 2009-06-01 10 1
1 2017-08-01 2018-03-01 30 1
It does not matter which row to keep, as long as there is one row with Usable=0, and Amount_new, Start and End are updated.
If not considering the end date, I was thinking of grouping by ID and Start_date, then update the column Usable and Amount_new of the first row. However I still have the problem of how to select the first row from the group by group. Considering the End_Date makes my mind even more messy!
Could anyone help to shed some light upon this issue?
You seem to want something like this:
alter table original
add amount_new int,
add usable bit,
add new_start,
add new_end;
Then, you can update it using window functions:
with toupdate as (
select o.*,
sum(amount) over (partition by id) as x_amount,
(case when row_number() over (partition by id order by start_date) as x_usable,
min(start_date) as x_start_date,
max(end_date) as x_end_date
from original o
)
update toupdate
set new_amount = x_amount,
usable = x_usable,
new_start = x_start_date,
new_end = x_end_date;
The following query should do what you want:
CREATE TABLE #temp (ID INT, [Start_date] DATE, End_Date DATE, Amount NUMERIC(28,0), Amount_new NUMERIC(28,0), Usable BIT, Start [Date], [End] [Date])
INSERT INTO #temp (ID, [Start_date], End_Date, Amount) VALUES
(1,'2005-01-01','2010-01-01',5),
(1,'2000-07-01','2009-06-01',10),
(1,'2017-08-01','2018-03-01',30),
(2,'2001-07-01','2009-06-01',5),
(2,'2017-08-01','2019-03-01',35)
UPDATE t1
SET Amount_new = t2.[Amount_new],
Usable = 1,
Start = t2.[Start],
[End] = t2.[End]
FROM (SELECT *,ROW_NUMBER() OVER (PARTITION BY ID ORDER BY (SELECT 1)) AS RNO FROM #temp) t1
INNER JOIN
(
SELECT ID,[Start_date],[End_Date],[Amount]
,SUM(Amount) OVER(PARTITION BY ID) AS [Amount_new]
,MIN([Start_date]) OVER(PARTITION BY ID) AS [Start]
,MAX(End_Date) OVER(PARTITION BY ID) AS [End]
,ROW_NUMBER() OVER (PARTITION BY ID ORDER BY (SELECT 1)) AS RNO
FROM #temp ) t2 ON t1.id = t2.id AND t2.rno = t1.RNO AND t2.RNO = 1
SELECT * FROM #temp
The result is as below,
ID Start_date End_Date Amount Amount_new Usable Start End
1 2005-01-01 2010-01-01 5 45 1 2000-07-01 2018-03-01
1 2000-07-01 2009-06-01 10 NULL NULL NULL NULL
1 2017-08-01 2018-03-01 30 NULL NULL NULL NULL
2 2001-07-01 2009-06-01 5 40 1 2001-07-01 2019-03-01
2 2017-08-01 2019-03-01 35 NULL NULL NULL NULL

How to get Cumulative data from the same table using SQL?

I have this table
table1
eventid entityid eventdate
----------------------------------------
123 xyz Jan-02-2019
541 xyz Jan-02-2019
234 xyz Jan-03-2019
432 xyz Jan-04-2019
111 xyz Jan-05-2019
124 xyz Jan-06-2019
123 xyz Jan-07-2019
234 xyz Jan-08-2019
432 xyz Jan-09-2019
111 xyz Jan-12-2019
I want to show final result as
entityid interval1 interval2
------------------------------
xyz 2 4
here intervals are in days.
Logic to calculate intervals are :
Ex - event 123 and 234 happens multiple time so date difference between each occurrence as shown below would be added finally into interval1.
Please note - its not necessary 234 would be always in a next row of 123. there could be other events in between.
Formula is
interval1 = datediff(day,eventdate of 123,eventdate of 234) + datediff(day,eventdate of 123,eventdate of 234) + and so on
Same for interval2 but for event 432 & 111.
entityid eventid1 eventid2 event_date_diff
--------------------------------------------
xyz 123 234 1
xyz 123 234 1
xyz 432 111 1
xyz 432 111 3
The challenge here is to find out if event 123 has 234 event or not in upcoming rows (not necessarily in immediate next row) and if its there then find the date difference. If there are any other events between 123-234 then we need to ignore those in between events. Also if 123 appears twice then need latest eventdate for 123.
Let's go over this in terms of your requirements, and build up the necessary pieces. This won't be approached in the order you stated them in, but in an order that makes them easier to understand.
Also if 123 appears twice then need latest eventdate for 123.
This means we need to create a range bounds. This is pretty easy:
NextOccurence AS (SELECT eventId, entityId, eventDate,
LEAD(eventDate) OVER(PARTITION BY eventId, entityId ORDER BY eventDate) AS nextOccurenceDate
FROM Table1)
... this will give us every occurrence of an event, with the next one, if present (these can be limited to just your "source" events, but I'm not bothering with that here).
The challenge here is to find out if event 123 has 234 event or not in upcoming rows (not necessarily in immediate next row) and if its there then find the date difference. If there are any other events between 123-234 then we need to ignore those in between events.
(and you previously mentioned it should be the minimum following date, if there were multiple following events).
For this we need to first map events:
EventMap AS (SELECT 123 AS original, 234 AS follow
UNION ALL
SELECT 432, 111)
... and use this to get the "next" following event in range, in what is partially a greatest-n-per-group query:
SELECT NextOccurence.entityId, NextOccurence.eventId, DATEDIFF(day, NextOccurence.eventDate, Table1.eventDate) AS diff
FROM NextOccurence
JOIN EventMap
ON EventMap.original = NextOccurence.eventId
CROSS APPLY (SELECT TOP 1 Table1.eventDate
FROM Table1
WHERE Table1.entityId = NextOccurence.entityId
AND Table1.eventId = EventMap.follow
AND Table1.eventDate >= NextOccurence.eventDate
AND (Table1.eventDate < NextOccurence.nextOccurenceDate OR NextOccurence.nextOccurenceDate IS NULL)
ORDER BY Table1.eventDate) AS Table1
... at this point, we have something close to your intermediate results table:
| entityId | eventId | diff |
|----------|---------|------|
| xyz | 123 | 1 |
| xyz | 123 | 1 |
| xyz | 432 | 1 |
| xyz | 432 | 3 |
... and what follows afterwards would be a standard PIVOT query to aggregate the results.
The final query ends up looking like this:
WITH NextOccurence AS (SELECT eventId, entityId, eventDate,
LEAD(eventDate) OVER(PARTITION BY eventId, entityId ORDER BY eventDate) AS nextOccurenceDate
FROM Table1),
EventMap AS (SELECT 123 AS original, 234 AS follow
UNION ALL
SELECT 432, 111)
SELECT entityId, [123] AS '123-234', [432] AS '432-111'
FROM (SELECT NextOccurence.entityId, NextOccurence.eventId, DATEDIFF(day, NextOccurence.eventDate, Table1.eventDate) AS diff
FROM NextOccurence
JOIN EventMap
ON EventMap.original = NextOccurence.eventId
CROSS APPLY (SELECT TOP 1 Table1.eventDate
FROM Table1
WHERE Table1.entityId = NextOccurence.entityId
AND Table1.eventId = EventMap.follow
AND Table1.eventDate >= NextOccurence.eventDate
AND (Table1.eventDate < NextOccurence.nextOccurenceDate OR NextOccurence.nextOccurenceDate IS NULL)
ORDER BY Table1.eventDate) AS Table1) AS d
PIVOT (SUM(diff)
FOR eventId IN ([123], [432])
) AS pvt
Fiddle example
...which generates the expected results:
| entityId | 123-234 | 432-111 |
|----------|---------|---------|
| xyz | 2 | 4 |
From what I understood of the question, we are asked to provide occurrences of each eventid per date. However these are to be represented in columns rather than rows.
My approach to this problem is firstly to pivot the data within a cte and then to select the unique value from each column in to the cross apply operator of a query. There may be better ways of doing it but this made the most sense to me.
DECLARE #T TABLE
(
EventId INT,
EntityId NVARCHAR(3),
EventDate DATETIME
);
INSERT INTO #T (EventId, EntityId, EventDate)
SELECT * FROM (VALUES
(123, 'xyz', '2019-01-02'),
(234, 'xyz', '2019-01-03'),
(432, 'xyz', '2019-01-04'),
(111, 'xyz', '2019-01-05'),
(124, 'xyz', '2019-01-06'),
(123, 'xyz', '2019-01-07'),
(234, 'xyz', '2019-01-08'),
(432, 'xyz', '2019-01-09'),
(111, 'xyz', '2019-01-12')
) X (EVENTID, ENTITYID, EVENTDATE);
with cte as (
select EntityId, [123] as Interval1, [234] as Interval2, [432] as Interval3, [111] as
Interval4, [124] as Interval5
from
(
select top 5 EntityId, EventId, min(eventdate) as ordering, count(distinct EventDate)
as
vol from #T
group by EntityId, EventId
order by ordering
) src
PIVOT
(
max(vol)
for EventId in ([123], [234], [432], [111], [124])
) as pvt)
select distinct EntityId, Interval1, Interval2, Interval3, Interval4, Interval5
from (select EntityId from cte) a
cross apply
(select Interval1 from cte where Interval1 is not null) b
cross apply
(select Interval2 from cte where Interval2 is not null) c
cross apply
(select Interval3 from cte where Interval3 is not null) d
cross apply
(select Interval4 from cte where Interval4 is not null) e
cross apply
(select Interval5 from cte where Interval5 is not null) f;
You can use lead() and conditional aggregation for this:
select sum(case when eventid = 123 and next_eventid = 234
then datediff(day, eventdate, next_eventdate)
end) as interval1,
sum(case when eventid = 432 and next_eventid = 111
then datediff(day, eventdate, next_eventdate)
end) as interval2
from (select t.*,
lead(eventid) over (partition by entityid order by eventdate) as next_eventid,
lead(eventdate) over (partition by entityid order by eventdate) as next_eventdate
from t
) t;
Probably the simplest way to handle intervening events is conditional cumulative arithemtic:
select sum(case when eventid = 123 and
then datediff(day, eventdate, next_eventdate_234)
end) as interval1,
sum(case when eventid = 432 and
then datediff(day, eventdate, next_eventdate_111)
end) as interval2
from (select t.*,
min(case when eventid = 234 then eventdate end) over (order by eventdate desc) as next_eventdate_234,
min(case when eventid = 111 then eventdate end) over (order by eventdate desc) as next_eventdate_111
from t
where eventid in (123, 234)
) t
where eventid in (123, 432);

Need help finding the correct T-SQL Query

I am not quite sure how to go about doing this. Basically I have have a table like this
UserId DateRequested Approved ApprovedBy Notes
------------ ----------------------- -------- ----------- -----
1 2011-05-26 0 NULL NULL
1 2011-05-27 0 NULL NULL
1 2011-05-28 0 NULL NULL
1 2011-06-05 0 NULL NULL
1 2011-06-06 0 NULL NULL
1 2011-06-25 0 NULL NULL
Which basically contains the days an employee requests a holiday. Now, when a day or days is granted, this data needs to be copied over to a table of the form
UserId DateFrom DateTo
So basically for the above data i want:
UserId DateFrom DateTo
-------------------------------
1 2011-05-26 2011-05-28
1 2011-06-05 2011-06-06
1 2011-06-25 2011-06-25
I.e I want consecutive days in the DateFrom and DateTo. Now I am not sure how to do this without using a while loop. This is SQL, So i would prefer a non-iterative solution.
Please advise!!!
;WITH cte AS
(
SELECT *,
DATEDIFF(DAY,0,DateRequested)-
ROW_NUMBER() OVER (PARTITION BY UserId ORDER BY DateRequested) AS Grp
FROM YourTable
WHERE Approved = 1 /*Presumably - but your example data doesn't show this.*/
)
SELECT UserId,
MIN(DateRequested) AS DateFrom,
MAX(DateRequested) AS DateTo
FROM cte
GROUP BY UserId,Grp
In Oracle PL/SQL it would be written as follows:
WITH cte
AS (SELECT a.*,
daterequested - TRUNC (SYSDATE)
- ROW_NUMBER ()
OVER (PARTITION BY UserId ORDER BY DateRequested)
AS Grp
FROM yourtable a
WHERE Approved = 0)
SELECT UserId, MIN (DateRequested) AS DateFrom, MAX (DateRequested) AS DateTo
FROM cte
GROUP BY UserId, Grp;