I am looking for SSMS (t-SQL) query to return info from the max updated_at row when date in column 1 = [updated_at] is < date in column 2 = [date]. Sometimes candidate_id has more [updated_at] ids, I want the query to return the latest [updated_at] if [updated_at] < [date].
So far I have following, but it only return info for max id and date in column 1 < date in column 2 not when. It doesn't return info if max [updated_at] is > [date]. Hope this make sense
SELECT
[candidate_id],
[date],
[updated_at]
[updated_by_id],
[user_id],
[summary],
[id]
FROM tmpacl1
WHERE EXISTS (
SELECT tmpacl1.id
FROM (
SELECT [candidate_id],
[date],
[updated_at],
[updated_by_id],
[user_id],
[summary],
[id]
FROM TPacl
WHERE (
updated_at = (
SELECT MAX(updated_at) AS TPMax
FROM TPacl_1
WHERE (candidate_id = TPacl.candidate_id)
)
)
AND ( [updated_at] < [date])
) AS TPacl_2
WHERE (tmpacl1.id = id) AND tmpacl1.candidate_id = candidate_id)
Thank you in advance
I have a query that calculates duration of an incident. However, it doesn't include the current time for the still "open" incidents. I am trying to figure out a way to add that to the below. This is running on Azure SQL 12.0.2000.8. Per the example, Incident 18 and 19 are closed (last record has StatusID<>1), so my current calculation is correct. However, Incident 20 is ongoing (last record has StatusId=1) and needs to calculate the time between the last update and now.
Structure:
CREATE TABLE [dbo].[IncidentActions](
[Id] [INT] IDENTITY(1,1) NOT NULL,
[IncidentId] [INT] NOT NULL,
[ActionDate] [DATETIMEOFFSET](7) NOT NULL,
[Description] [NVARCHAR](MAX) NOT NULL,
[StatusId] [INT] NOT NULL
) ON [PRIMARY]
GO
INSERT INTO [dbo].[IncidentActions] VALUES
( 51, 18, N'2020-03-10T13:39:27.8621563+00:00', N'This is a demo of the app ops incident management portal', 1 ),
( 52, 18, N'2020-03-10T13:41:42.4306254+00:00', N'Superfast update we''re on it', 1 ),
( 53, 18, N'2020-03-10T13:42:19.0766735+00:00', N'Found a workaround', 1 ),
( 55, 18, N'2020-03-10T13:44:05.7958553+00:00', N'Suspending for now', 2 ),
( 56, 18, N'2020-03-10T13:44:49.732564+00:00', N'No longer suspended', 1 ),
( 57, 18, N'2020-03-10T13:45:09.8056202+00:00', N'All sorted', 3 ),
( 58, 19, N'2020-03-11T14:47:05.6968653+00:00', N'This is just a test', 1 ),
( 59, 19, N'2020-03-11T14:51:20.4522014+00:00', N'Found workaround and root cause, not yet fixed', 1 ),
( 60, 19, N'2020-03-11T14:52:34.857061+00:00', N'Networking issues, updates suspended', 2 ),
( 61, 19, N'2020-03-11T14:54:48.2262037+00:00', N'Network issue resolved, full functionality restored', 3 ),
( 62, 20, N'2020-03-12T10:49:11.5595048+00:00', N'There is an ongoing issue', 1 ),
( 63, 20, N'2020-03-12T11:29:37.9376805+00:00', N'This incident is ongoing....', 1 )
GO
CREATE TABLE [dbo].[IncidentStatuses](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Description] [nvarchar](500) NOT NULL
) ON [PRIMARY]
GO
INSERT INTO [dbo].[IncidentStatuses] VALUES
( 1, N'OPEN' ), ( 2, N'SUSPENDED' ), ( 3, N'CLOSED' )
GO
Query:
WITH allActions
AS (SELECT IncidentId,
ActionDate,
IncidentStatuses.Description,
ROW_NUMBER() OVER (PARTITION BY IncidentId ORDER BY ActionDate) AS rowNum
FROM IncidentActions
INNER JOIN dbo.IncidentStatuses ON IncidentStatuses.Id = IncidentActions.StatusId
)
,actionPeriods
AS (SELECT firstAction.IncidentId,
firstAction.Description StartStatus,
secondAction.Description EndStatus,
DATEDIFF(SECOND, firstAction.ActionDate, secondAction.ActionDate) SecondsElapsed
FROM allActions firstAction
INNER JOIN allActions secondAction ON firstAction.rowNum +1 = secondAction.rowNum --the next action
AND firstAction.IncidentId = secondAction.IncidentId --for the same incident
)
SELECT
actionPeriods.IncidentId,
SUM(CASE WHEN actionPeriods.StartStatus = 'OPEN' THEN actionPeriods.SecondsElapsed ELSE 0 END) SecondsActive,
SUM(CASE WHEN actionPeriods.StartStatus <> 'OPEN' THEN actionPeriods.SecondsElapsed ELSE 0 END) SecondsInactive,
SUM(actionPeriods.SecondsElapsed) SecondsElapsed
FROM actionPeriods
GROUP BY actionPeriods.IncidentId
GO
Using a CTE is quit overkill. You could use tsql windowing functions. In this case the lag and or lead functions. I have added a sample code based on your tables.
select
IncidentId,
StatusId,actiondate ,
lag(actiondate) over (partition by incidentid order by incidentid, actiondate) as previousrow,
coalesce(
lead(actiondate) over (partition by incidentid order by incidentid, actiondate),
case
when (max(actiondate) over (partition by incidentid order by incidentid) = actiondate) and (statusid = 3) then actiondate
when (max(actiondate) over (partition by incidentid order by incidentid) = actiondate) and (statusid = 1) then convert([DATETIMEOFFSET](7),getdate())
end
) as nextrow
from
dbo.IncidentActions
order by
IncidentId, ActionDate
Applying LEAD instead of a self-join based on Row_number:
WITH allPeriods AS
(
SELECT IncidentId,
Lead(ActionDate) Over (PARTITION BY IncidentId ORDER BY ActionDate DESC) AS ActionDate,
Lead(st.Description) Over (PARTITION BY IncidentId ORDER BY ActionDate DESC) AS StartStatus,
st.Description AS EndStatus,
CASE -- return NOW if the last row is "open"
WHEN Row_Number() Over (PARTITION BY IncidentId ORDER BY ActionDate DESC) = 1
AND StatusId = 1
THEN getdate()
ELSE ActionDate
END AS nextDate
FROM IncidentActions AS act
JOIN dbo.IncidentStatuses AS st
ON st.Id = act.StatusId
),
elapsed AS
( SELECT *,
DATEDIFF(SECOND, ActionDate, nextDate) AS SecondsElapsed
FROM allPeriods
)
SELECT
IncidentId,
Sum(CASE WHEN StartStatus = 'OPEN' THEN SecondsElapsed ELSE 0 END) SecondsActive,
Sum(CASE WHEN StartStatus <> 'OPEN' THEN SecondsElapsed ELSE 0 END) SecondsInactive,
Sum(SecondsElapsed) SecondsElapsed
FROM elapsed
WHERE ActionDate IS NOT NULL
GROUP BY IncidentId
See fiddle
For the curious, the final version, courtesy of Remko
WITH actionPeriods /* This one determines the elapsed time between actions */
AS (SELECT IncidentId,
IncidentStatuses.Description StatusDesc,
/* LAG(ActionDate) OVER (PARTITION BY IncidentId ORDER BY IncidentId, ActionDate) AS previousrow, */
DATEDIFF(SECOND,ActionDate,
COALESCE(LEAD(ActionDate) OVER (PARTITION BY IncidentId ORDER BY IncidentId, ActionDate), /* Lead gets the next action */
CASE /* If the next aciton is NULL, then get either the current time for active, or the original time for closed */
WHEN (MAX(ActionDate) OVER (PARTITION BY IncidentId ORDER BY IncidentId) = ActionDate) AND (StatusId = (SELECT Id FROM dbo.IncidentStatuses WHERE Description = 'CLOSED')) THEN ActionDate
WHEN (MAX(ActionDate) OVER (PARTITION BY IncidentId ORDER BY IncidentId) = ActionDate) AND (StatusId <> (SELECT Id FROM dbo.IncidentStatuses WHERE Description = 'CLOSED')) THEN GETUTCDATE()
END
)
) SecondsElapsed
FROM dbo.IncidentActions
INNER JOIN dbo.IncidentStatuses ON IncidentStatuses.Id = IncidentActions.StatusId
)
SELECT
actionPeriods.IncidentId,
SUM(CASE WHEN actionPeriods.StatusDesc = 'OPEN' THEN actionPeriods.SecondsElapsed ELSE 0 END) SecondsActive, /* This counts periods that are active */
SUM(CASE WHEN actionPeriods.StatusDesc = 'SUSPENDED' THEN actionPeriods.SecondsElapsed ELSE 0 END) SecondsInactive, /* This count periods that are inactive */
SUM(CASE WHEN actionPeriods.StatusDesc IN ('OPEN','SUSPENDED') THEN actionPeriods.SecondsElapsed ELSE 0 END) SecondsElapsed /* We don't want periods that were CLOSED, as the ticket was not elapsing during that time */
FROM actionPeriods
GROUP BY actionPeriods.IncidentId
I'm struggling to write a SQL query that will identify customers who have taken out multiple loans in a short period of time (say 60 days).
MyTable
CREATE TABLE [dbo].[MyTable](
[Customerid] [numeric](18, 0) NOT NULL,
[Date Open] [date] NOT NULL,
[Date Closed] [date] NOT NULL,
[Rank] [varchar](50) NOT NULL
) ON [PRIMARY]
E.g. - Customerid 1 has taken multiple loans with date open say 12/01/2017, 12/02/2017, 13/04/2018 etc. I need to find the customers who have taken next loan (Date Open) within 60 days.
It looks like you can do this with a simple INNER JOIN. I'm not entirely sure what Rank indicates in your table, but something like below should work if they can't take out two loans on the same day.
SELECT DISTINCT yt1.Customerid
FROM yourtable yt1
INNER JOIN yourtable yt2 ON yt2.Customerid = yt1.Customerid AND
yt2.[Date Open] > yt1.[Date Open] AND
yt2.[Date Open] <= DATEADD(DAY, 60, yt1.[Date Open])
I have a table that's structured like the one below. I'm wondering if it's possible to compress each transaction by the TransactionID, pull values from the first and last row, then finally calculate a duration for the transaction.
For instance below, I would like to pull RecordID and CustomerID. Then alias a new column called startingStatus which would be 'Submitted' from the first row. I would alias a second column called endingStatus that would be 'Success' from the last row. I then need to grab the timestamps in the same manner, creating startingTime and endingTime. The final column would be the difference between starting and ending in seconds.
RecordID TransactionID CustomerID Status TimeStamp
1 12 10 Submitted 04/07/2014 14:32:23
2 12 10 Queued 04/07/2014 14:32:24
3 12 10 Processing 04/08/2014 14:32:26
4 12 10 Error 04/09/2014 14:32:27
5 12 10 Resubmitted 04/10/2014 15:12:29
6 12 10 Queued 04/11/2014 15:12:31
7 12 10 Processing 04/12/2014 15:12:34
8 12 10 Success 04/13/2014 15:12:47
I've been trying to group by TransactionID and using MIN and MAX, but I haven't gotten it working yet.
How would I go about doing something like this?
Give this a try:
SELECT
CustomerID,
TransactionID,
Min([TimeStamp]) as StartTime,
Max([TimeStamp]) as EndTime,
DATEDIFF(MINUTE,Min([TimeStamp]),Max([TimeStamp])) as TransactionTime
FROM YourTable
GROUP BY CustomerID, TransactionID
ORDER BY CustomerID, TransactionID
I would use window functions to retrieve the first and last records for a group.
This will work for SQL Server 2005 and later by using the ROW_NUMBER() window function and then pivoting the results with the MAX/CASE WHEN method:
SELECT
[TransactionID],
[CustomerID],
MAX(CASE WHEN [rn_asc] = 1 THEN [Status] ELSE NULL END) [startingStatus],
MAX(CASE WHEN [rn_desc] = 1 THEN [Status] ELSE NULL END) [endingStatus],
MAX(CASE WHEN [rn_asc] = 1 THEN [TimeStamp] ELSE NULL END) [startingTimeStamp],
MAX(CASE WHEN [rn_desc] = 1 THEN [TimeStamp] ELSE NULL END) [endingTimeStamp],
DATEDIFF(
SECOND,
MAX(CASE WHEN [rn_asc] = 1 THEN [TimeStamp] ELSE NULL END),
MAX(CASE WHEN [rn_desc] = 1 THEN [TimeStamp] ELSE NULL END)
) [duration]
FROM
(
SELECT
[TransactionID],
[CustomerID],
ROW_NUMBER() OVER (PARTITION BY [TransactionID], [CustomerID] ORDER BY [RecordID] ASC) [rn_asc],
ROW_NUMBER() OVER (PARTITION BY [TransactionID], [CustomerID] ORDER BY [RecordID] DESC) [rn_desc],
[Status],
[Timestamp]
FROM [tbl]
) A
GROUP BY
[TransactionID],
[CustomerID]
SQL Server 2012 introduced the FIRST_VALUE function, so if you're running that version you can consider this query
SELECT DISTINCT
[TransactionID],
[CustomerID],
FIRST_VALUE([Status]) OVER (PARTITION BY [TransactionID], [CustomerID] ORDER BY [RecordID] ASC) [startingStatus],
FIRST_VALUE([Status]) OVER (PARTITION BY [TransactionID], [CustomerID] ORDER BY [RecordID] DESC) [endingStatus],
FIRST_VALUE([TimeStamp]) OVER (PARTITION BY [TransactionID], [CustomerID] ORDER BY [RecordID] ASC) [startingTimeStamp],
FIRST_VALUE([TimeStamp]) OVER (PARTITION BY [TransactionID], [CustomerID] ORDER BY [RecordID] DESC) [endingTimeStamp],
DATEDIFF(
SECOND,
FIRST_VALUE([TimeStamp]) OVER (PARTITION BY [TransactionID], [CustomerID] ORDER BY [RecordID] ASC),
FIRST_VALUE([TimeStamp]) OVER (PARTITION BY [TransactionID], [CustomerID] ORDER BY [RecordID] DESC)
) [duration]
FROM [tbl]
I have a Timesheet table in SQL Server which stores personnel roles
CREATE TABLE [dbo].[TimeSheetTable](
[pk_TimeSheetID] [bigint] IDENTITY(1,1) NOT NULL,
[fk_PersonelID] [int] NOT NULL,
[StartDate] [datetime] NOT NULL,
[EndDate] [datetime] NOT NULL,
[fk_RoleID] [int] NOT NULL,
[Comments] [nvarchar](2000) NULL,
[dateCreated] [datetime] NOT NULL,
[CreatedBy] [int] NULL,
Sometimes, an employee can take a vacation, with overlapping months, starting from 28-Oct-2012 to 2-Nov-2012. With the above example, the employee had 4 days in October and 3 days in November.
My problem is writing a query that counts the number of days of vacation taken for each. If the enddate exceeds the month, it shouldn't count that.
SELECT TOP 1000
sum(DATEDIFF(day,startdate,EndDate))
FROM [db_ASCO_Personel].[dbo].[TimeSheetTable]
where fk_RoleID=51 /* how do I count leave days that over lap between two months*/
Row #17 has a 1 day in nov and 2days in dec( total 3 days vacation). how do I count the number of vacation days taking by each employee for 1 month alone?
TimeSheetID StartDate EndDate RoleID Comments dateCreated CreatedBy
15 August-6-2012 August-7-2012 51 03:10.6 NULL
16 August-1-2012 August-2-2012 51 03:31.5 NULL
17 November-29-2012 December-3-2012 51 51:15.5 NULL
You need to know the start and end of the month. Something like this:
with const as (
select cast('2012-09-01' as date) as MonthStart, cast('2012-09-30' as date) as MonthEnd
)
select sum(DATEDIFF(day,
(case when startdate < const.MonthStart then const.MonthStart else startdate end),
(case when enddate > const.MonthEnd then const.MontEnd else ndDate end)
)
FROM [db_ASCO_Personel].[dbo].[TimeSheetTable] tst cross join
const
where fk_RoleID=51 and
startdate <= const.MonthEnd and
enddate >= const.MonthStart
I would just grab the data and calculate with c# and put into a new table (or do whatever needs to be done with the values)
but if you can be sure that vacation time will never be more than an entire month (ex start is jan and end is never later than feb) then you could do some if else
SELECT TOP 1000 CASE
WHEN month(startDate) = month(enddate)
THEN sum(DATEDIFF(day,startdate,EndDate)) as daymonth1, 0 as daysmonth2
ELSE sum(DATEDIFF(day,startdate,EndDate)) - day(enddate) as daymonth1,day(enddate) as daysmonth2
END
FROM [db_ASCO_Personel].[dbo].[TimeSheetTable]
where fk_RoleID=51
check that syntax as I dont have ssms on this comp to fully test
Edit: to powaga: Unless I'm stupid he did (just not clearly) he said he wants to return 4 and 3 instead of 7 ... he want the resulting number of days to be per month instead of number of days