Calculate date difference between dates in different rows of a dataset - sql

A table looks like this :
CREATE TABLE [dbo].[HistDT](
[ID] [bigint] NULL,
[StartDtSK] [varchar](8) NULL,
[StartDt] [datetime] NULL,
[status] [nvarchar](30) NULL,
) ON [PRIMARY]
Example data set:
ID | StartDtSK | StartDt | Status |
1 20190520 20-05-2019 12:00:13 10
1 20190520 20-05-2019 10:00:00 5
1 20190414 14-04-2019 13:23:00 2
2 20190312 12-03-2019 10:03:00 10
2 20190308 08-03-2019 18:03:00 1
etc..
I need a query which will display the number of days spent in each status. That would be easy if the table i inherited had an end date. I would then calculate the datediff and pivot for column status values.
Maybe i should create a new table using ssis where i will add an EndDt column which will be the StartDt of the latest added Status.
But is there any way to do this without creating another table?

SQL Server 2008
This is not very pretty, and I haven't tested it for all use cases. I hope you can use it or find inspiration. I'm sure there is a better way :)
declare #table2 table (
[ID] [bigint] NULL,
[StartDtSK] [varchar](8) NULL,
[StartDt] [datetime] NULL,
[status] [nvarchar](30) NULL
)
insert into #table2
values
(1 , '20190520','2019-05-20 12:00:13','10'),
(1 , '20190520','2019-05-20 10:00:00','5'),
(1 , '20190414','2019-04-14 13:23:00','2'),
(2, '20190312', '2019-03-12 10:03:00', '10'),
(2 , '20190308', '2019-03-08 18:03:00', '1')
select *,DATEDIFF(dd,startdt,enddate) as TotalDAys from (
select x.ID,StartDtSK,Startdt,[Status],Enddate from (
select *,ROW_NUMBER() over(partition by id order by startdt) as rn from #table2
) x
cross apply ( select * from (select id,StartDt as Enddate,ROW_NUMBER() over(partition by id order by startdt) as rn2 from #table2 b
)f where (rn +1 = f.rn2 ) and x.id = f.id ) d
union all
select ID,StartDtSK,startdt,[Status],'9999-12-31' as Enddate from (
select *,ROW_NUMBER() over(partition by id order by startdt desc) as rn from #table2
)X where rn=1
)y
order by id,startdt
SQL Server 2008 without cross apply
This might be a bit more pretty :)
select *,DATEDIFF(dd,startdt,enddate) as TotalDAys from (
select x.ID,StartDtSK,Startdt,[Status],case when Enddate is null then '9999-12-31' else Enddate end as Enddate from (
select *,ROW_NUMBER() over(partition by id order by startdt) as rn from #table2
) x
left join (
select * from (select id,StartDt as Enddate,ROW_NUMBER() over(partition by id order by startdt) as rn2 from #table2 b
)f ) d on (rn +1 = d.rn2 ) and x.id = d.id
)y
SQL Server 2012 and above:
Is this what you want?
declare #table2 table (
[ID] [bigint] NULL,
[StartDtSK] [varchar](8) NULL,
[StartDt] [datetime] NULL,
[status] [nvarchar](30) NULL
)
insert into #table2
values
(1 , '20190520','2019-05-20 12:00:13','10'),
(1 , '20190520','2019-05-20 10:00:00','5'),
(1 , '20190414','2019-04-14 13:23:00','2')
select *,Datediff(dd,Startdt,Enddate) as TotalDays from (
select *,LAG(StartDt,1,'9999-12-31') over(partition by ID order by StartDT desc) as EndDate from #table2
)x
Insert a rule that handles current status (9999-12-31) date

Maybe LEAD function is usefull for your question.
Like this
IsNull(DateAdd(SECOND,-1,Cast(LEAD ([StartDt],1) OVER (PARTITION BY [status] ORDER BY [StartDt]) AS DATETIME)),getdate()) AS EndDate

Related

Find MAX value in two columns simultaneously in SQL table

