Understanding outstanding days with DATEDIFF, GETDATE - sql

Is possible for my script code to return outstanding days on one column per created_Date?
What I would like my code to do is, have the total days such as 90_days, 120_days, 180_days, 365_days, or, 1 year by the side of Created_Date.
My query lists all data correctly per column name, however, I would like to see only one column displaying total days outstanding per Created_Date not all separate days outstanding with different headed columns.
I have tried the following, and I get all the days separated on separate columns.
Example, this is what I meant:
______________________________________________________
Created_Date | Days Outstanding
2015-01-02 08:29:06 | 90-120
2015-01-02 08:35:44 | 90-120
2015-01-02 08:37:34 | 365
2015-01-02 09:07:01 | 120-180
2015-01-02 09:07:01 | 1 Year Plus
______________________________________________________
[Script Code:]
SELECT DISTINCT ge.Name,
ge.Entity_Type,
ge.Entity_Number,
bc.Super_Entity_ID,
ch.Check_Date, 'Date check was requested
ch.Created_Date, ' Created_Date was paid in full
ch.Check_Number,
ch.Amount,
vn.Vendor_Name,
DATEDIFF(day, [Created_Date], Getdate()) as " Number of Days ", ' Number
of Days Outstanding
'90_days' = CASE WHEN DATEDIFF(day, [Created_Date], Getdate()) Between 90
AND 120 Then [Amount] END,
'120_days' = CASE WHEN DATEDIFF(day, [Created_Date], Getdate()) Between 120
AND 180 Then [Amount] END,
'180_plus' = CASE WHEN DATEDIFF(day, [Created_Date], Getdate()) > 180 Then
[Amount] END
FROM
dbo.gl_entities AS ge ' All linked tables listed with "INNER JOIN"
INNER JOIN
dbo.super_entity AS se
ON ge.Super_Entity_ID = se.Super_Entity_ID
INNER JOIN
dbo.bank_codes AS bc
ON se.Super_Entity_ID = bc.Super_Entity_ID
INNER JOIN
dbo.checks AS ch
ON bc.Bank_Code_ID = ch.Bank_Code_ID
INNER JOIN
dbo.vendors AS vn
ON ch.Vendor_ID = vn.Vendor_ID
WHERE
DATEDIFF(Day, ch.Created_Date, GETDATE ()) > = 90 AND
ge.Active = 1 and vn.active = 1 and (ge.IS_Shadow = 1 OR se.IS_Tiered = 0)
AND CHECK_DATE > '20150101 00:00:00'
AND CHECK_DATE< '20190918 00:00:00'
ORDER BY ch.Check_Date, ch.Created_Date

Use a single CASE expression, like:
CASE
WHEN DATEDIFF(day, [Created_Date], Getdate()) Between 90 AND 119 Then '90 days'
WHEN DATEDIFF(day, [Created_Date], Getdate()) Between 120 AND 179 Then '90 days'
WHEN DATEDIFF(day, [Created_Date], Getdate()) >= 180 Then '180+ days'
END As [Days Outstanding]

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

Adjust the where statement based on the columns

