Adjust the where statement based on the columns - sql

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

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

Understanding outstanding days with DATEDIFF, GETDATE

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]

SQL - How to count records for each status in one line per day?

I have a table Sales
Sales
--------
id
FormUpdated
TrackingStatus
There are several status e.g. Complete, Incomplete, SaveforLater, ViewRates etc.
I want to have my results in this form for the last 8 days(including today).
Expected Result:
Date Part of FormUpdated, Day of Week, Counts of ViewRates, Counts of Sales(complete), Counts of SaveForLater
--------------------------------------
2015-05-19 Tuesday 3 1 21
2015-05-18 Monday 12 5 10
2015-05-17 Sunday 6 1 8
2015-05-16 Saturday 5 3 7
2015-05-15 Friday 67 5 32
2015-05-14 Thursday 17 0 5
2015-05-13 Wednesday 22 0 9
2015-05-12 Tuesday 19 2 6
Here is my sql query:
select datename(dw, FormUpdated), count(ID), TrackingStatus
from Sales
where FormUpdated <= GETDATE()
AND FormUpdated >= GetDate() - 8
group by datename(dw, FormUpdated), TrackingStatus
order by datename(dw, FormUpdated) desc
I do not know how to make the next step.
Update
I forgot to mention, I only need the Date part of the FormUpdated, not all parts.
You can use SUM(CASE WHEN TrackingStatus = 'SomeTrackingStatus' THEN 1 ELSE 0 END)) to get the status count for each tracking status in individual column. Something like this. SQL Fiddle
select
CONVERT(DATE,FormUpdated) FormUpdated,
DATENAME(dw, CONVERT(DATE,FormUpdated)),
SUM(CASE WHEN TrackingStatus = 'ViewRates' THEN 1 ELSE 0 END) c_ViewRates,
SUM(CASE WHEN TrackingStatus = 'Complete' THEN 1 ELSE 0 END) c_Complete,
SUM(CASE WHEN TrackingStatus = 'SaveforLater' THEN 1 ELSE 0 END) c_SaveforLater
from Sales
where FormUpdated <= GETDATE()
AND FormUpdated >= DATEADD(D,-8,GetDate())
group by CONVERT(DATE,FormUpdated)
order by CONVERT(DATE,FormUpdated) desc
You can also use a PIVOT to achieve this result - you'll just need to complete the list of TrackingStatus names in both the SELECT and the FOR, and no GROUP BY required:
WITH DatesOnly AS
(
SELECT Id, CAST(FormUpdated AS DATE) AS DateOnly, DATENAME(dw, FormUpdated) AS DayOfWeek, TrackingStatus
FROM Sales
)
SELECT DateOnly, DayOfWeek,
-- List of Pivoted Columns
[Complete],[Incomplete], [ViewRates], [SaveforLater]
FROM DatesOnly
PIVOT
(
COUNT(Id)
-- List of Pivoted columns
FOR TrackingStatus IN([Complete],[Incomplete], [ViewRates], [SaveforLater])
) pvt
WHERE DateOnly <= GETDATE() AND DateOnly >= GetDate() - 8
ORDER BY DateOnly DESC
SqlFiddle
Also, I think your ORDER BY is wrong - it should just be the Date, not day of week.

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

Fetch data in MS SQL 2008

I have three tables which are like:
table1
id,
created_Date
table2
id
district_ID
status_ID
table3
district_ID
district_Name
Now i need the records in following format
Srno District_name <10 days >10 and <20 days >20 days
1 xxx 12 15 20
2 yyy 8 0 2
count days as per current date
for example: if the created date is 10-08-2013 and current date is 13-08-2013 the date difference will be 3
So what should my query be? Any suggestions will be appreciated.
Thank you
table1
id created_Date
1 2013-07-12 13:32:10.957
2 2013-07-12 13:32:10.957
3 2013-08-01 10:00:10.957
4 2013-08-10 13:32:10.957
5 2013-08-10 14:32:10.957
table2
id district_ID status_id
1 1 3
2 2 3
3 2 7
4 3 4
5 4 3
table1
district_ID district_Name
1 xxx
2 yyy
3 zzz
4 aaa
5 bbb
I would have a look at using DATEDIFF and CASE.
DATEDIFF (Transact-SQL)
Returns the count (signed integer) of the specified datepart
boundaries crossed between the specified startdate and enddate.
Something like
SELECT District_name,
SUM(
CASE
WHEN DATEDIFF(day,created_Date, getdate()) < 10
THEN 1
ELSE 0
END
) [<10 days],
SUM(
CASE
WHEN DATEDIFF(day,created_Date, getdate()) >= 10 AND DATEDIFF(day,created_Date, getdate()) < 20
THEN 1
ELSE 0
END
) [>10 and <20 days],
SUM(
CASE
WHEN DATEDIFF(day,created_Date, getdate()) >= 20
THEN 1
ELSE 0
END
) [>20 days]
FROM Your_Tables_Here
GROUP BY District_name
;with cte as (
select t3.district_Name, datediff(day, t1.created_Date, getdate()) as diff
from table1 as t1 as t1
inner join table2 as t2 on t2.id = t1.id
inner join table3 as t3 on t3.district_id = t2.district_id
)
select
district_Name,
sum(case when diff < 10 then 1 else 0 end) as [<10 days],
sum(case when diff >= 10 and diff < 20 then 1 else 0 end) as [>=10 and < 20 days],
sum(case when diff >= 20 then 1 else 0 end) as [>= 20 days]
from cte
group by district_Name