I'm trying to get a specific subset of data using "over(partition)" syntax. I've created sample data to illustrate. Running the following CTE results in the a small example result set.
I need to calculate 2 date ranges using the following definitions/pseudocode
1: Days to close = CloseDate where stat = 'Closed' minus opendate partitioned by problem
2: Days to solve = "ClosedDate where stat = 'Solved' minus opendate partitioned by problem.
I can get #1 using the over(partition) syntax, but I cannot figure out #2.
with cte as
(
select 114110712007835 as 'SRNumber', 214110712007835004 as ProblemNumber, 'Open' as 'Stat', 314110712007835004001 as TaskNumber, convert(datetime, '2015-03-02 19:47:43',120) as OpenDate, convert(datetime, '2015-03-03 19:36:37',120) as CloseDate union
select 114110712007835 as 'SRNumber', 214110712007835004 as ProblemNumber, 'Investigate' as 'stat', 314110712007835004002 as TaskNumber, convert(datetime, '2015-03-04 00:29:13',120) as OpenDate, convert(datetime, '2015-03-05 19:36:34',120) as CloseDate union
select 114110712007835 as 'SRNumber', 214110712007835004 as ProblemNumber, 'Solve' as 'stat', 314110712007835004003 as TaskNumber, convert(datetime, '2015-03-06 18:17:13',120) as OpenDate, convert(datetime, '2015-03-07 13:07:31',120) as CloseDate union
select 114110712007835 as 'SRNumber', 214110712007835004 as ProblemNumber, 'Close' as 'stat', 315032012542588001001 as TaskNumber, convert(datetime, '2015-03-08 15:24:34',120) as OpenDate, convert(datetime, '2015-03-09 15:15:42',120) as CloseDate union
select 114110712007835 as 'SRNumber', 215032012542588001 as ProblemNumber, 'Open' as 'stat', 315032012542588001002 as TaskNumber, convert(datetime, '2015-04-20 20:05:48',120) as OpenDate, convert(datetime, '2015-04-21 03:24:24',120) as CloseDate union
select 114110712007835 as 'SRNumber', 215032012542588001 as ProblemNumber, 'Investigate' as 'stat', 315032012542588001003 as TaskNumber, convert(datetime, '2015-04-22 18:55:03',120) as OpenDate, convert(datetime, '2015-04-23 03:24:28',120) as CloseDate union
select 114110712007835 as 'SRNumber', 215032012542588001 as ProblemNumber, 'Solve' as 'stat', 315032012542588001004 as TaskNumber, convert(datetime, '2015-04-24 13:35:24',120) as OpenDate, convert(datetime, '2015-04-27 02:24:31',120) as CloseDate union
select 114110712007835 as 'SRNumber', 215032012542588001 as ProblemNumber, 'Close' as 'stat', 315032012542588001004 as TaskNumber, convert(datetime, '2015-04-26 13:35:24',120) as OpenDate, convert(datetime, '2015-04-29 03:24:31',120) as CloseDate
)
select srnumber, problemnumber, stat, opendate, closedate, --min(opendate) over(partition by problemnumber) as MinDate, max(closedate) over(partition by problemnumber) as MaxDate
case
when stat = 'Solve' then datediff(mi,min(opendate) over(partition by problemnumber),max(closedate) over(partition by problemnumber))
else NULL end as Days_to_Solve
,datediff(mi,min(opendate) over(partition by problemnumber),max(closedate) over(partition by problemnumber)) as Days_to_Close
from cte
order by problemnumber asc, opendate asc
go
A subquery could get you the result you need.
SELECT srnumber ,
problemnumber ,
stat ,
opendate ,
closedate , --min(opendate) over(partition by problemnumber) as MinDate, max(closedate) over(partition by problemnumber) as MaxDate
( CASE WHEN stat = 'Solve'
THEN ( SELECT TOP 1
DATEDIFF(mi, MIN(opendate) OVER ( ),
MAX(closedate) OVER ( ))
FROM cte c
WHERE c.problemnumber = cte.problemnumber
AND c.CloseDate <= cte.CloseDate
)
ELSE NULL
END ) AS Days_to_Solve ,
DATEDIFF(mi, MIN(opendate) OVER ( PARTITION BY problemnumber ),
MAX(closedate) OVER ( PARTITION BY problemnumber )) AS Days_to_Close
FROM cte
ORDER BY problemnumber ASC ,
opendate ASC
My results very closely resemble user1221684's, but I use windows functions instead of a subquery. So my answer should run a little more efficiently and it's simpler. Check it out:
SELECT *,
DATEDIFF( MI, --this is minutes. For days switch "MI" to "DAY"
MIN(CASE WHEN [Stat] = 'Open' THEN OpenDate END) OVER (PARTITION BY problemNumber),
MIN(CASE WHEN [Stat] = 'Solve' THEN CloseDate END) OVER (PARTITION BY problemNumber)
) AS Minutes_To_Solve
,
DATEDIFF( MI, --this is minutes. For days switch "MI" to "DAY"
MIN(CASE WHEN [stat] = 'Open' THEN OpenDate END) OVER (PARTITION BY problemNumber),
MIN(CASE WHEN [Stat] = 'Close' THEN CloseDate END) OVER (PARTITION BY problemNumber)
) AS Minutes_To_Close
FROM CTE
ORDER BY OpenDate
Related
I am am building a Time Dimension table in PostgreSQL with DATE_ID and DATE_DESC.
My T-SQL (works perfectly) script is:
set DATEFIRST 1
;WITH DATES AS (
SELECT CAST('2019-01-01 00:00:00.000' AS datetime) AS [DATE]
UNION ALL
SELECT DATEADD(HH,1,[DATE])
FROM DATES
WHERE DATEADD(HH,1,[DATE]) <= CAST('2019-12-31' AS datetime)
)
SELECT
DATE_ID, DATE_DESC
from
(
SELECT
CONVERT(int, CONVERT(char(8), DATE, 112)) AS DATE_ID,
DATE AS DATE_DESC
FROM
DATES)a
order by 1
OPTION (MAXRECURSION 0)
At the moment Im trying to convert this code to PostgreSQL readable one and it does not work..
Here is mine at the moment:
set EXTRACT(DOW FROM TIMESTAMP '2019-01-01 00:00:00.000')+1
;WITH DATES AS (
SELECT CAST('2019-01-01 00:00:00.000' AS timestamp) AS DATE
UNION ALL
SELECT CURRENT_DATE + INTERVAL '1 hour'
FROM DATES
WHERE CURRENT_DATE + INTERVAL '1 hour' <= CAST('2019-12-31' AS timestamp)
)
SELECT DATE_ID, DATE_DESC from
(SELECT cast(to_char((DATE)::TIMESTAMP,'yyyymmddhhmiss') as BIGInt) AS DATE_ID,
DATE AS DATE_DESC
FROM
DATES)a
order by 1
OPTION (MAXRECURSION 0)
I need all the hours (24h) between 2019-01-01 and 2019-12-31 . At the moment I think OPTION (MAXRECURSION 0) and set EXTRACT(DOW FROM TIMESTAMP '2019-01-01 00:00:00.000')+1 is not working properly.
Its a problem of Recursive CTE, In Postgresql, your desired query will be like below
WITH recursive DATES AS (
SELECT CAST('2019-01-01 00:00:00.000' AS timestamp) AS date_
UNION ALL
SELECT date_ + INTERVAL '1 hour'
FROM DATES
WHERE date_ + INTERVAL '1 hour' <= CAST('2019-12-31' AS timestamp)
)
SELECT DATE_ID, DATE_DESC from
(SELECT cast(to_char((date_)::TIMESTAMP,'yyyymmddhhmiss') as BIGInt) AS DATE_ID,
date_ AS DATE_DESC
FROM
DATES)a
order by 1
DEMO
I would like to ALTER a proc so I can insert results into table. Can you please guide? thank you..
ALTER proc [dbo].[usp_CtotalPPPP]
AS
BEGIN
SELECT CID, Consumer1, Consumer2, datepart(year, getdate()) Year, 'Jan' Month, [Jan_S] Budget, [JanAct] Act, getdate()
FROM CProgramDetails
UNION
SELECT CID, Consumer1, Consumer2, datepart(year, getdate()) Year, 'Feb' Month, [FEBOCV_Spend] Budget, [FEBAct] Act, getdate()
FROM CProgramDetails
UNION
SELECT CID, Consumer1, Consumer2, date part(year, getdate()) Year, 'Mar' Month, [MarOCV_Spend] Budget, [MarAct] Act, getdate()
FROM CProgramDetails
UNION
SELECT CID, Consumer1, Consumer2, datepart(year, getdate()) Year, 'Apr' Month, [AprOCV_Spend] Budget, [AprAct] Act, getdate()
FROM CProgramDetails
INSERT INTO [dbo].[ABCD123] (---this is failing - I do not know where to add this)
End
IF [dbo].[ABCD123] already exists
INSERT INTO [dbo].[ABCD123]
Select ...
Union
Select ...
IF [dbo].[ABCD123] does NOT exist and you want to create it on-the-fly
SELECT CID
, Consumer1
, Consumer2
, datepart(year, getdate()) Year
, 'Jan' Month
, [Jan_S] Budget
, [JanAct] Act
, getdate()
INTO [dbo].[ABCD123] --<< only once in the top query
FROM CProgramDetails
UNION
SELECT ...
UNION
SELECT
I have to find the missing hour in my table , for frequency = 1 I have to find a record per hour, if it's not the case, I have to display the missing hour.
here's my code
declare #StartDate datetime declare #EndDate datetime declare #now datetime set #now = getdate() set #StartDate = dateadd(day,-30,#now) set #EndDate = dateadd(day,-2,#now) Select Flow.Id,Flow.ComponentId, Frequency.Name frequencyName, Flow.MeasurementDate as MeasurementDate, LAG(MeasurementDate) OVER (ORDER BY MeasurementDate) LagValue, abs( DATEDIFF (hour, MeasurementDate, LAG(MeasurementDate) OVER (ORDER BY MeasurementDate) ) ) DifferenceDate , (CASE WHEN DATEDIFF (hour, MeasurementDate, LAG(MeasurementDate) OVER (ORDER BY MeasurementDate) ) > '1' THEN 'Yes' ELSE 'No' END) AS Gap into #tab1 FROM Data.dbo.Flow inner join Data.dbo.Component on flow.ComponentId = Component.Id inner join Data.dbo.Frequency on Flow.Frequency = Frequency.Id Where flow.LoaderCode='TOT' and Flow.Frequency='1' and ScheduledVolume IS NOT NULL and MeasurementDate between #StartDate and #EndDate --and DATEDIFF (hour, MeasurementDate, LAG(MeasurementDate) OVER (ORDER BY MeasurementDate) ) >1 Group By Frequency.Name, Flow.MeasurementDate, Flow.ComponentId select * from #tab1
--if i right understood then try this
DECLARE #StartDate DATETIME
DECLARE #EndDate DATETIME
DECLARE #now DATETIME
IF OBJECT_ID('Tempdb..#tab1') IS NOT NULL
BEGIN
DROP TABLE #tab1
END
SET #now = GETDATE()
SET #StartDate = GETDATE() - 30
SET #EndDate = GETDATE() - 2
SELECT Flow.Id ,
Flow.ComponentId ,
Frequency.Name AS frequencyName ,
CONVERT(DATE, Flow.MeasurementDate) AS [Measurement Date] ,
DATEPART(HOUR, Flow.MeasurementDate) AS [Measurement Hour] ,
COALESCE(LAG(DATEPART(HOUR, Flow.MeasurementDate)) OVER ( PARTITION BY CONVERT(DATE, MeasurementDate) ORDER BY DATEPART(HOUR,
MeasurementDate) ),
0) AS [Measurement Previous Hour]
INTO #tab1
FROM Data.dbo.Flow
INNER JOIN Data.dbo.Component ON Flow.ComponentId = Component.Id
INNER JOIN Data.dbo.Frequency ON Flow.Frequency = Frequency.Id
WHERE Flow.LoaderCode = 'TOT'
AND Flow.Frequency = '1'
AND ScheduledVolume IS NOT NULL
AND CONVERT(DATE, MeasurementDate) BETWEEN CONVERT(DATE, #StartDate)
AND CONVERT(DATE, #EndDate)
SELECT T.* ,
CASE WHEN ( T.[Measurement Hour] - T.[Measurement Previous Hour] ) > 1
THEN ( T.[Measurement Hour] - T.[Measurement Previous Hour] - 1 )
ELSE 0
END AS [Missing Hours]
FROM #tab1a AS T
WHERE ( T.[Measurement Hour] - T.[Measurement Previous Hour] ) > 1
I've been struggling with this but I'm not really good at tsql.
This is what I got, and I can't have the DateTime calculates all right. I'm getting the sum between A and B but not the total sum. For example in the last column I have a 0 which is getting me back to -x.
Here is the procedure, and some of the data are like this:
Code_Procedure date_evenement codes_situation
---------------------------------------------------------------
000079500000 2013-05-21 13:07:00.000 COMCFM
000079500000 2013-05-21 20:24:00.000 PCHCFM
000079500000 2013-05-22 09:58:00.000 PCHCFM
000079500000 2013-05-23 00:00:00.000 AARCFM
000079500000 2013-05-23 00:00:00.000 LIVCFM
000079600000 2013-05-21 13:07:00.000 COMCFM
000079600000 2013-05-21 20:24:00.000 PCHCFM
000079600000 2013-05-22 11:18:00.000 PCHCFM
000079600000 2013-05-23 00:00:00.000 AARCFM
000079600000 2013-05-23 00:00:00.000 LIVCFM
Here is the proc:
DECLARE #COMCFM TABLE(numero_colis VARCHAR(25), date_evenement DATETIME);
INSERT #COMCFM SELECT TOP(5) numero_colis, date_evenement FROM cartitem_colis_postaux_etats WHERE (code_situation = 'PCH' AND code_justification = 'CFM')
WHILE (SELECT COUNT(*) FROM #COMCFM) > 0
BEGIN
DECLARE #Colis TABLE(numero_colis VARCHAR(25), date_evenement DATETIME, code_situation_code_justification NVARCHAR(32));
INSERT #Colis SELECT numero_colis, date_evenement, code_situation + code_justification FROM cartitem_colis_postaux_etats WHERE numero_colis = (SELECT TOP(1) numero_colis FROM #COMCFM) ORDER BY numero_colis, date_evenement
;WITH CTE AS
(
Select DISTINCT
*
,ROW_NUMBER() OVER(PARTITION BY numero_colis ORDER BY date_evenement ASC) Rn FROM #Colis
),CTE1 AS
(
SELECT DISTINCT
A.*
,DATEDIFF(mi, B.date_evenement, A.date_evenement) AS DIFF
FROM CTE A INNER JOIN CTE B On B.Rn + 1 = A.Rn
UNION All
SELECT A.*, 0 FROM CTE A Where Rn = 1
)
SELECT
A.*
,ISNULL((
SELECT
A.DIFF + B.DIFF
FROM CTE1 AS B
WHERE A.numero_colis = B.numero_colis
AND A.Rn = B.Rn + 1), 0) AS Sums
FROM CTE1 AS a
ORDER BY numero_colis, Rn ASC
DELETE FROM #Colis
DELETE FROM #COMCFM WHERE numero_colis = (SELECT TOP(1) numero_colis FROM #COMCFM)
END
I'm not really sure what you would like to achieve. Do you need date differencies as a cummulated value? If you need this, change your BEGIN-END block of your stored procedure with this code
BEGIN
DECLARE #Colis TABLE(numero_colis VARCHAR(25), date_evenement DATETIME, code_situation_code_justification NVARCHAR(32));
INSERT #Colis SELECT numero_colis, date_evenement, code_situation + code_justification FROM cartitem_colis_postaux_etats WHERE numero_colis = (SELECT TOP(1) numero_colis FROM #COMCFM) ORDER BY numero_colis, date_evenement
;WITH CTE AS
(
SELECT DISTINCT
*,
ROW_NUMBER() OVER(PARTITION BY numero_colis ORDER BY date_evenement ASC) Rn
FROM #Colis
),CTE1 AS
(
SELECT A.*, 0 AS CummulatedDiff
FROM CTE A
WHERE Rn = 1
UNION ALL
SELECT DISTINCT A.*, B.CummulatedDiff + DATEDIFF(mi, B.date_evenement, A.date_evenement) AS CummulatedDiff
FROM CTE AS A INNER JOIN
CTE1 AS B ON B.Rn + 1 = A.Rn AND B.numero_colis = A.numero_colis
)
SELECT *
FROM CTE1 AS a
ORDER BY numero_colis, Rn ASC
DELETE FROM #Colis
DELETE FROM #COMCFM WHERE numero_colis = (SELECT TOP(1) numero_colis FROM #COMCFM)
END
I hope this takes you further to your goal.
I have this table called Table1 as follows:
UserID Date
1 01/01/09
1 14/01/09
1 25/01/09
1 01/02/09
1 15/02/09
2 02/02/09
2 15/02/09
I am trying to return a result that counts the number of times between the MIN(Date) and 30 days after the MIN(Date) which is DATEADD(day,30,MIN(DATE)). So it would look something like this:
UserID Count
1 3
2 2
This code below is wrong but it expresses what I am trying to achieve:
SELECT COUNT(1) AS Count
FROM Table1
GROUP BY UserID
WHERE Date BETWEEN MIN(Date) AND DATEADD(day,30,MIN(DATE))
SELECT a.UserID, COUNT(a.UserID) AS [Count]
FROM Table1 AS a
INNER JOIN
(
SELECT UserID, MIN([Date]) AS MinDate
FROM Table1
GROUP BY UserID
) AS b
ON a.UserID = b.UserID
WHERE [Date] BETWEEN MinDate AND DATEADD(day, 30, MinDate)
GROUP BY a.UserID
Try this
DECLARE #table TABLE(
UserID INT,
DDate DATETIME
)
INSERT INTO #table (UserID,DDate) SELECT 1, '01 Jan 2009'
INSERT INTO #table (UserID,DDate) SELECT 1, '14 Jan 2009'
INSERT INTO #table (UserID,DDate) SELECT 1, '25 Jan 2009'
INSERT INTO #table (UserID,DDate) SELECT 1, '01 Feb 2009'
INSERT INTO #table (UserID,DDate) SELECT 1, '15 Feb 2009'
INSERT INTO #table (UserID,DDate) SELECT 2, '02 Feb 2009'
INSERT INTO #table (UserID,DDate) SELECT 2, '15 Feb 2009'
SELECT t.UserID,
COUNT(t.UserID)
FROM #table t INNER JOIN
(
SELECT UserID,
MinDate,
DATEADD(dd, 30, MinDate) MinDataAdd30
FROM (
SELECT UserID,
MIN(DDate) MinDate
FROM #table
GROUP BY UserID
) MINDates
) DateRange ON t.UserID = DateRange.UserID
WHERE t.DDate BETWEEN DateRange.MinDate AND DateRange.MinDataAdd30
GROUP BY t.UserID
I think you'll need to use a subquery to get the minimum date. I've shown it below
as a separate query into a variable as I'd probably turn this into a table-valued function.
DECLARE #STARTDATE DATETIME
SELECT #STARTDATE = MIN(DATE) FROM Table1
SELECT COUNT(1) AS Count
FROM Table1
GROUP BY UserID
WHERE Date BETWEEN #STARTDATE AND DATEADD(day,30,#STARTDATE)
I would do it like this:
select a.UserID, count(case when DDate - MinDate <= 30 then 1 end) as Count
from (
select UserID, min(DDate) MinDate
from Table1
group by UserID
) a
inner join Table1 t on a.UserID = t.UserID
group by a.UserID