I have a query like this, I need to adjust my where statement based on the columns, please look at the following query:
SELECT
b.id as [ID]
,d.[Title] as [Title]
,e.Class as [Class]
,Sum(a.col1) as [Last 30 Days Col1]
,Sum(a.col2) as [Last 30 Days Col2]
,Sum(a.col1) as [Last 90 Days Col1]
,Sum(a.col2) as [Last 90 Days Col2]
,Sum(a.col1) as [Last 365 Days Col1]
,Sum(a.col2) as [Last 365 Days Col2]
FROM
tb1 a
INNER JOIN
tb2 b -- ON anything
INNER JOIN
tb3 c -- ON anything
INNER JOIN
tb4 d -- ON anything
INNER JOIN
tb5 e -- ON anything
WHERE
a.DateCol >= DATEADD(MONTH, -1, GETDATE())
GROUP BY
b.id, d.Title, e.Class
What I am currently getting as the result is like this:
WHEN:
where a.DateCol >= DATEADD(MONTH,-1,GETDATE()) => Last 30 Days Col1 = Last 90 Days Col1 = Last 365 Days Col1 = 10
WHEN:
where a.DateCol >= DATEADD(QUARTER,-1,GETDATE()) => Last 30 Days Col1 = Last 90 Days Col1 = Last 365 Days Col1 = 100
WHEN:
where a.DateCol >= DATEADD(YEAR,-1,GETDATE()) => Last 30 Days Col1 = Last
90 Days Col1 = Last 365 Days Col1 = 1000
ID | Title | Class | Last 30 Days Col1 | Last 30 Days Col2 | Last 90 Days Col1 | Last 90 Days Col2 | Last 365 Days Col1 | Last 365 Days Col2
--------------------------------------------------------------------------------------------------------------------------------------
1 | T1 | C1 | 10 | 20 | 10 | 20 | 10 | 20
But as you guess, I need the correct value will be displayed per column, I don't know how can I change my query to achieve this, since I have a fixed where statement. Any thought on how can I adjust my where statement based on 30,90 and 365 days and show the correct values in correct columns accordingly?
First, where condition should filter all records from last year where a.DateCol >= DATEADD(YEAR, -1, GETDATE()) then in your Sum function, you put days diff to condition:
SELECT b.id AS [ID]
,d.[Title] AS [Title]
,e.Class AS [Class]
,Sum(WHERE datediff(day, a.DateCol, getdate()) <= 30 then a.Col1 ELSE 0 END) AS [Last 30 Days Col1]
,Sum(WHERE datediff(day, a.DateCol, getdate()) <= 30 then a.col2 ELSE 0 END) AS [Last 30 Days Col2]
,Sum(WHERE datediff(day, a.DateCol, getdate()) <= 90 then a.Col1 ELSE 0 END) AS [Last 90 Days Col1]
,Sum(WHERE datediff(day, a.DateCol, getdate()) <= 90 then a.col2 ELSE 0 END) AS [Last 90 Days Col2]
,Sum(WHERE datediff(day, a.DateCol, getdate()) <= 365 then a.Col1 ELSE 0 END) AS [Last 365 Days Col1]
,Sum(WHERE datediff(day, a.DateCol, getdate()) <= 356 then a.col2 ELSE 0 END) AS [Last 365 Days Col2]
FROM tb1 a
INNER JOIN tb2 b -- ON anything
INNER JOIN tb3 c -- ON anything
INNER JOIN tb4 d -- ON anything
INNER JOIN tb5 e -- ON anything
WHERE a.DateCol >= DATEADD(YEAR, - 1, GETDATE())
GROUP BY b.id
,d.Title
,e.Class

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;

Group By clause over a series of data SQL

