SQL Server Windowing - 24 Hour Window - sql-server

I have the following data
CREATE TABLE [dbo].[Test](
[CustId] [int] NULL,
[Spend] [money] NULL,
[TimeOdSpent] [datetime] NULL,
[ID] [int] IDENTITY(1,1) NOT NULL
) ON [PRIMARY]
GO
SET IDENTITY_INSERT [dbo].[Test] ON
GO
INSERT [dbo].[Test] ([CustId], [Spend], [TimeOdSpent], [ID])
VALUES (11, 400.0000, CAST(N'2016-10-27 10:00:00.000' AS DateTime), 1)
INSERT [dbo].[Test] ([CustId], [Spend], [TimeOdSpent], [ID])
VALUES (11, 200.0000, CAST(N'2016-10-27 11:00:00.000' AS DateTime), 2)
INSERT [dbo].[Test] ([CustId], [Spend], [TimeOdSpent], [ID])
VALUES (11, 400.0000, CAST(N'2016-10-28 09:00:00.000' AS DateTime), 3)
INSERT [dbo].[Test] ([CustId], [Spend], [TimeOdSpent], [ID])
VALUES (11, 500.0000, CAST(N'2016-10-28 16:00:00.000' AS DateTime), 4)
GO
SET IDENTITY_INSERT [dbo].[Test] OFF
Expected Result should be like this
1 2016-10-27 11:00:00.000 600
2 2016-10-28 09:00:00.000 1000
3 2016-10-28 16:00:00.000 900
I want to find out the instances where the spend Totals > 500 within a 24 hour period. Being trying to write a windowing query without luck

You can query as below:
Select * from (
Select *, Sm = sum(spend) over(partition by convert(date,timeofuse)) from #customer
) a
Where Sm > 500

This is the sort of thing I was looking for. I used the Sales.SalesOrderHeader table from AdventureWorks Instead of my simple table above
;WITH cte1 as
(
select
LAG(ShipDate) OVER(PARTITION By SAlesPersonId ORDER BY ShipDate) ShipDateBefore,ShipDate, SalesPersonID,SubTotal,CAST(ShipDate as Date) Date
from Sales.SalesOrderHeader
where CAST(ShipDate as DATE)<'20080710' and SalesPersonID IS NOT NULL
),cte2 as
(Select * ,DATEDIFF(ss,ShipDateBefore,ShipDate) as DiffinDays
from cte1
), cte3 as (
select * ,SUM(DiffinDays) OVER(Partition BY SalesPersonId ORDER BY ShipDate ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) as RunningTime
from cte2
),cte4 as
(select
*,ISNULL(CAST((RunningTime / 86400.00) AS INT),0) Cycle
FROM cte3
)
SELECT
SalesPersonID ,SUM(SubTotal)Total,MIN(ShipDate)DurationStart,MAX(ShipDate)DurationStart
from cte4
GROUP by SalesPersonID,Cycle
Having SUM(SubTotal) > 100000.00

Related

sqlserver get records count repeating

