Date difference for same ID - sql-server

I ve got a data set similar to
+----+------------+------------+------------+
| ID | Udate | last_code | Ddate |
+----+------------+------------+------------+
| 1 | 05/11/2018 | ACCEPTED | 13/10/2018 |
| 1 | 03/11/2018 | ATTEMPT | 13/10/2018 |
| 1 | 01/11/2018 | INFO | 13/10/2018 |
| 1 | 22/10/2018 | ARRIVED | 13/10/2018 |
| 1 | 15/10/2018 | SENT | 13/10/2018 |
+----+------------+------------+------------+
I m trying to get the date difference for each code on Udate, but for the first date I want to make datedifference between Udate and Ddate.
So I ve been trying:
DATEDIFF(DAY,LAG(Udate) OVER (PARTITION BY Shipment_Number ORDER BY Udate), Udate)
to get the difference between dates and it works so far, but I also need the first date difference between Udate and Ddate.
I was thinking about ISNULL()
Also, at the end I need an average of days between codes as well, usually they keep the same pattern. Sample output data:
+----+------------+------------+------------+------------+
| ID | Udate | last_code | Ddate | Difference |
+----+------------+------------+------------+------------+
| 1 | 05/11/2018 | ACCEPTED | 13/10/2018 | 2 |
| 1 | 03/11/2018 | ATTEMPT | 13/10/2018 | 2 |
| 1 | 01/11/2018 | INFO | 13/10/2018 | 10 |
| 1 | 22/10/2018 | ARRIVED | 13/10/2018 | 7 |
| 1 | 15/10/2018 | SENT | 13/10/2018 | 2 |
+----+------------+------------+------------+------------+
Notice that when there is no previous code, the date diff is between Udate and Ddate.
Would appreciate any idea.
Thank you.

Well, ISNULL is the way to go here.
Since you also want the average difference, you can use a common table expression to get the difference, and query it to get the average:
First, Create and populate sample data (Please save us this step in your future questions)
-- This would not be needed if you've used ISO8601 for date strings (yyyy-mm-dd | yyyymmdd)
SET DATEFORMAT DMY;
DECLARE #T AS TABLE
(
ID int,
UDate date,
last_code varchar(10),
Ddate date
) ;
INSERT INTO #T (ID, Udate, last_code, Ddate) VALUES
(1, '05/11/2018', 'ACCEPTED', '13/10/2018'),
(1, '03/11/2018', 'ATTEMPT' , '13/10/2018'),
(1, '01/11/2018', 'INFO' , '13/10/2018'),
(1, '22/10/2018', 'ARRIVED' , '13/10/2018'),
(1, '15/10/2018', 'SENT' , '13/10/2018');
The cte:
WITH CTE AS
(
SELECT ID,
Udate,
last_code,
Ddate,
DATEDIFF(
DAY,
ISNULL(
LAG(Udate) OVER(PARTITION BY ID ORDER BY Udate),
Ddate
),
UDate
) As Difference
FROM #T
)
The query:
SELECT *, AVG(Difference) OVER(PARTITION BY ID) As AverageDifference
FROM CTE;
Results:
ID Udate last_code Ddate Difference AverageDifference
1 15.10.2018 SENT 13.10.2018 2 4
1 22.10.2018 ARRIVED 13.10.2018 7 4
1 01.11.2018 INFO 13.10.2018 10 4
1 03.11.2018 ATTEMPT 13.10.2018 2 4
1 05.11.2018 ACCEPTED 13.10.2018 2 4

Related

SQL Server - Cumulate awake time for devices from event summary

