Display data in Days range - sql

I have a sql query output as below
Customer LastModifiedDate
A 1/12/2013
B 1/1/2015
C 1/28/2015
Now I need to display the customers count whose details updated in different days range like (30-60 days), (61-90 Days) and 90+(More than 90 days)
For example please see the output below
DaysRange CustomersCount
30-60 1
61-90 1
90+ 1
Please help me in achieving the above output

Something like this.
SELECT DaysRange=CASE
WHEN Datediff(DAY, LastModifiedDate, Getdate()) BETWEEN 30 AND 60 THEN '30-60'
WHEN Datediff(DAY, LastModifiedDate, Getdate()) BETWEEN 61 AND 90 THEN '61-90'
WHEN Datediff(DAY, LastModifiedDate, Getdate()) > 90 THEN '90+'
END,
CustomersCount=Count(1)
FROM yourtable
GROUP BY CASE
WHEN Datediff(DAY, LastModifiedDate, Getdate()) BETWEEN 30 AND 60 THEN '30-60'
WHEN Datediff(DAY, LastModifiedDate, Getdate()) BETWEEN 61 AND 90 THEN '61-90'
WHEN Datediff(DAY, LastModifiedDate, Getdate()) > 90 THEN '90+'
END

Using this query:
SELECT COUNT(CASE WHEN DATEDIFF(d, LastModifiedDate, getdate()) BETWEEN 30 AND 60 THEN 1 END) AS [30-60],
COUNT(CASE WHEN DATEDIFF(d, LastModifiedDate, getdate()) BETWEEN 61 AND 90 THEN 1 end) AS [61-90],
COUNT(CASE WHEN DATEDIFF(d, LastModifiedDate, getdate()) > 90 THEN 1 END) AS [90+]
FROM mytable
produces the following output:
30-60 61-90 90+
1 1 1
With UNPIVOT we get the desired result set:
SELECT DaysRange, CustomersCount
FROM (
SELECT COUNT(CASE WHEN DATEDIFF(d, LastModifiedDate, getdate()) BETWEEN 30 AND 60 THEN 1 END) AS [30-60],
COUNT(CASE WHEN DATEDIFF(d, LastModifiedDate, getdate()) BETWEEN 61 AND 90 THEN 1 END) AS [61-90],
COUNT(CASE WHEN DATEDIFF(d, LastModifiedDate, getdate()) > 90 THEN 1 END) AS [90+]
FROM mytable) p
UNPIVOT
(CustomersCount FOR DaysRange IN ([30-60], [61-90], [90+])
) AS unpvt;
Output:
DaysRange CustomersCount
30-60 1
61-90 1
90+ 1

Related

T-SQL pivot inventory aging by day range

Writing a T-SQL statement to display items in inventory broken out by day range (pivot).
For example from this inventory table:
ItemName
DateCreated
PO_ID
A
2020-10-07
0
B
2020-10-07
1
A
2020-10-22
2
A
2020-10-22
2
A
2020-10-22
2
B
2020-10-29
3
Would like to generate the bellow results (typically a pivot table), showing the number of pieces per ItemName per day range. The date used to calculate the # of days since DateCreated would be the day the report was ran or passed in as a parameter - in the example shown here, the date used is from '2020-11-07':
ItemName
0-10 days
11-20 days
21-30 days
>30 days
A
0
3
0
1
B
1
0
0
1
Not sure what would be the best way to write the statement to generate the above results?
I would use conditional aggregation:
select itemName,
sum(case when datediff(day, dateCreated, getdate()) <= 10 then 1 else 0 end) as days_0_10,
sum(case when datediff(day, dateCreated, getdate()) > 10 and
datediff(day, dateCreated, getdate()) <= 20
then 1 else 0 end) as days_11_20,
sum(case when datediff(day, dateCreated, getdate()) > 20 and
datediff(day, dateCreated, getdate()) <= 30
then 1 else 0 end) as days_21_30,
sum(case when datediff(day, dateCreated, getdate()) > 30 then 1 else 0 end) as days_31
from t
group by itemName
I would use something similar to the following query SQL Server:
SELECT *
FROM
(
SELECT [ItemName],[PO_ID],
CASE
WHEN DATEDIFF(day, DateCreated, getdate()) BETWEEN 0 AND 10 THEN '0-10 days'
WHEN DATEDIFF(day, DateCreated, getdate()) BETWEEN 11 AND 20 THEN '11-20 days'
WHEN DATEDIFF(day, DateCreated, getdate()) BETWEEN 21 AND 30 THEN '21-30 days'
ELSE '>30 days'
END AS PeriodCreated
FROM [TableName])
) src
pivot
(
COUNT(PO_ID)
FOR PeriodCreated in ([0-10 days], [11-20 days], [>30 days])
) piv