I am in the process of writing a report and am having some trouble getting a series of data to return correctly (Data warehouse may be ideal, but not an option right now). Effectively, I have 2 tables I need to join and report on...
Transactions and Receipts. Transactions contain the amount billed and Receipts contain the amount paid. My report needs to show:
LastName | FirstName | Company | Location | Total | 30 | 60 | 90
---------------------------------------------------------------------
Tom | Clark | Microsoft | Washington | $300 | $80 | $100 | $120
Where 30,60,90 are buckets to show the amount owed 30 days ago, 60 days ago, etc. This is where I'm struggling. I can get the other values without issue. This is what I have thus far:
select
st.Client_Name_Last,
st.Client_Name_First,
st.Location_Company,
st.Location_Address_City,
sum((st.Billing_AmountPerUnitAllowed * st.Billing_NumberOfUnits) - coalesce(r.PaymentAmount, 0)) as Total,
(select sum((st.Billing_AmountPerUnitAllowed * st.Billing_NumberOfUnits) - coalesce(r.PaymentAmount, 0))
where DateDiff(day, st.service_date, #effectiveDate) > 0 and DateDiff(day, st.service_date, #effectiveDate) < 30) as '30',
(select sum((st.Billing_AmountPerUnitAllowed * st.Billing_NumberOfUnits) - coalesce(r.PaymentAmount, 0))
where DateDiff(day, st.service_date, #effectiveDate) >= 30 and DateDiff(day, st.service_date, #effectiveDate) < 60) as '60'
from
ServiceTransactions st
join Claims c on st.Claim_Id = c.Id
left outer join Receipts r on c.Id = r.ClaimId
group by
st.Client_Name_Last,
st.Client_Name_First,
st.Location_Company,
st.Location_Address_City
This of course doesn't work because the st.Service_Date is in the top level select statement, which causes an error because it's not in an aggregate or the group by clause. I've considered going with a Common Table Expression, but wasn't sure how to best utilize that. Any insight would be most appreciated.
Thanks for your time!
You want conditional aggregation. This puts the case inside the sum():
sum(case when DateDiff(day, st.service_date, #effectiveDate) > 0 and DateDiff(day, st.service_date, #effectiveDate) < 30)
then (st.Billing_AmountPerUnitAllowed * st.Billing_NumberOfUnits) -
coalesce(r.PaymentAmount, 0)
else 0
end) as days_30,
sum(case when DateDiff(day, st.service_date, #effectiveDate) >= 30 and DateDiff(day, st.service_date, #effectiveDate) < 60)
then (st.Billing_AmountPerUnitAllowed * st.Billing_NumberOfUnits) -
coalesce(r.PaymentAmount, 0)
else 0
end) as days_60

SQL Server : Gap / Island, datetime, contiguous block 365 day block

I have a table that looks like this:-
tblMeterReadings
id meter period_start period_end amount
1 1 2014-01-01 00:00 2014-01-01 00:29:59 100.3
2 1 2014-01-01 00:30 2014-01-01 00:59:59 50.5
3 1 2014-01-01 01:00 2014-01-01 01:29:59 70.7
4 1 2014-01-01 01:30 2014-01-01 01:59:59 900.1
5 1 2014-01-01 02:00 2014-01-01 02:29:59 400.0
6 1 2014-01-01 02:30 2014-01-01 02:59:59 200.3
7 1 2014-01-01 03:00 2014-01-01 03:29:59 100.8
8 1 2014-01-01 03:30 2014-01-01 03:59:59 140.3
This is a tiny "contiguous block" from '2014-01-01 00:00' to '2014-01-01 3:59:59'.
In the real table there are "contiguous blocks" of years in length.
I need to find the the period_start and period_end of the most recent CONTINUOUS 365 COMPLETE DAYs (fileterd by meter column).
When I say COMPLETE DAYs I mean a day that has entries spanning 00:00 to 23:59.
When I say CONTINUOUS I mean there must be no days missing.
I would like to select all the rows that make up this block of CONTINUOUS COMPLETE DAYs.
I also need an output like:
block_start block_end total_amount_for_block
2013-02-26 00:00 2014-02-26 23:59:59 1034234.5
This is beyond me, so if someone can solve... I will be very impressed.
Since your granularity is 1 second, you need to expand your periods into all the date/times between the start and end at 1 second intervals. To do this you need to cross join with a numbers table (The numbers table is generated on the fly by ranking object ids from an arbitrary system view, I have limited it to TOP 86400 since this is the number of seconds in a day, and you have stated your time periods never span more than one day):
WITH Numbers AS
( SELECT TOP (86400)
Number = ROW_NUMBER() OVER(ORDER BY a.object_id) - 1
FROM sys.all_objects a
CROSS JOIN sys.all_objects b
ORDER BY a.object_id
)
SELECT r.ID, r.meter, dt.[DateTime]
FROM tblMeterReadings r
CROSS JOIN Numbers n
OUTER APPLY
( SELECT [DateTime] = DATEADD(SECOND, n.Number, r.period_start)
) dt
WHERE dt.[DateTime] <= r.Period_End;
You then have your continuous range in which to perform the normal gaps and islands grouping:
WITH Numbers AS
( SELECT TOP (86400)
Number = ROW_NUMBER() OVER(ORDER BY a.object_id) - 1
FROM sys.all_objects a
CROSS JOIN sys.all_objects b
ORDER BY a.object_id
), Grouped AS
( SELECT r.meter,
Amount = CASE WHEN Number = 1 THEN r.Amount ELSE 0 END,
dt.[DateTime],
GroupingSet = DATEADD(SECOND,
-DENSE_RANK() OVER(PARTITION BY r.Meter
ORDER BY dt.[DateTime]),
dt.[DateTime])
FROM tblMeterReadings r
CROSS JOIN Numbers n
OUTER APPLY
( SELECT [DateTime] = DATEADD(SECOND, n.Number, r.period_start)
) dt
WHERE dt.[DateTime] <= r.Period_End
)
SELECT meter,
PeriodStart = MIN([DateTime]),
PeriodEnd = MAX([DateTime]),
Amount = SUM(Amount)
FROM Grouped
GROUP BY meter, GroupingSet
HAVING DATEADD(YEAR, 1, MIN([DateTime])) < MAX([DateTime]);
N.B. Since the join to Number causes amounts to be duplicated, it is necessary to set all duplicates to 0 using CASE WHEN Number = 1 THEN r.Amount ELSE 0 END, i.e only include the amount for the first row for each ID
Removing the Having clause for your sample data will give:
meter | PeriodStart | PeriodEnd | Amount
------+---------------------+---------------------+----------
1 | 2014-01-01 00:00:00 | 2014-01-01 03:59:59 | 1963
Example on SQL Fiddle
You could try this:
Select MIN(period_start) as "block start"
, MAX(period_end) as "block end"
, SUM(amount) as "total amount"
FROM YourTable
GROUP BY datepart(year, period_start)
, datepart(month, period_start)
, datepart(day, period_start)
, datepart(year, period_end)
, datepart(month, period_end)
, datepart(day, period_end)
Having datepart(year, period_start) = datepart(year, period_end)
AND datepart(month, period_start) = datepart(month, period_end)
AND datepart(day, period_start) = datepart(day, period_end)
AND datepart(hour, MIN(period_start)) = 0
AND datepart(minute,MIN(period_start)) = 0
AND datepart(hour, MAX(period_end)) = 23
AND datepart(minute,MIN(period_end)) = 59