I have one doubt in sql server
get records based on installments
Table :productdetails
CREATE TABLE [dbo].[productdetails](
[productid] [int] NULL,
[Productrstartdate] [date] NULL,
[Productenddate] [date] NULL,
[EMIInstallment] [int] NULL
) ON [PRIMARY]
GO
INSERT [dbo].[productdetails] ([productid], [Productrstartdate], [Productenddate], [EMIInstallment]) VALUES (1, CAST(N'2020-10-02' AS Date), CAST(N'2024-10-02' AS Date), 5)
GO
INSERT [dbo].[productdetails] ([productid], [Productrstartdate], [Productenddate], [EMIInstallment]) VALUES (2, CAST(N'2020-02-10' AS Date), CAST(N'2021-02-10' AS Date), 2)
GO
INSERT [dbo].[productdetails] ([productid], [Productrstartdate], [Productenddate], [EMIInstallment]) VALUES (3, CAST(N'2019-01-10' AS Date), CAST(N'2019-01-10' AS Date), 1)
GO
INSERT [dbo].[productdetails] ([productid], [Productrstartdate], [Productenddate], [EMIInstallment]) VALUES (4, CAST(N'2019-01-18' AS Date), CAST(N'2021-01-18' AS Date), 3)
GO
based on above data i want output like below
Productid |Installmentdate |noofinstallmentcount
1 |2020-10-02 |1
1 |2021-10-02 |2
1 |2022-10-02 |3
1 |2023-10-02 |4
1 |2024-10-02 |5
2 |2020-02-10 |1
2 |2021-02-10 |2
3 |2019-01-10 |1
4 |2019-01-18 |1
4 |2020-01-18 |2
4 |2021-01-18 |3
i tried like below :
DECLARE #MINDATE DATE='2019-01-18'
DECLARE #COUNT INT=10
dECLARE #MAXDATE DATE='2024-10-02'
;WITH ABC
AS
(
SELECT productid ,#MINDATE CalendarDate ,1 as id from [dbo].[productdetails]
UNION ALL
SELECT a.productid ,DATEADD(YEAR,1,a.Productrstartdate ), 1 FROM [dbo].[productdetails] a
join [dbo].[productdetails] b on a.productid=b.productid
WHERE #MINDATE <#MAXDATE )
SELECT * FROM ABC
above query not given expected output
could you please tell me how to write query to achive this task in sql server .
You want a tally here. If you're not going to have a value (much) higher than 10, then just a VALUES clause will work:
SELECT pd.productid,
DATEADD(YEAR, V.I, pd.Productrstartdate) AS Installmentdate,
V.I + 1 AS noofinstallmentcount
FROM dbo.productdetails pd
JOIN (VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9))V(I) ON pd.EMIInstallment > V.I;
If you need bigger numbers, just use a bigger tally:
WITH N AS(
SELECT N
FROM (VALUES(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL))N(N)),
Tally AS(
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) -1 AS I
FROM N N1, N N2, N N3) --1000 rows
SELECT pd.productid,
DATEADD(YEAR, T.I, pd.Productrstartdate) AS Installmentdate,
T.I + 1 AS noofinstallmentcount
FROM dbo.productdetails pd
JOIN Tally T ON pd.EMIInstallment > T.I;
You could create a temp table that mimics the actual productdetails table, then iterate through it, creating an output table that lists the product id with each installment date. Once you have that, you can use ROW_NUMBER() to count the installment numbers. Try this:
SELECT *
INTO #productdetails
FROM productdetails
CREATE TABLE #output (
productid int null
,installmentdate date null)
WHILE (SELECT COUNT(*) FROM #productdetails) > 0
BEGIN
DECLARE #yr int = 0
WHILE (SELECT TOP 1 DATEDIFF(year, DATEADD(year, #yr, Productrstartdate), productenddate) FROM #productdetails) >= 0
BEGIN
INSERT INTO #output (
productid
,installmentdate)
SELECT top 1
productid
,DATEADD(year, #yr, Productrstartdate) installmentdate
FROM #productdetails
SET #yr = #yr + 1
END
DELETE TOP (1) FROM #productdetails
END
SELECT *
,ROW_NUMBER() OVER (PARTITION BY ProductId ORDER BY InstallmentDate asc) NumberOfInstallmentCount
FROM #output
DROP TABLE #output, #productdetails

SQL Select Only the Most Common Results

I have a table with IDs and Items where sometimes the associated Item has a variation from the other Items associated with the same ID. I need a query that selects the most common Item and assigns it to that ID.
The below query works, but I'm hoping to optimize it to avoid having to join two separate CTEs at the end, and rather have one slick SELECT statement:
IF OBJECT_ID('tempdb..#Test') IS NOT NULL
DROP TABLE #Test
CREATE TABLE #Test
(
[ID] INT
,[Item] VARCHAR(20)
)
INSERT #Test
VALUES
(100, 'Apple'),
(100, 'Apple'),
(100, 'Apples'),
(200, 'Orange'),
(200, 'Orange'),
(200, 'Orange'),
(200, 'Oranges'),
(300, 'Grape');
WITH cteOne AS (SELECT
[ID]
,[Item]
,COUNT(*) [Count]
FROM #Test
GROUP BY [ID]
,[Item]
),
cteTwo AS (SELECT
[ID]
,MAX([Count]) [Max]
FROM cteOne
GROUP BY [ID])
SELECT
C1.[ID]
,C1.[Item]
FROM cteOne C1
INNER JOIN cteTwo C2 ON C2.[ID] = C1.[ID]
AND C2.[Max] = C1.[Count]
ORDER BY [ID]
Any help is appreciated!
You can try top 1 with ties with row_number
select
top 1 with ties [ID], [Item]
from (
SELECT
[ID], [Item], COUNT(*) [Count]
FROM #Test
GROUP BY [ID], [Item]
) t
order by row_number() over (partition by [ID] order by [Count] desc)
This is even better:
;WITH
cteOne AS (
SELECT [ID],[Item] ,COUNT(*) [Count]
FROM #Test
GROUP BY [ID],[Item]
),
cteTwoo as (
select *, ROW_NUMBER() over (partition by id order by count) idx
from cteOne
)
select ID, Item
from cteTwoo
where idx = 1

SQL Server Joining record based on max 7 days old

The query that I've build does not fit the solution. I've tried many queries but can't get the result I want.
I have the following table
CREATE TABLE [dbo].[test](
[ID] [int] IDENTITY(1,1) NOT NULL,
[ATC] [varchar](15) NOT NULL,
[PID] [varchar](7) NOT NULL,
[NAME] [varchar](50) NOT NULL,
[REG_DATE] [datetime] NULL,
[ACTIVE] [bit] NOT NULL CONSTRAINT [DF_test_ACTIVE] DEFAULT ((1))
) ON [PRIMARY]
Example data
INSERT INTO [dbo].[test] ([ATC],[PID],[NAME],[REG_DATE],[ACTIVE])
VALUES ('A01','123456','TEST1','2016-08-31 00:00:00.000',0);
INSERT INTO [dbo].[test] ([ATC],[PID],[NAME],[REG_DATE],[ACTIVE])
VALUES ('A01','123456','TEST2','2016-09-01 00:00:00.000',0);
INSERT INTO [dbo].[test] ([ATC],[PID],[NAME],[REG_DATE],[ACTIVE])
VALUES ('A01','123456','TEST3','2016-09-02 00:00:00.000',0);
INSERT INTO [dbo].[test] ([ATC],[PID],[NAME],[REG_DATE],[ACTIVE])
VALUES ('A01','123456','TEST4','2016-09-03 00:00:00.000',0);
INSERT INTO [dbo].[test] ([ATC],[PID],[NAME],[REG_DATE],[ACTIVE])
VALUES('A01','123456','TEST5','2016-09-06 00:00:00.000',1);
Example Query:
;WITH CTE AS
(
SELECT ROW_NUMBER() OVER (PARTITION BY ATC, PID ORDER BY REG_DATE ASC) AS ROWNUM,* FROM [dbo].[test]
WHERE ACTIVE=0
)
SELECT DATEDIFF(DAY, d2.REG_DATE, d1.REG_DATE),d1.NAME, d1.REG_DATE AS ACTIVE_REC_DATE, d2.REG_DATE AS NOTACTIVE_REC_DATE, d1.ACTIVE, d2.ACTIVE FROM [dbo].[test] as d1
LEFT JOIN CTE d2 ON d2.ATC = d1.ATC AND d2.PID = d1.PID
AND DATEDIFF(DAY, d2.REG_DATE, d1.REG_DATE) <= 7
WHERE d1.ACTIVE=1 AND d1.PID=123456;
Wanted result:
The record with column ACTIVE True (1) should contain (if the record exists) the REG_DATE of the not active previous record max 7 days old. Like:
(No column name) NAME ACTIVE_REC_DATE NOTACTIVE_REC_DATE ACTIVE ACTIVE
3 TEST5 2016-09-06 00:00:00.000 2016-08-31 00:00:00.000 1 0
Currently the query result contains multiple records because the are more records that will fall in the 7 days period. I need to join 1 record that will be the max 7 day old one.
I've used the ROW_NUMBER() with over partition by so I can use and identify the first record because I will be sorting ascending. This doesn't work when there is no previous records available or the previous records are older then 7 days
When there is no records to join is ignored can use INNER JOIN or date columns at null
I hope I'm clear with my explanation.
SELECT DATEDIFF(DAY, d2.REG_DATE, d1.REG_DATE),d1.NAME, d1.REG_DATE AS ACTIVE_REC_DATE, d2.REG_DATE AS NOTACTIVE_REC_DATE, d1.ACTIVE, d2.ACTIVE FROM [dbo].[test] as d1
OUTER APPLY
(SELECT TOP 1 T1.* FROM dbo.test t1 WHERE t1.ATC = d1.ATC AND t1.PID = d1.PID and DATEDIFF(DAY, t1.REG_DATE, d1.REG_DATE) <= 7 order by t1.REG_DATE desc) d2
WHERE d1.ACTIVE=1 AND d1.PID=123456;
You can return a single row for a PID by adding a filter on ROWNUM to your left join:
;WITH CTE AS
(
SELECT ROW_NUMBER() OVER (PARTITION BY ATC, PID ORDER BY REG_DATE ASC) AS ROWNUM,* FROM [dbo].[test]
WHERE ACTIVE=0
)
SELECT DATEDIFF(DAY, d2.REG_DATE, d1.REG_DATE),d1.NAME, d1.REG_DATE AS ACTIVE_REC_DATE, d2.REG_DATE AS NOTACTIVE_REC_DATE, d1.ACTIVE, d2.ACTIVE FROM [dbo].[test] as d1
LEFT JOIN CTE d2 ON d2.ATC = d1.ATC AND d2.PID = d1.PID
AND DATEDIFF(DAY, d2.REG_DATE, d1.REG_DATE) <= 7
AND d2.ROWNUM = 1
WHERE d1.ACTIVE=1 AND d1.PID=123456;
It's not clear from the question how the query should behave when no previous records from the last seven days exist.

Getting the last row from a ROW_NUMBER using SQL

I am thinking there is a better way to grab the last row from a row_number instead of doing multiple nesting using T-SQL.
I need the total number of orders and the last ordered date. Say I have the following:
DECLARE #T TABLE (PERSON_ID INT, ORDER_DATE DATE)
INSERT INTO #T VALUES(1, '2016/01/01')
INSERT INTO #T VALUES(1, '2016/01/02')
INSERT INTO #T VALUES(1, '2016/01/03')
INSERT INTO #T VALUES(2, '2016/01/01')
INSERT INTO #T VALUES(2, '2016/01/02')
INSERT INTO #T VALUES(3, '2016/01/01')
INSERT INTO #T VALUES(3, '2016/01/02')
INSERT INTO #T VALUES(3, '2016/01/03')
INSERT INTO #T VALUES(3, '2016/01/04')
What I want is:
PERSON_ID ORDER_DATE ORDER_CNT
1 2016-01-03 3
2 2016-01-02 2
3 2016-01-04 4
Is there a better way to do this besides the following:
SELECT *
FROM (
SELECT *
, ROW_NUMBER() OVER (PARTITION BY PERSON_ID ORDER BY ORDER_CNT DESC) AS LAST_ROW
FROM (
SELECT *
, ROW_NUMBER () OVER (PARTITION BY PERSON_ID ORDER BY ORDER_DATE) AS ORDER_CNT
FROM #T
) AS A
) AS B
WHERE LAST_ROW = 1
Yes, you can use this:
SELECT
PERSON_ID,
MAX(ORDER_DATE) AS ORDER_DATE,
COUNT(*) AS ORDER_CNT
FROM #T
GROUP BY PERSON_ID
SELECT a.PERSON_ID
, a.ORDER_DATE
, a.ORDER_CNT
FROM
(
SELECT PERSON_ID
, ORDER_DATE
, rn = ROW_NUMBER () OVER (PARTITION BY PERSON_ID ORDER BY ORDER_DATE DESC)
, ORDER_CNT = COUNT(ORDER_DATE) OVER (PARTITION BY PERSON_ID)
FROM #T
) AS a
WHERE rn = 1
ORDER BY a.PERSON_ID;

SQL - Filter on dates X number of days apart from the previous

I have a table containing orders. I would like to select those orders that are a certain number of days apart for a specific client. For example, in the table below I would like to select all of the orders for CustomerID = 10 that are at least 30 days apart from the previous instance. With the starting point to be the first occurrence (07/05/2014 in this data).
OrderID | CustomerID | OrderDate
==========================================
1 10 07/05/2014
2 10 07/15/2014
3 11 07/20/2014
4 11 08/20/2014
5 11 09/21/2014
6 10 09/23/2014
7 10 10/15/2014
8 10 10/30/2014
I would want to select OrderIDs (1,6,8) since they are 30 days apart from each other and all from CustomerID = 10. OrderIDs 2 and 7 would not be included as they are within 30 days of the previous order for that customer.
What confuses me is how to set the "checkpoint" to the last valid date. Here is a little "pseudo" SQL.
SELECT OrderID
FROM Orders
WHERE CusomerID = 10
AND OrderDate > LastValidOrderDate + 30
i came here and i saw #SveinFidjestøl already posted answer but i can't control my self after by long tried :
with the help of LAG and LEAD we can comparison between same column
and as per your Q you are looking 1,6,8. might be this is helpful
SQL SERVER 2012 and after
declare #temp table
(orderid int,
customerid int,
orderDate date
);
insert into #temp values (1, 10, '07/05/2014')
insert into #temp values (2, 10, '07/15/2014')
insert into #temp values (3, 11, '07/20/2014')
insert into #temp values (4, 11, '08/20/2014')
insert into #temp values (5, 11, '09/21/2014')
insert into #temp values (6, 10, '09/23/2014')
insert into #temp values (7, 10, '10/15/2014')
insert into #temp values (8, 10, '10/30/2014');
with cte as
(SELECT orderid,customerid,orderDate,
LAG(orderDate) OVER (ORDER BY orderid ) PreviousValue,
LEAD(orderDate) OVER (ORDER BY orderid) NextValue,
rownum = ROW_NUMBER() OVER (ORDER BY orderid)
FROM #temp
WHERE customerid = 10)
select orderid,customerid,orderDate from cte
where DATEDIFF ( day , PreviousValue , orderDate) > 30
or PreviousValue is null or NextValue is null
SQL SERVER 2005 and after
WITH CTE AS (
SELECT
rownum = ROW_NUMBER() OVER (ORDER BY p.orderid),
p.orderid,
p.customerid,
p.orderDate
FROM #temp p
where p.customerid = 10)
SELECT CTE.orderid,CTE.customerid,CTE.orderDate,
prev.orderDate PreviousValue,
nex.orderDate NextValue
FROM CTE
LEFT JOIN CTE prev ON prev.rownum = CTE.rownum - 1
LEFT JOIN CTE nex ON nex.rownum = CTE.rownum + 1
where CTE.customerid = 10
and
DATEDIFF ( day , prev.orderDate , CTE.orderDate) > 30
or prev.orderDate is null or nex.orderDate is null
GO
You can use the LAG() function, available in SQL Server 2012, together with a Common Table Expression. You calculate the days between the customer's current order and the customer's previous order and then query the Common Table Expression using the filter >= 30
with cte as
(select OrderId
,CustomerId
,datediff(d
,lag(orderdate) over (partition by CustomerId order by OrderDate)
,OrderDate) DaysSinceLastOrder
from Orders)
select OrderId, CustomerId, DaysSinceLastOrder
from cte
where DaysSinceLastOrder >= 30 or DaysSinceLastOrder is null
Results:
OrderId CustomerId DaysSinceLastOrder
1 10 NULL
6 10 70
3 11 NULL
4 11 31
5 11 32
(Note that 1970-01-01 is chosen arbitrarily, you may choose any date)
Update
A slighty more reliable way of doing it will involve a temporary table. But the original table tbl can be left unchanged. See here:
CREATE TABLE #tmp (id int); -- set-up temp table
INSERT INTO #tmp VALUES (1); -- plant "seed": first oid
WHILE (##ROWCOUNT>0)
INSERT INTO #tmp (id)
SELECT TOP 1 OrderId FROM tbl
WHERE OrderId>0 AND CustomerId=10
AND OrderDate>(SELECT max(OrderDate)+30 FROM tbl INNER JOIN #tmp ON id=OrderId)
ORDER BY OrderDate;
-- now list all found entries of tbl:
SELECT * FROM tbl WHERE EXISTS (SELECT 1 FROM #tmp WHERE id=OrderId)
#tinka shows how to use CTEs to do the trick, and the new windowed functions (for 2012 and later) are probably the best answer. There is also the option, assuming you do not have a very large data set, to use a recursive CTE.
Example:
declare #customerid int = 10;
declare #temp table
(orderid int,
customerid int,
orderDate date
);
insert into #temp values (1, 10, '07/05/2014')
insert into #temp values (2, 10, '07/15/2014')
insert into #temp values (3, 11, '07/20/2014')
insert into #temp values (4, 11, '08/20/2014')
insert into #temp values (5, 11, '09/21/2014')
insert into #temp values (6, 10, '09/23/2014')
insert into #temp values (7, 10, '10/15/2014')
insert into #temp values (8, 10, '10/30/2014');
with datefilter AS
(
SELECT row_number() OVER(PARTITION BY CustomerId ORDER BY OrderDate) as RowId,
OrderId,
CustomerId,
OrderDate,
DATEADD(day, 30, OrderDate) as FilterDate
from #temp
WHERE CustomerId = #customerid
)
, firstdate as
(
SELECT RowId, OrderId, CustomerId, OrderDate, FilterDate
FROM datefilter
WHERE rowId = 1
union all
SELECT datefilter.RowId, datefilter.OrderId, datefilter.CustomerId,
datefilter.OrderDate, datefilter.FilterDate
FROM datefilter
join firstdate
on datefilter.CustomerId = firstdate.CustomerId
and datefilter.OrderDate > firstdate.FilterDate
WHERE NOT EXISTS
(
SELECT 1 FROM datefilter betweens
WHERE betweens.CustomerId = firstdate.CustomerId
AND betweens.orderdate > firstdate.FilterDate
AND datefilter.orderdate > betweens.orderdate
)
)
SELECT * FROM firstdate

Resources