Trying to return a Count for three numbered sequenses,

These are loans that have "Matured". I need a Count for loans that are 25 days after their Maturity Date, 45 days, and the Rest. I need to assign a Label for each so I can create an iDashboard Chart.
I'm using a Subquery but I believe the data I need is located in ONE table.
----------code-------------------
Select z.Status, Count(z.Status)
From (Select a.Account, a.MaturityDate
Case
When datediff(dd, getdate(),[MaturityDate]) between -44 and -25 Then 'Yellow - 25 Days'
When datediff(dd, getdate(),[MaturityDate]) <= -45 Then 'RED - 45 Days'
Else 'All Good'
End As Status
From (Select * From LNSLoan a ))
Group by z.Status
z.Status Count
Yellow - 25 Days 128
RED - 45 Days 56
Rest of data 1138
You seem to want something like this:
select v.status, count(*)
from LNSLoan l cross apply
(values (case when datediff(day, getdate(), l.MaturityDate) between -44 and -25
then 'Yellow - 25 Days'
when datediff(day, getdate(), l.MaturityDate) <= -45
then 'RED - 45 Days'
else 'All Good'
end)
) v(status)
group by v.status;

Have non-boolean result in Case When Function

I am trying to create a query that will break results into separate columns. The best formula that I can find is the Case When function, but it says the Then part of the equation must be Boolean (or a true/false result). Is there a way for the Then to calculate a number 3-1 for example?
Case
when
DATEDIFF(day, T0.[DocDueDate], getdate()) > 0
AND DATEDIFF(day, T0.[DocDueDate], getdate()) < 30
then
(T0.[DocTotal] - T0.[PaidToDate])
else
' '
end
as "Greater than 1",
Case
when
DATEDIFF(day, T0.[DocDueDate], getdate()) > 30
AND DATEDIFF(day, T0.[DocDueDate], getdate()) < 60
then
(T0.[DocTotal] - T0.[PaidToDate])
else
' '
end
as "Greater than 30"
You have a problem with type compatibility. I would recommend that you simply use NULL for no match:
(case when DATEDIFF(day, T0.[DocDueDate], getdate()) > 0 AND DATEDIFF(day, T0.[DocDueDate], getdate()) < 30
then (T0.[DocTotal] - T0.[PaidToDate])
end) as Greater_than_1,
(case when DATEDIFF(day, T0.[DocDueDate], getdate()) > 30 and DATEDIFF(day, T0.[DocDueDate], getdate()) < 60
then (T0.[DocTotal] - T0.[PaidToDate])
end) as Greater_than_30
I would also guess that you intend <= 30 for the first condition.
If you use sql server then your code snip will be like below.
you have to keep same data type on after then and else as you used string type after using else so you have to convert it on later then
Case
when
DATEDIFF(day, T0.[DocDueDate], getdate()) > 0
AND DATEDIFF(day, T0.[DocDueDate], getdate()) < 30
then
( convert(varchar(255), T0.[DocTotal] - T0.[PaidToDate]))
else
' '
end
as Greater_than_1,
Case
when
DATEDIFF(day, T0.[DocDueDate], getdate()) > 30
AND DATEDIFF(day, T0.[DocDueDate], getdate()) < 60
then
(convert(varchar(255), T0.[DocTotal] - T0.[PaidToDate]))
else
' '
end
as Greater_than_30
Found that i needed to cast the equation as an integer as below.
Case when
DATEDIFF(day, T0.[DocDueDate], getdate()) > 0
AND DATEDIFF(day, T0.[DocDueDate], getdate()) <30
then
cast( (T0.[DocTotal]-T0.[PaidToDate]) as varchar(12) )
else
' '
end as "Greater than 1"
Here is how you can do this with a variety of methods. TSET is just generating dates from June 1 to today for the test.
WITH tset(td)
AS (
SELECT CAST('2018-08-01' AS DATE) AS td
UNION ALL
SELECT DATEADD(d, -1, td)
FROM tset
WHERE td > CAST('2018-06-01' AS DATE))
-- Actual examples start here
SELECT td
-- Diff is just showing the difference in days so you can see the group assignements
, DATEDIFF(d, td, GETDATE()) AS diff
-- This column groups into < 30, 31-60, and > 60
, CASE
WHEN DATEDIFF(d, td, GETDATE()) < 30 THEN 1 ELSE CASE
WHEN DATEDIFF(d, td, GETDATE()) < 60 THEN 2 ELSE 3
END
END three_sets
-- this example will group into any number of 30 day sets.
, cast ( DATEDIFF(d, td, GETDATE()) as int) / 30 any_number_of_sets
FROM tset
ORDER BY td;
Some sample Results:
td diff three_sets any_number_of_sets
2018-06-01 66 3 2
2018-06-02 65 3 2
2018-06-03 64 3 2
. . .
2018-06-12 55 2 1
2018-06-13 54 2 1
2018-06-14 53 2 1
. . .
2018-07-09 28 1 0
2018-07-10 27 1 0
2018-07-11 26 1 0