Can somebody help?
I have this table (simplified view, it has 20 columns and more then 1 mln rows)
date_id period_time PlotsID dateOfContractID
20071225 1:12:45 212 a12f
20080812 1:12:45 212 a12f
20080815 10:07:46 232 f45j
20100213 8:05:12 435 y54g
20100213 8:06:33 435 y54g
And I need to find all data with MAX date_id and MAX period_time, grouped by PlotsID
And it must be this
date_id period_time PlotsID dateOfContractID
20080812 1:12:45 212 a12f
20080815 10:07:46 232 f45j
20100213 8:06:33 435 y54g
Using this code, I found MAX date_id and it's working correct, but I need after Max date find MAx Period_time, OR find in one step
SELECT
[date_id],
[period_time],
[PlotsID],
[FieldID],
[partnerContract],
[ownerContractID],
[partnerContractCode]
FROM
bd
WHERE
EXISTS(
SELECT
1 AS Expr1
FROM
bd AS t2
WHERE
partnerContractCode = bd.partnerContractCode
GROUP BY
partnerContractCode
HAVING
( bd.date_id = MAX(date_id) )
)
) AS t1
Create TABLE
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE bd
( [hash_diff] [varbinary](8000) NOT NULL,
[hash_key] [varbinary](8000) NULL,
[date_id] [int] NULL,
[period_time] [time](7) NULL,
[PlotsID] [bigint] NULL,
[FieldID] [bigint] NULL,
[partnerContract] [nvarchar](100) NULL,
[ownerContractID] [bigint] NULL,
[partnerContractCode] [nvarchar](50) NULL,
)
WITH
(
DISTRIBUTION = HASH ( [hash_key] ),
CLUSTERED INDEX
(
[date_id] ASC
)
)
GO
Try this
DECLARE #tbl_SampleData AS TABLE ( date_id DATE, period_time varchar(8), plotsID INT, dateOfContractID VARCHAR(10))
INSERT INTO #tbl_SampleData (date_id , period_time , plotsID , dateOfContractID )
VALUES
('20071225', '1:12:45', 212, 'a12f'),
('20080812', '1:12:45', 212, 'a12f'),
('20080815', '10:07:46', 232, 'f45j'),
('20100213', '8:05:12', 435, 'y54g'),
('20100213', '8:06:33', 435, 'y54g')
--SELECT * FROM #tbl_SampleData
SELECT date_id, period_time, plotsID, dateOfContractID FROM
(
SELECT date_id, period_time, plotsID, dateOfContractID ,
ROW_NUMBER() OVER(PARTITION BY PlotsID ORDER BY date_id DESC, period_time DESC) AS RowID
FROM #tbl_SampleData
) AS a
WHERE RowID = 1

Select row name with max date with grouping