I'm working on a SQL server (used by BMC) to grab the uptime of some devices.
I've got a query that display me results like this:
| DeviceName | EventDate | EventType |
| ---------- | ----------------------- | ---------- |
| 1 | 2021-02-15 08:06:12.000 | getting up |
| 1 | 2021-02-12 15:07:26.000 | going down |
| 2 | 2021-02-16 08:12:54.000 | getting up |
| 2 | 2021-02-12 15:43:00.000 | going down |
| 3 | 2021-02-15 07:47:42.000 | getting up |
| 3 | 2021-02-12 15:38:41.000 | going down |
| 4 | 2021-02-15 08:10:07.000 | getting up |
| 5 | 2021-02-18 06:41:40.000 | getting up |
| ... | ... | ... |
I would like to get a result that looks like that:
| DeviceName | TotalUpTime (min) |
| ---------- | ----------------- |
| 1 | 16543 |
| 2 | 13639 |
| 3 | 13524 |
| 4 | 19235 |
| 5 | 12347 |
Here is my current query:
SELECT
DeviceName,
EventDate,
EventType
FROM **irrelevant complexe SELECT query**
ORDER BY DeviceName, EventDate DESC
Any help would be great!!
Many thx in advance!
SOLUTION:
Ok, here's what worked for me:
SELECT
DeviceName,
DATEDIFF(s, EventDate, EndTime)/60 AS [TotalUpTime (min)]
FROM (
SELECT *,
LEAD(CASE WHEN EventType = 32 THEN EventDate END, 1, GETDATE())
OVER (PARTITION BY DeviceName ORDER BY EventDate) AS EndTime
FROM (
**Irrelevant SELECT query**
) r
) s
WHERE EventType = 16 AND EndTime IS NOT NULL
Many thanks to #Charlieface whos response helped me a lot.
Hope this help someone someday, even if it's very specific.
SELECT
DeviceName,
SUM(DATEDIFF(ms, EventDate, EndTime) / 60000.0) AS [TotalUpTime (min)]
FROM (
SELECT *,
LEAD(CASE WHEN EventType = 'going down' THEN EventDate END, 1, GETDATE()) OVER
OVER (PARTITION BY DeviceName ORDER BY EventDate) AS EndTime
FROM table
)
WHERE EventType = 'getting up' AND EndTime IS NOT NULL
GROUP BY DeviceName

Select results based on nearest date window

I have a SQL Server table as follows. I would like to group by name and place of test taken, order by date ascending as partition based on above mentioned grouping.
now a configurable window of eg:4 days is provided. In below table if first test taken date is
02/01/2019 (1st Feb) - its score is taken, and any other test score which has been retaken within the next 4 day window shall not be considered. If record also falls within 4 day window of already excluded item example row id - 4 , that also shall be excluded.
Any SQL statements for this logic is much appreciated.
CREATE TABLE test(
[recordid] int IDENTITY(1,1) PRIMARY KEY,
[name] [nvarchar](25) NULL,
[testcentre] [nvarchar](25) NULL,
[testdate] [smalldatetime] NOT NULL,
[testscore] [int],
[Preferred_Output] [int],
[Result] [nvarchar](75) NULL
)
GO
INSERT INTO test
(
[name],
[testcentre],
[testdate],
[testscore],
[Preferred_Output],
[Result] )
VALUES
('George','bangalore',' 02/01/2019',1,1,'Selected as first item -grouped by name and location'),
('George','bangalore',' 02/02/2019',0,0,'ignore as within 4 days'),
('George','bangalore',' 02/04/2019',1,0,'ignore as within 4 days'),
('George','bangalore',' 02/06/2019',3,0,'ignore as within 4 days from already ignored item -04-02-2019'),
('George','bangalore',' 02/15/2019',2,2,'Selected as second item -grouped by name and location'),
('George','bangalore',' 02/18/2019',5,0,'ignore as within 4 days of previous'),
('George','Pune',' 02/15/2019',4,3,'Selected as third item'),
('George','Pune',' 02/18/2019',6,0,'ignore as within 4 days of previous'),
('George','Pune',' 02/19/2019',7,0,'ignore as within 4 days of previous'),
('George','Pune',' 02/20/2019',8,0,'ignore as within 4 days of previous')
GO
select * from test
GO
+----------+--------+------------+------------+-----------+------------------+
| recordid | name | testcentre | testdate | testscore | Preferred_Output |
+----------+--------+------------+------------+-----------+------------------+
| 1 | George | bangalore | 02/01/2019 | 1 | 1 |
| 2 | George | bangalore | 02/02/2019 | 0 | 0 |
| 3 | George | bangalore | 02/04/2019 | 1 | 0 |
| 4 | George | bangalore | 02/06/2019 | 3 | 0 |
| 5 | George | bangalore | 02/15/2019 | 2 | 2 |
| 6 | George | bangalore | 02/18/2019 | 5 | 0 |
| 7 | George | Pune | 02/15/2019 | 4 | 3 |
| 8 | George | Pune | 02/18/2019 | 6 | 0 |
| 9 | George | Pune | 02/19/2019 | 7 | 0 |
| 10 | George | Pune | 02/20/2019 | 8 | 0 |
+----------+--------+------------+------------+-----------+------------------+
I don't think that a recursive query is required for this. You want to compare the dates across consecutive records, so this is a kind of gaps-and-island problem, where want to identify the start of each island.
Window functions can do that:
select t.*,
case when lag_testdate is null or testdate > dateadd(day, 4, lag_testdate)
then testscore
else 0
end new_core
from (
select t.*, lag(testdate) over(partition by name, testcentre order by testdate) lag_testdate
from test t
) t
Demo on DB Fiddle