Counting the number of returning item for a timeframe

Context:
For every item returned, we need to know how many time "this" item was return in different timeframe: 30,60,90,120,180,365 days.
An item is unique based on his Serial (Itm_Item_Serial).
Sample Data:
Complete sample with creation script, and expected result here*.
CREATE TABLE ItemReturn
(
[Itm_Id] int,
[Itm_Item_Serial] int,
[Itm_CDate] datetime
);
INSERT INTO ItemReturn ([Itm_Id], [Itm_Item_Serial], [Itm_CDate])
VALUES
(1, 1, '2016-10-02 02:00:00'),
(2, 1, '2016-09-03 02:00:00'),
(3, 1, '2016-11-03 01:00:00')
;
Expected result: for Itm_Item_Serial = 1
Itm_Id 30d 60d 90d 120d 180d 365d
1 0 0 0 0 0 0
2 1 0 0 0 0 0
3 1 1 0 0 0 0
0 or null if there is no return in this time frame.
How does it work: for Itm_Item_Serial = 1
[Itm_Id] [Itm_Item_Serial] [Itm_CDate]
1, 1, '2016-10-02 02:00:00'
2, 1, '2016-09-03 02:00:00'
3, 1, '2016-11-03 01:00:00'
For [Itm_Id]=1, there is 0 previous return.
For [Itm_Id]=2, there is 1 previous return
on '2016-10-02'. datediff = 29. So there is one return in the timeframe "0-30 Days".
For [Itm_Id]=3, there is 2 previous return.
on '2016-09-03'. datediff = 60. So there is one return in the timeframe "30-60 Days".
on '2016-10-02'. datediff = 31. So there is one return in the timeframe "30-60 Days".
*: rextester Data Sample, and ordered Data Sample.
SQL Fiddle: http://sqlfiddle.com/#!6/00460/4
Does not give you the result you provided but does provide the counts for the various day lapses
; with data_CTE as (
select
m1.Itm_CDate
, DATEDIFF(day, m1.Itm_CDate, m2.Itm_CDate) DayDiff
, m1.Itm_id
, m1.Itm_Item_Serial
from ItemReturn m1
left outer join ItemReturn m2
on m1.Itm_Item_Serial = m2.Itm_Item_Serial
where m1.Itm_CDate < m2.Itm_CDate
and m1.Itm_CDate > DATEADD(day, -30, m2.Itm_CDate)
), aggr_CTE as (
select
Itm_Id, Itm_Item_Serial
, case
when DayDiff < 30 then '30d'
when DayDiff < 60 then '60d'
when DayDiff < 90 then '90d'
when DayDiff < 120 then '120d'
when DayDiff < 180 then '180d'
else '365d'
end DayLapse
from data_CTE
)select
Itm_id, [30d], [60d], [90d], [120d], [180d], [365d]
from (
select Itm_id, Itm_Item_Serial, DayLapse
from aggr_CTE
) src PIVOT (
count(Itm_Item_Serial)
for DayLapse in ([30d], [60d], [90d], [120d], [180d], [365d])
) as PivotTable
data_CTE Result (Output):
Itm_CDate DayDiff Itm_id Itm_Item_Serial
----------------------- ----------- ----------- ---------------
2016-09-03 02:00:00.000 29 2 1
2016-09-03 02:00:00.000 29 3 1
2016-09-03 02:00:00.000 29 5 2
2016-09-03 02:00:00.000 29 13 6
2016-08-05 02:00:00.000 29 15 6
2016-08-04 02:00:00.000 1 14 6
Update: 2017-07-26
Final Version of the Query
; with data_CTE as (
SELECT
Itm_Id
, Itm_Item_Serial
, ROW_NUMBER() over (Partition By Itm_Item_Serial order by Itm_Item_Serial) Itm_Item_RowNum
, Itm_CDate
FROM [ItemReturn] ir1
), date_CTE as (
select
d2.Itm_Id
, d1.Itm_Item_Serial
, DATEDIFF(day, d1.Itm_CDate, d2.Itm_CDate) DayDiff
from data_CTE d1, data_CTE d2
where d1.Itm_Item_Serial = d2.Itm_Item_Serial
and DATEDIFF(day, d1.Itm_CDate, d2.Itm_CDate) >= 0
), aggr_CTE as (
select
Itm_Id, Itm_Item_Serial
, case
when DayDiff <= 0 then '0d'
when DayDiff <= 30 then '30d'
when DayDiff <= 60 then '60d'
when DayDiff <= 90 then '90d'
when DayDiff <= 120 then '120d'
when DayDiff <= 180 then '180d'
else '365d'
end DayLapse
from date_CTE
)select
Itm_id, [30d], [60d], [90d], [120d], [180d], [365d]
from (
select Itm_id, Itm_Item_Serial, DayLapse
from aggr_CTE
) src PIVOT (
count(Itm_Item_Serial)
for DayLapse in ([0d], [30d], [60d], [90d], [120d], [180d], [365d])
) as PivotTable
You can query as below:
Select * from (
Select Itm_id, Itm_Item_Serial, case when datediff(day, Itm_CDate, getdate()) between 0 and 30 then '30'
when datediff(day, Itm_CDate, getdate()) between 30 and 60 then '60'
when datediff(day, Itm_CDate, getdate()) between 60 and 90 then '90'
when datediff(day, Itm_CDate, getdate()) between 90 and 120 then '120'
when datediff(day, Itm_CDate, getdate()) between 120 and 180 then '180'
when datediff(day, Itm_CDate, getdate()) between 180 and 360 then '365'
Else 0 End as test
from ItemReturn
) a
pivot (count(test) for test in ([30],[60],[90],[120],[180],[365])) p
If your difference date is between next date then you might need to use lead as below:
;With cte as (
Select *, NextDate = lead(itm_cDate) over(partition by Itm_Item_Serial order by Itm_id)
from ItemReturn
), cte2 as (
Select Itm_id, Itm_Item_Serial, case when datediff(day, Itm_CDate, NextDate) between 0 and 30 then '30'
when datediff(day, Itm_CDate, NextDate) between 30 and 60 then '60'
when datediff(day, Itm_CDate, NextDate) between 60 and 90 then '90'
when datediff(day, Itm_CDate, NextDate) between 90 and 120 then '120'
when datediff(day, Itm_CDate, NextDate) between 120 and 180 then '180'
when datediff(day, Itm_CDate, NextDate) between 180 and 360 then '365'
Else 0 End as test
from cte
)
Select * from cte2
pivot (count(test) for test in ([30],[60],[90],[120],[180],[365])) p
Demo here
not the final answer but slick way to get the difference
with cte as
select [Itm_Id], [Itm_Item_Serial], [Itm_CDate]
, row_number() over (partition by [Itm_Item_Serial] order by [Itm_CDate]) as rn
from ItemReturn;
select t1.*
, datediff(dd, t1.[Itm_CDate], t2.[Itm_CDate] desc) as diff
, t2.[Itm_Id], t2.[Itm_CDate]
from cte t1
join cte t2
on t1.[Itm_Item_Serial] = t2.[Itm_Item_Serial]
and t1.rn = 1
and t2.rn <> 1
order by t1.[Itm_Item_Serial], t1.rn