For example:
create table #t
(
[ID] int,
[Date] date,
[Name] varchar(5)
)
insert into #t
values
(1, getdate(),'1-1'),
(2, dateadd(D,-10,getdate()),'2-1'),
(2, dateadd(D,-5,getdate()),'2-2'),
(1, dateadd(M,-1,getdate()),'1-2')
select * from #t
I need to select [Name] for each [ID] with max [Data].
Something like this:
select [1], [2]
from ( select ID, [Date] from #t ) y
pivot (
max(y.[Date])
for y.ID in ([1],[2])
) pvt;
Output:
1 2
2017-04-28 2017-04-23
but instead of [Date] i want to see [Name]
what i want to view
1 2
1-1 2-2
Please help. Thank you.
Please try the following code
create table #t
(
[ID] int,
[Date] date,
[Name] varchar(5)
)
insert into #t
values
(1, getdate(),'1-1'),
(2, dateadd(D,-10,getdate()),'2-1'),
(2, dateadd(D,-5,getdate()),'2-2'),
(1, dateadd(M,-1,getdate()),'1-2')
select [1], [2]
from ( select ID, [Name] from #t ) y
pivot (
max(y.[Name])
for y.ID in ([1],[2])
) pvt;
drop table #t
You can use row_nubmber() with date desc and pivot as below:
;with cte as (
select id, RowN = row_number() over (partition by id order by date desc), name from #t
) select * from
(select id, name from cte where rown = 1 ) s
pivot (max(name) for id in ([1],[2])) p
You can try the following :
SELECT [1], [2]
FROM (SELECT y.ID,
t.Name
FROM (SELECT ID,
MAX([Date]) AS [Date]
FROM #t
GROUP BY ID ) y
INNER JOIN #t t ON y.[Date] = t.[Date]
) x
PIVOT
(
MAX(x.Name)
FOR x.ID IN ([1],[2])
) pvt;
You can see this here -> http://rextester.com/ZGQGSC94965
Hope this helps!!!
try this one.
CREATE TABLE #t
(
[ID] INT ,
[Date] DATE ,
[Name] VARCHAR(5)
)
INSERT INTO #t
VALUES (1, getdate(),'1-1'),
(2, dateadd(D,-10,getdate()),'2-1'),
(2, dateadd(D,-5,getdate()),'2-2'),
(1, dateadd(M,-1,getdate()),'1-2')
SELECT *
FROM #t;
WITH CTE
AS ( SELECT ID ,
MAX(Date) [Date]
FROM #t
GROUP BY ID
),
CTE2
AS ( SELECT cte.ID ,
cte.Date ,
t.name
FROM CTE
OUTER APPLY ( SELECT TOP 1
name
FROM #t
WHERE (ID = cte.ID AND Date = cte.Date)
) T
)
SELECT MAX([1]) [1] ,
MAX([2]) [2]
FROM ( SELECT ID ,
[Date] ,
NAME
FROM CTE2
) y PIVOT ( MAX(y.NAME) FOR y.ID IN ( [1], [2] ) ) pvt
Result
ID Date Name
----------- ---------- -----
1 2017-05-02 1-1
2 2017-04-22 2-1
2 2017-04-27 2-2
1 2017-04-02 1-2
(4 row(s) affected)
1 2
----- -----
1-1 2-2
(1 row(s) affected)

How can I display repeated values only one time and have '-' if it repeats

I have some subqueries that retreives the same values for each PolicyNumber. How can I substitute repeated value with '-' and only display it one in a top row for each policy?
Right now I have this:
But I need something like this:
SELECT
-------------/* GrossPremium*/
(SELECT ISNULL(SUM(tblFin_InvoiceDetails.AmtBilled), 0)
FROM tblFin_InvoiceDetails WITH (NOLOCK)
WHERE (tblFin_InvoiceDetails.ChargeType = 'P')
AND (tblFin_InvoiceDetails.InvoiceNum = INV.InvoiceNum))
AS GrossPremium
--------------/*CompanyCommissionPercentage*/
,((SELECT ISNULL(SUM(tblFin_InvoiceDetails.MGAAmt), 0)
FROM tblFin_InvoiceDetails
WHERE (tblFin_InvoiceDetails.ChargeType = 'P')
AND (tblFin_InvoiceDetails.InvoiceNum = INV.InvoiceNum))
+
CASE WHEN INV.Remitter = 'B' then
(SELECT ISNULL(SUM(tblFin_InvoiceDetails.RemitterAmt), 0)
FROM tblFin_InvoiceDetails
WHERE (tblFin_InvoiceDetails.ChargeType = 'P')
AND (tblFin_InvoiceDetails.InvoiceNum = INV.InvoiceNum))----------------RemitterCommission
ELSE
(SELECT ISNULL(SUM(tblFin_InvoicedItemsPayees.PayeeAmt), 0)
FROM tblFin_InvoicedItemsPayees
INNER JOIN tblFin_PolicyCharges pc on pc.ChargeCode = tblFin_InvoicedItemsPayees.ChargeCode and pc.chargeType = 'P'
WHERE (tblFin_InvoicedItemsPayees.InvoiceNum = INV.InvoiceNum and tblFin_InvoicedItemsPayees.PayeeGuid = INV.ProducerLocationGuid))
END) * 100 /
NULLIF((SELECT ISNULL(SUM(tblFin_InvoiceDetails.AmtBilled), 0)
FROM tblFin_InvoiceDetails WITH (NOLOCK)
WHERE (tblFin_InvoiceDetails.ChargeType = 'P')
AND (tblFin_InvoiceDetails.InvoiceNum = INV.InvoiceNum)),0)
AS CompanyCommissionPercentage
FROM [tblFin_PayablesWorking] PW
INNER JOIN tblFin_Invoices INV ON PW.InvoiceNumber=INV.InvoiceNum
Well since you do not mention you full query and Table schema, i will give your answer with two simple example.If you want replace your repeated value with - the follow this query(please change the columns name according to your needs).
IF YOU WANT TO SELECT YOUR EXISTING TABLE:
;with ts as (
select S1.[ProductID], row_number() over (partition by S1.[ProductID] order by S1.[ProductID]) as seqnum
from (SELECT [SalesID],[ProductID] FROM [Sales]) AS S1 --Replace 'SELECT [ProductID] FROM [Sales]' with your Subquery and change the column accordingly
)
SELECT
(case when seqnum = 1 then [ProductID] ELSE '-' end) as [ProductID]
FROM ts
FOR USING SUBQUERY:
--CREATE TABLE [dbo].[Sales](
-- [SalesID] [uniqueidentifier] NOT NULL DEFAULT (newid()),
-- [ProductID] [int] NOT NULL,
-- [EmployeeID] [int] NOT NULL,
-- [Quantity] [smallint] NOT NULL,
-- [SaleDate] [datetime] NOT NULL CONSTRAINT [DF_SaleDate] DEFAULT (getdate()),
-- CONSTRAINT [PK_SalesID] PRIMARY KEY CLUSTERED
--(
-- [SalesID] ASC
--)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
--) ON [PRIMARY]
--GO
--INSERT [dbo].[Sales] ([SalesID], [ProductID], [EmployeeID], [Quantity], [SaleDate]) VALUES (N'9498d566-e31b-4ac8-ab54-1c898471fba8', 2, 1, 1, CAST(N'2012-03-01 00:00:00.000' AS DateTime))
--INSERT [dbo].[Sales] ([SalesID], [ProductID], [EmployeeID], [Quantity], [SaleDate]) VALUES (N'69c7dff4-fbac-48d3-ae0a-5027c816acd2', 2, 2, 2, CAST(N'2012-04-01 00:00:00.000' AS DateTime))
--INSERT [dbo].[Sales] ([SalesID], [ProductID], [EmployeeID], [Quantity], [SaleDate]) VALUES (N'a40b9505-4a2c-4186-a89b-88a401248a58', 1, 1, 4, CAST(N'2012-02-01 00:00:00.000' AS DateTime))
--INSERT [dbo].[Sales] ([SalesID], [ProductID], [EmployeeID], [Quantity], [SaleDate]) VALUES (N'04856027-d7ad-40fe-889b-8d933595ffde', 3, 1, 2, CAST(N'2012-02-01 00:00:00.000' AS DateTime))
--INSERT [dbo].[Sales] ([SalesID], [ProductID], [EmployeeID], [Quantity], [SaleDate]) VALUES (N'173be2de-3b80-4a3d-8bcc-a74d0d70b3a9', 3, 2, 1, CAST(N'2012-03-01 00:00:00.000' AS DateTime))
--GO
;with ts as (
SELECT
JOIN1.[SalesID] AS [SalesID]
, JOIN1.[ProductID]
, JOIN1.seqnum AS seqnum
, JOIN2.[EmployeeID], JOIN2.seqnum2 AS seqnum2
FROM
(
select row_number() over (order by S1.[SalesID] asc) as RowNumber
, S1.[SalesID] AS [SalesID]
, S1.[ProductID] AS [ProductID]
, row_number() over (partition by S1.[ProductID] order by S1.[SalesID]) as seqnum
from (SELECT [SalesID],[ProductID] FROM [Sales]) AS S1 --Replace 'SELECT [ProductID] FROM [Sales]' with your Subquery ( For Example GrossPremium) and change the column accordingly. Remember you need some thing common for Iner join, in this case [SalesID]
)AS JOIN1
INNER JOIN
(
select row_number() over (order by S2.[SalesID] asc) as RowNumber
, S2.[SalesID] AS [SalesID]
, S2.[EmployeeID] AS [EmployeeID]
, row_number() over (partition by S2.[EmployeeID] order by S2.[SalesID]) as seqnum2
from (SELECT [SalesID],[EmployeeID] FROM [Sales]) AS S2 --Replace 'SELECT [[SalesID]] FROM [Sales]' with your Subquery ( For Example CompanyCommissionPercentage) and change the column accordingly. Remember you need some thing common for Iner join, in this case [SalesID]
)AS JOIN2
ON JOIN1.[SalesID]=JOIN2.[SalesID]
)
SELECT
(case when seqnum = 1 then [ProductID] ELSE '-' end) as [ProductID]
,(case when seqnum2 = 1 then [EmployeeID] ELSE '-' end) as [EmployeeID]
FROM (Select TOP 10000000 *FROM ts ORDER BY [SalesID] ASC ) AS ts -- Mentioning TOP is Must, or it will give Error
I do know why you are using - instead of NULL, - will take space
if you wanted to do it in SQL (you probably shouldn't it's pretty ugly) you could do something like this using LAG(). It relies on having a field that you can use to sort the records for each Policy Number, in my dummy data below I included a field called RecordID to do this.
SELECT
PolicyNumber
,CASE
WHEN LAG(GrossPremium) OVER(PARTITION BY PolicyNumber ORDER BY RecordID) IS NULL
THEN CAST(GrossPremium AS VARCHAR(MAX))
ELSE '-'
END GrossPremium
,CASE
WHEN LAG(CompanyComissionPercentage) OVER(PARTITION BY PolicyNumber ORDER BY RecordID) IS NULL
THEN CAST(CompanyComissionPercentage AS VARCHAR(MAX))
ELSE '-'
END CompanyComissionPercentage
,CASE
WHEN LAG(RemitterCommissionPercentage) OVER(PARTITION BY PolicyNumber ORDER BY RecordID) IS NULL
THEN CAST(RemitterCommissionPercentage AS VARCHAR(MAX))
ELSE '-'
END RemitterCommissionPercentage
,CASE
WHEN LAG(RemitterCommission) OVER(PARTITION BY PolicyNumber ORDER BY RecordID) IS NULL
THEN CAST(RemitterCommission AS VARCHAR(MAX))
ELSE '-'
END GrossCommission
,CASE
WHEN LAG(RemitterCommission) OVER(PARTITION BY PolicyNumber ORDER BY RecordID) IS NULL
THEN CAST(RemitterCommission AS VARCHAR(MAX))
ELSE '-'
END GrossCommission
FROM
(
-- Dummy data
SELECT
1234 PolicyNumber -- Partition the LAG() on the policy number.
,1 RecordID -- use this to order the LAG() function.
,8749.00 GrossPremium
,18 CompanyComissionPercentage
,10 RemitterCommissionPercentage
,874.90 RemitterCommission
,1574.82 GrossCommission
UNION ALL
SELECT
1234
,2 RecordID
,8749.00
,18
,10
,874.90
,1574.82
UNION ALL
SELECT
5678
,1 RecordID
,8749.00
,18
,10
,874.90
,1574.82
) x;

PIVOT over two columns

is there a more elegant way to implement a PIVOT on the following table - basically I am trying to count the typeid's for ever userid, as well as get the first value for each type and pivot the types and the values (using sql server 2012):
This is the expected result:
And here is what I came up with:
SELECT userid ,
MAX([1]) [1] ,
MAX([2]) [2] ,
MAX(value1) [value1] ,
MAX(value2) [value2]
FROM ( SELECT userid ,
typeid ,
FIRST_VALUE(value) OVER ( PARTITION BY userid, typeid ORDER BY testid ) [value] ,
'value' + CONVERT(VARCHAR(20), typeid) [valuepivot]
FROM dbo.test
) t PIVOT( COUNT(typeid) FOR typeid IN ( [1], [2] ) ) as pivottype
PIVOT( MAX(value) FOR [valuepivot] IN ( [value1], [value2] ) ) as pivottype2
GROUP BY userid
Table Schema:
CREATE TABLE [dbo].[Test](
PIVOT( MAX(value) FOR [valuepivot] IN ( [value1], [value2] ) ) as pivottype2
[TestID] [int] PRIMARY KEY IDENTITY(1,1) ,
[UserID] [int] NOT NULL,
[TypeID] [int] NOT NULL,
[Value] [varchar](100) NOT NULL
)
GROUP BY userid
I would just do this using conditional aggregation:
select userid,
sum(case when typeid = 1 then 1 else 0 end) as test1,
sum(case when typeid = 2 then 1 else 0 end) as test2,
max(case when typeid = 1 then value end) as test1,
max(case when typeid = 2 then value end) as test2
from (select t.*,
row_number() over (partition by userid, typeid order by testid) as seqnum
from test
) t
group by userid;
Whether it is simpler or not is a matter of opinion.

Duplicate a row multiple times

Basically I want to duplicate a row a variable number of times.
I have a table with the following structure:
CREATE TABLE [dbo].[Start](
[ID] [int] NOT NULL,
[Apt] [int] NOT NULL,
[Cost] [int] NOT NULL)
I want to duplicate each row in this table (Apt-1) times so in the end there will be #Apt rows. Moreover for each new row the value of Cost is decremented until it reaches 0. ID will be the same as there are no primary keys. If I have a record like this:
1 5 3
I need 4 new rows inserted in the same table and they should look like this
1 5 2
1 5 1
1 5 0
1 5 0
I have tried so far a lot of ways but I cannot make it work. Many thanks!
try this
DECLARE #Start TABLE (
[ID] [int] NOT NULL,
[Apt] [int] NOT NULL,
[Cost] [int] NOT NULL)
INSERT #Start (ID, Apt, Cost)
VALUES (1, 5, 3)
; WITH CTE_DIGS AS (
SELECT ROW_NUMBER() OVER(ORDER BY (SELECT 1)) AS rn
FROM master.sys.all_columns AS a
)
INSERT #Start (ID, Apt, Cost)
SELECT ID, Apt, CASE WHEN Cost - rn < 0 THEN 0 ELSE Cost - rn END
FROM #Start
INNER JOIN CTE_DIGS
ON Apt > rn
Try:
;with cte as
(select [ID], [Apt], [Cost], 1 counter from [Start]
union all
select [ID],
[Apt],
case sign([Cost]) when 1 then [Cost]-1 else 0 end [Cost],
counter+1 counter
from cte where counter < [Apt])
select [ID], [Apt], [Cost]
from cte