Lead/Lag syntax help - how do you order the columns?

Thanks for your help/advice. I'm unclear about the 1,0 within the LAG expression - what is that and why isn't mine working?
Do I have to do two Order by for both lead and lag?
Select
* Customer,
Prod,
day,
current sold,
date,
lag[current sold,1,0] OVER(PARTITION BY customer ORDER BY date DESC) as Previous Day,
lead[current sold,1,0] OVER(PARTITION BY customer ORDER BY date DESC) as Next Day
From
table1
Result:
| PROD | DAY | CURRENT SOLD | date customer |
+-------+-----+--------------+-----------------------
| SHIRT | M | 2 | 1-2018 A
| SHIRT | T | 9 | 2-2018 B
| SHIRT | W | 0 | 12-2018 C
| SHIRT | TH | 6 | 11-2018 D
| SHIRT | F | 7 | 3-2018 E
+-------+-----+--------------+--+----------------
+-------+-----+--------------+---------------+-----------+--+--------------
| PROD | DAY | CURRENT SOLD | PREVIOUS SOLD | NEXT SOLD | date |customer
+-------+-----+--------------+---------------+-----------+--+---------------
| SHIRT | M | 2 | | 9 | 1-2018 |A
| SHIRT | T | 9 | 2 | 0 | 2-2018 |B
| SHIRT | W | 0 | 9 | 6 | 12-2018|C
| SHIRT | TH | 6 | 0 | 7 | 11-2018|D
| SHIRT | F | 7 | 6 | | 3-2018 |E
+-------+-----+--------------+---------------+-----------+--+---------------
you can use LAG for previous sales and LEAD for next sales. I prepared sample with your example :
--DROP TABLE #Tbl;
--DROP TABLE #Days;
CREATE TABLE #Tbl
(
Prod VARCHAR(10)
,[DayName] VARCHAR(2)
,CurrentSold INT
);
CREATE TABLE #Days
(
DayNumber INT
,[DayName] VARCHAR(2)
);
INSERT INTO #Days
VALUES (1,'M'),(2,'T'),(3,'W'),(4,'TH'),(5,'F');
INSERT INTO #Tbl
VALUES ('SHIRT','M',2)
,('SHIRT','T',9)
,('SHIRT','W',0)
,('SHIRT','TH',6)
,('SHIRT','F',7);
SELECT T.Prod
,T.DayName
,T.CurrentSold
,LAG(CurrentSold, 1,0) OVER (ORDER BY DayNumber) AS PreviousSold
,LEAD(CurrentSold, 1,0) OVER (ORDER BY DayNumber) AS PreviousSold
FROM #Tbl T
INNER JOIN #Days D ON T.DayName = D.DayName;