Using a sub query in column list

I would like to create a query that would count how many records were created in the last 7, 14 and 28 days. My result would return something like:
7Days 14Days 28Days
21 35 56
I know how to for each timepsan e.g. 7 days, but I do I capture all three in one query?
select count(*) from Mytable
where Created > DATEADD(day,-8, getdate())
Also not pretty, but doesn't rely on subqueries (table/column names are from AdventureWorks). The case statement returns 1 if it falls within your criteria, 0 otherwise - then you just sum the results :
select sum(case when datediff(day, modifieddate, getdate()) <= 7
then 1 else 0 end) as '7days',
sum(case when datediff(day, modifieddate, getdate()) > 7
and datediff(day, modifieddate, getdate()) <= 14
then 1 else 0 end) as '14days',
sum(case when datediff(day, modifieddate, getdate()) > 14
and datediff(day, modifieddate, getdate()) <= 28
then 1 else 0 end) as '28days'
from sales.salesorderdetail
Edit: Updated the datediff function - the way it was written, it would return a negative number (assuming modifieddate was in the past) causing all items to fall under the first case. Thanks to Andriy M for pointing that out
It isn't the prettiest code in the world, but it does the trick. Try selecting from three subqueries, one for each range.
select * from
(select COUNT(*) as Cnt from Log_UrlRewrites where CreateDate >= DATEADD(day, -7, getdate())) as Seven
inner join (select COUNT(*) as Cnt from Log_UrlRewrites where CreateDate >= DATEADD(day, -14, getdate())) as fourteen on 1 = 1
inner join (select COUNT(*) as Cnt from Log_UrlRewrites where CreateDate >= DATEADD(day, -28, getdate())) as twentyeight on 1 = 1
select
(
select count(*)
from Mytable
where Created > DATEADD(day,-8, getdate())
) as [7Days],
(
select count(*)
from Mytable
where Created > DATEADD(day,-15, getdate())
) as [14Days],
(
select count(*)
from Mytable
where Created > DATEADD(day,-29, getdate())
) as [28Days]
SELECT
[7Days] = COUNT(CASE UtmostRange WHEN 7 THEN 1 END),
[14Days] = COUNT(CASE UtmostRange WHEN 14 THEN 1 END),
[28Days] = COUNT(CASE UtmostRange WHEN 28 THEN 1 END)
FROM (
SELECT
*,
UtmostRange = CASE
WHEN Created > DATEADD(day, -8, GETDATE()) THEN 7
WHEN Created > DATEADD(day, -15, GETDATE()) THEN 14
WHEN Created > DATEADD(day, -29, GETDATE()) THEN 28
END
FROM Mytable
) s