Sql query to check if a certain value appears more than once in rows

I have table with 5 columns like this
+----+-------------------------+-----------+--------+-----------+
| Id | CreateDate | CompanyId | UserId | IsEnabled |
+----+-------------------------+-----------+--------+-----------+
| 1 | 2016-01-02 23:40:46.517 | 1 | 1 | 1 |
| 2 | 2016-01-16 00:07:59.857 | 1 | 2 | 1 |
| 3 | 2016-01-25 15:17:54.420 | 3 | 3 | 1 |
| 25 | 2016-03-07 16:48:39.260 | 24 | 10 | 0 |
| 26 | 2016-03-07 16:48:39.263 | 25 | 2 | 0 |
+----+-------------------------+-----------+--------+-----------+
(thanks http://www.sensefulsolutions.com/2010/10/format-text-as-table.html for ASCII table!)
I'm trying to check if a UserId is recorded for more than one CompanyId's.
So far I managed to check if a UserId happens to appear more than one by using this query
WITH T AS
(
SELECT * ,
Count(*) OVER (PARTITION BY UserId) as Cnt
From CompanyUser
)
select Distinct UserId
FROM T
Where Cnt >1
It returns 2 correctly.
Where I'm stuck is, how can I parameterize the UserId and check if an Id is recorded for more than one company.
Declare #UserID as bigint
Set #UserID = 2
select Distinct Count(CompanyID)
FROM ComapynUser
Where UserId = #UserId
I think this gives you what you need.

Selecting grouped rows after first two rows SQL Server

This is a bit of a tricky question/situation and my search fu failed me.
Lets say i have the following data
| UID | SharedID | Type | Date |
|-----|----------|------|-----------|
| 1 | 1 | foo | 2/4/2016 |
| 2 | 1 | foo | 2/5/2016 |
| 3 | 1 | foo | 2/8/2016 |
| 4 | 1 | foo | 2/11/2016 |
| 5 | 2 | bar | 1/11/2016 |
| 6 | 2 | bar | 2/11/2016 |
| 7 | 3 | baz | 2/1/2016 |
| 8 | 3 | baz | 2/3/2016 |
| 9 | 3 | baz | 2/11/2016 |
And I would like to ommit a variable number of leading rows (most recent date in this case) and lets say that number is 2 in this example. The resulting table would be something like this:
| UID | SharedID | Type | Date |
|-----|----------|------|-----------|
| 1 | 1 | foo | 2/4/2016 |
| 2 | 1 | foo | 2/5/2016 |
| 7 | 3 | baz | 2/1/2016 |
Is this possible in SQL? Essentially I want to filter on an unknown number of rows which uses the date column as the order by. The goal is to get the oldest types and get a list of UID's in the process.
Sure, it's possible. Use a ROW_NUMBER function to assign a value to each row, partitioning by the SharedID column so that the count restarts every time that ID changes, and select those rows with a value greater than your limit.
WITH cteNumberedRows AS (
SELECT UID, SharedID, Type, Date,
ROW_NUMBER() OVER(PARTITION BY SharedID ORDER BY Date DESC) AS RowNum
FROM YourTable
)
SELECT UID, SharedID, Type, Date
FROM cteNumberedRows
WHERE RowNum > 2;
Not sure if I understand what you mean but something like this?
SELECT * FROM MyTable t1 JOIN MyTable T2 ON t2.id NOT IN (
SELECT TOP 2 UID FROM myTable
WHERE SharedID = t1.sharedID
ORDER BY [Date] DESC
)

Resources