day wise and date range calculation - sql-server

I am using Sql Server 2012.
This is how I calculate the ratio of failures in an order:
31 Days Table 1 query
sum(CASE
WHEN (datediff(dd,serDATE,'2015-01-21')) >= 31 THEN 31
WHEN (datediff(dd,serDATE,'2015-01-21')) < 0 THEN 0
ELSE (datediff(dd,serDATE,'2015-01-21'))END) as 31days
1 . How do i loop and pass dates dynamically in the Datediff?
31 Failures Table 2 query
SUM(Case when sometable.FAILUREDATE BETWEEN dateadd(DAY,-31,CONVERT(DATETIME, '2015-01-21 23:59:00.0', 102))
AND CONVERT(DATETIME, '2015-01-21 23:59:00.0', 102)Then 1 Else 0 END) As Failures31,
31 Day Cal(Formula)
((365*(Convert(decimal (8,1),T2.Failures31)/T1.31day))) [31dayCal]
This works fine when done for a specific order.
I want a similar kind of calculation done for day wise and month wise.
2. what approach should I be using to achieve day wise and month wise calculation?
I do also have a table called Calender with the list of dates that i can use.
I would really appreciate any help regarding this..Thank you..

Solution
DECLARE #StartDate date = 'Jan 1, 2015'
DECLARE #EndDate date = DATEADD(DAY, 30, #StartDate)
;WITH cte AS (
SELECT #StartDate AS ReportDate
UNION ALL
SELECT DATEADD(DAY, 1, ReportDate)
FROM cte
WHERE ReportDate < #EndDate
)
Select T1.[date],T1.Fail31,T2.days31,
((365*(Convert(decimal (8,1),T1.Fail31)/T2.days31))) [31Fly]
from
(
SELECT cte.ReportDate as [date],
SUM(Case when HISTORY.FAILUREDATE BETWEEN dateadd(DAY,-31,CONVERT(DATETIME, cte.ReportDate, 102))
AND CONVERT(DATETIME, cte.ReportDate, 102)Then 1 Else 0 END) As Fail31
FROM HISTORY left JOIN UNIT ON HISTORY.UNIT = UNIT.UNIT
CROSS JOIN cte
WHERE
UNIT.INSV_DATE < cte.ReportDate
AND UNIT.MODEL in('Toyota')
AND(UNIT.Customer in('Jona' ))
group by
cte.ReportDate ) T1
Inner Join
(SELECT cte.ReportDate as [date1],
COUNT(UNIT.UNIT) As Units,
sum(CASE
WHEN (datediff(dd,INSV_DATE,cte.ReportDate)) >= 31 THEN 31
WHEN (datediff(dd,INSV_DATE,cte.ReportDate)) < 0 THEN 0
ELSE (datediff(dd,INSV_DATE,cte.ReportDate))END) as days31
FROM UNIT
CROSS JOIN cte
WHERE
UNIT.INSV_DATE < cte.ReportDate
AND UNIT.MODEL in('Toyota')
AND(UNIT.Customer in('Jona' ))
group by
cte.ReportDate
) T2 on T1.[date] = t2.[date1]
Order by [date]

Related

Adding the total number of distinct customers to a cte table in Microsoft SQL Server

I am using Microsoft SQL Server and am trying to achieve the following
Date
Distinct Customers last 30Days
2020-12-01
20000
2020-12-02
23000
What I am trying to get is that between 2020-11-01 and 2020-12-01 I had 20000 distinct customers.
I have created a cte table with the List of Dates as can be seen below:
WITH listdate AS
(
SELECT CAST('2020-11-01' AS datetime) DateValue
UNION ALL
SELECT DateValue + 1
FROM listdate
WHERE DateValue + 1 < getdate()
)
SELECT
cast(DateValue as date) as DateValue
FROM listdate d
Now I am trying to join the customer and usage table with the list of dates table, however, I am not getting the correct end result. The following is what I have tried doing:
WITH listdate AS
(
SELECT CAST('2020-11-01' AS datetime) DateValue
UNION ALL
SELECT DateValue + 1
FROM listdate
WHERE DateValue + 1 < getdate()
)
SELECT
cast(DateValue as date) as DateValue
,count(distinct case when m.CallDate between dateadd(dd,-30,cast(d.datevalue as date)) and cast(d.datevalue as date) then m.Customerid end) as Distinct_CID
FROM listdate d
join Usage m on d.DateValue=m.CallDate
left join Customer c on c.CustomerID=m.Customer
where c.customertype = 'type A'
group by d.DateValue
OPTION (MAXRECURSION 0)
Can someone maybe suggest a different way of how to solve such a query?
Thanks
I would go for a lateral join to bring the count of distinct customers for the last 30 days:
with listdate as (
select cast('20201101' as date) as datevalue
union all
select dateadd(day, 1, datevalue) from listdate where datevalue < cast(getdate() as date)
)
select ld.datevalue, x.cnt
from listdate ld
cross apply (
select count(distinct c.customerid) as cnt
from usage u
inner join customer c on c.customerid = u.customerid
where
c.customertype = 'type A'
and c.calldate >= dateadd(day, -29, datevalue)
and c.calldate < dateadd(day, 1, datevalue)
) x
option (maxrecursion 0)
Note that I simplified the parts related to dates: this uses proper literal dates and date arithmetics in the recursive query; the where clause of the subquery implements what I understand as the last 30 days (today + the preceding 29 days), and properly handles the time portion of calldate, if any.

SQL Server Query # of Events Per Day

Perhaps I am making this more complicated that it really is, hopefully someone can point me in the right direction. I get pretty close this this query:
SELECT
Action, TimeOccurred,
COUNT(Action)
FROM
[].[dbo].[]
WHERE
Action LIKE '%Logon Failed%'
AND (DATEDIFF(day, TimeOccurred, GETDATE()) BETWEEN 0 AND 30)
GROUP BY
Action, TimeOccurred
ORDER BY
TimeOccurred
My problem is TimeOccurred is formatted like this: 2017-05-13 00:02:00 so right now instead of giving me all the "logon failed" events per day, I get it per hour/min/second as well.
I would like to essentially cut the hh:mm:ss off so my results are per day. Hopefully that makes sense.
You can convert() to date to truncate the time portion of a datetime data type.
select
Action
, TimeOccurred = convert(date,TimeOccurred )
, Count(Action)
from [].[dbo].[]
where Action like '%Logon Failed%'
and TimeOccured >= dateadd(day,-30,dateadd(day, datediff(day, 0, getdate()), 0))
group by Action
, convert(date,TimeOccurred)
order by TimeOccurred
For your where, you can calculate the date for 30 days ago instead of getting a datediff() and restricting that range to 0-30.
For conditional aggregation you could do something like this:
select
TimeOccurred = convert(date, TimeOccurred)
, logon_kerberos = count (case when Action like ' %logon (kerberos)%' then 1 end)
, logon_local_wts = count (case when Action like ' %logon (local/wts)%' then 1 end)
, logon_ntlm = count (case when Action like ' %logon (ntlm)%' then 1 end)
, logon_total = count (case when Action like ' %logon (%' then 1 end)
, Count(Action)
from [CPTRAX_for_Windows].[dbo].[Logon_Logoff_and_Failed_Logon_Profiles]
where Action like '%Logon (%'
and TimeOccurred >= dateadd(day, -30, dateadd(day, datediff(day, 0, getdate()), 0))
group by convert(date, TimeOccurred)
order by TimeOccurred
You can use a Calendar or dates table for this sort of thing.
For only 152kb in memory, you can have 30 years of dates in a table with this:
/* dates table */
declare #fromdate date = '20000101';
declare #years int = 30;
/* 30 years, 19 used data pages ~152kb in memory, ~264kb on disk */
;with n as (select n from (values(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) t(n))
select top (datediff(day, #fromdate,dateadd(year,#years,#fromdate)))
[Date]=convert(date,dateadd(day,row_number() over(order by (select 1))-1,#fromdate))
into dbo.Dates
from n as deka cross join n as hecto cross join n as kilo
cross join n as tenK cross join n as hundredK
order by [Date];
create unique clustered index ix_dbo_Dates_date
on dbo.Dates([Date]);
Without taking the actual step of creating a table, you can use it inside a common table expression with just this:
declare #fromdate date = dateadd(day , datediff(day , 0, getdate() )-30 , 0);
declare #thrudate date = dateadd(day , datediff(day , 0, getdate() ), 0);
;with n as (select n from (values(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) t(n))
, dates as (
select top (datediff(day, #fromdate, #thrudate)+1)
[Date]=convert(date,dateadd(day,row_number() over(order by (select 1))-1,#fromdate))
from n as deka cross join n as hecto cross join n as kilo
cross join n as tenK cross join n as hundredK
order by [Date]
)
select [Date]
from dates;
Use either like so:
select
TimeOccurred = d.Date
, logon_kerberos = count (case when Action like ' %logon (kerberos)%' then 1 end)
, logon_local_wts = count (case when Action like ' %logon (local/wts)%' then 1 end)
, logon_ntlm = count (case when Action like ' %logon (ntlm)%' then 1 end)
, logon_total = count (case when Action like ' %logon (%' then 1 end)
, Count(Action)
from Dates d
left join [CPTRAX_for_Windows].[dbo].[Logon_Logoff_and_Failed_Logon_Profiles] l
on d.Date = convert(date,l.TimeOccured)
and l.Action like '%Logon (%'
where d.Date >= dateadd(day, -30, dateadd(day, datediff(day, 0, getdate()), 0))
group by d.Date
order by d.Date
Number and Calendar table reference:
Generate a set or sequence without loops - 2 - Aaron Bertrand
The "Numbers" or "Tally" Table: What it is and how it replaces a loop - Jeff Moden
Creating a Date Table/Dimension in sql Server 2008 - David Stein
Calendar Tables - Why You Need One - David Stein
Creating a date dimension or calendar table in sql Server - Aaron Bertrand

Last 2 month record in SQL

I try this SQL query to get current total and also last 2 months records and current month record means total 3 months..
Select distinct
tblRv.Owner,
(Select Count(*) as total from tblvv WHERE MasterID =tblRv.ID and Name <> '')
as currentdata
from tblRe
inner join tblRv
On tblRe.RID = tblRv.RID
WHERE
tblRe.StartDate between dateadd(m, -2, getdate()) and getdate() and
//tblRe.StartDate >= '2016-07-01 00:00:00' AND
//tblRe.EndDate <= '2016-07-08 23:59:59'
and tblRe.Region = 'uk' and
tblRv.Owner='Roh'
order by tblRv.Owner
when i exe this show me like this
OwnerName currentdata
Roh 1
Roh 2
Roh 3
Roh 5
and when i check individually write query and check from date 2016-07-01 and todate 2016-07-30 dates then this show me data 3 and 2016-06-01 00:00:00 and 2016-06-31 23:59:59 show me data 1 and 2016-05-01 00:00:00 ,2016-05-31 23:59:59 show me data 0
so i want data like this
owner july june may
roh 3 1 0
also when there will be current month i.e. aug then data must display last 2 months i.e.june july
Select
tblRv.Owner
,DATENAME(MONTH,tblRe.StartDate) as [Month]
,ISNULL(SUM(total),0) as currentdata
from tblRe
INNER JOIN tblRv ON tblRe.RID = tblRv.RID
LEFT JOIN (
Select Count(*) as total ,MasterID
from tblvv
WHERE Name <> ''
GROUP BY MasterID
) tblvv
ON tblvv.MasterID =tblRv.ID
WHERE tblRe.StartDate >= DATEADD(MONTH, -2, GETDATE())
AND tblRe.EndDate <= GETDATE()
AND tblRe.Region = 'uk'
AND tblRv.[Owner] ='Roh'
GROUP BY tblRv.Owner
,DATENAME(MONTH,tblRe.StartDate)
order by tblRv.[Owner]
You can try doing a pivot query where the three columns to be pivoted are the current, previous, and previous previous month in your data set. Note that I had to rewrite your query to remove the subqueries in the SELECT clause, because this makes it impossible to do aggregation of those columns.
SELECT tblRv.Owner,
SUM(CASE WHEN tblRe.StartDate = GETDATE() THEN t.total ELSE 0 END) AS currMonth,
SUM(CASE WHEN tblRe.StartDate = DATEADD(m, -1, GETDATE())
THEN t.total ELSE 0 END) AS prevMonth,
SUM(CASE WHEN tblRe.StartDate = DATEADD(m, -2, GETDATE())
THEN t.total ELSE 0 END) AS lastPrevMonth
FROM tblRe
INNER JOIN tblRv
ON tblRe.RID = tblRv.RID
INNER JOIN
(
SELECT MasterID, COUNT(*) AS total
FROM tblvv
GROUP BY MasterID
WHERE Name <> ''
) AS t
ON tblRv.ID = t.MasterID
WHERE tblRe.StartDate BETWEEN DATEADD(m, -2, GETDATE()) AND GETDATE() AND
tblRe.Region = 'uk' AND
tblRv.Owner = 'Roh'
GROUP BY tblRv.Owner
ORDER BY tblRv.Owner

pass date dynamically to a query to calculate day wise results

I created a new topic as i am trying to divide my initial question into multiple pieces.
The initial topic can be found in the following link.
I have created a SQL Fiddle link with table and sample data.
This is the Query that i have right now.
SELECT
Order1,
COUNT(UNit.UNIT) AS Units,
SUM(CASE
WHEN (DATEDIFF(dd, INSV_DATE, '2015-01-21')) >= 31 THEN 31
WHEN (DATEDIFF(dd, INSV_DATE, '2015-01-21')) < 0 THEN 0
ELSE (DATEDIFF(dd, INSV_DATE, '2015-01-21'))
END) AS Days31
FROM UNIT
WHERE Unit.INSV_DATE < '2015-01-21'
AND UNIT.MODEL IN ('Toyota')
AND (UNIT.Customer IN ('Jona'))
GROUP BY [Order1],
customer
how do i loop and pass date dynamically in the Datediff for a period of one month?
I want the 31 days output calculated for day wise.
The output should be like
Date | Order1 | Unit | Day31
----------------------------------
May20 | 90909 | 5 | 128
May19 | 90909 | 4 | 124
May17 | 90909 | 2 | 62
I actually want to do something like the following.
SELECT
Order1,
COUNT(UNit.UNIT) AS Units,
SUM(CASE
WHEN (DATEDIFF(dd, INSV_DATE, '2015-05-20')) >= 31 THEN 31
WHEN (DATEDIFF(dd, INSV_DATE, '2015-05-20')) < 0 THEN 0
ELSE (DATEDIFF(dd, INSV_DATE, '2015-05-20'))
END) AS Days31
FROM UNIT
WHERE Unit.INSV_DATE < '2015-05-20'
AND UNIT.MODEL IN ('Toyota')
AND (UNIT.Customer IN ('Jona'))
GROUP BY [Order1],
customer
SELECT
Order1,
COUNT(UNit.UNIT) AS Units,
SUM(CASE
WHEN (DATEDIFF(dd, INSV_DATE, '2015-05-19')) >= 31 THEN 31
WHEN (DATEDIFF(dd, INSV_DATE, '2015-05-19')) < 0 THEN 0
ELSE (DATEDIFF(dd, INSV_DATE, '2015-05-19'))
END) AS Days31
FROM UNIT
WHERE Unit.INSV_DATE < '2015-05-19'
AND UNIT.MODEL IN ('Toyota')
AND (UNIT.Customer IN ('Jona'))
GROUP BY [Order1],
customer
SELECT
Order1,
COUNT(UNit.UNIT) AS Units,
SUM(CASE
WHEN (DATEDIFF(dd, INSV_DATE, '2015-05-18')) >= 31 THEN 31
WHEN (DATEDIFF(dd, INSV_DATE, '2015-05-18')) < 0 THEN 0
ELSE (DATEDIFF(dd, INSV_DATE, '2015-05-18'))
END) AS Days31
FROM UNIT
WHERE Unit.INSV_DATE < '2015-05-18'
AND UNIT.MODEL IN ('Toyota')
AND (UNIT.Customer IN ('Jona'))
GROUP BY [Order1],
customer
Running the same query for everyday with the different date.
If you can guide me in making a day wise query that will be great.
You can build the list of dates using a recursive CTE, then CROSS JOIN it against your table to get the list of days you want:
DECLARE #StartDate date = 'Jan 1, 2015'
DECLARE #EndDate date = DATEADD(DAY, 30, #StartDate)
;WITH cte AS (
SELECT #StartDate AS ReportDate
UNION ALL
SELECT DATEADD(DAY, 1, ReportDate)
FROM cte
WHERE ReportDate < #EndDate
)
SELECT Order1,COUNT(UNit.UNIT) As Units,sum(CASE
WHEN (datediff(dd,INSV_DATE,cte.ReportDate)) >= 31 THEN 31
WHEN (datediff(dd,INSV_DATE,cte.ReportDate)) < 0 THEN 0
ELSE (datediff(dd,INSV_DATE,cte.ReportDate))END) as Days31
FROM UNIT
CROSS JOIN cte
WHERE Unit.INSV_DATE < cte.ReportDate AND
UNIT.MODEL in('Toyota') AND(UNIT.Customer in('Jona' ))
group by [Order1],customer
Based on my understanding of what you want, here is my "guess":
DECLARE #startDate DATE = CAST(MONTH(GETDATE()) AS VARCHAR) + '/' + '01/' + + CAST(YEAR(GETDATE()) AS VARCHAR) -- cast as mm/dd/yyyy
DECLARE #endDate DATE = GETDATE() -- mm/dd/yyyy
--creates a list of dates for the running month
;WITH month_dates
AS (
SELECT [Date] = DATEADD(Day, Number, #startDate)
FROM master.dbo.spt_values
WHERE Type = 'P'
AND DATEADD(day, Number, #startDate) <= #endDate
)
SELECT month_dates.[Date]
,Order1
,COUNT(UNit.UNIT) AS Units
,sum(CASE
WHEN (datediff(dd, INSV_DATE, month_dates.[Date])) >= 31
THEN 31
WHEN (datediff(dd, INSV_DATE, month_dates.[Date])) < 0
THEN 0
ELSE (datediff(dd, INSV_DATE, month_dates.[Date]))
END) AS Days31
FROM UNIT
CROSS JOIN month_dates --cross join list of dates
WHERE Unit.INSV_DATE < month_dates.[Date]
AND UNIT.MODEL IN ('Toyota')
AND (UNIT.Customer IN ('Jona'))
AND UNIT.Order1 = 'A1056729' --added this filter to test the output
GROUP BY month_dates.[Date]
,[Order1]
,customer
ORDER BY month_dates.[Date] desc

Include missing months in Group By query

I think I have a tough one here... :(
I am trying to get an order count by month, even when zero. Here's the problem query:
SELECT datename(month, OrderDate) as Month, COUNT(OrderNumber) AS Orders
FROM OrderTable
WHERE OrderDate >= '2012-01-01' and OrderDate <= '2012-06-30'
GROUP BY year(OrderDate), month(OrderDate), datename(month, OrderDate)
What I'm looking to get is something like this:
Month Orders
----- ------
January 10
February 7
March 0
April 12
May 0
June 5
...but my query skips a row for March and May. I've tried COALESCE(COUNT(OrderNumber), 0) and ISNULL(COUNT(OrderNumber), 0) but I'm pretty sure the grouping is causing that not to work.
This solution doesn't require you to hard-code the list of months you might want, all you need to do is provide any start date and any end date, and it will calculate the month boundaries for you. It includes year in the output so that it will support more than 12 months and so that your start and end dates can cross a year boundary and still order correctly and show the correct month and year.
DECLARE #StartDate SMALLDATETIME, #EndDate SMALLDATETIME;
SELECT #StartDate = '20120101', #EndDate = '20120630';
;WITH d(d) AS
(
SELECT DATEADD(MONTH, n, DATEADD(MONTH, DATEDIFF(MONTH, 0, #StartDate), 0))
FROM ( SELECT TOP (DATEDIFF(MONTH, #StartDate, #EndDate) + 1)
n = ROW_NUMBER() OVER (ORDER BY [object_id]) - 1
FROM sys.all_objects ORDER BY [object_id] ) AS n
)
SELECT
[Month] = DATENAME(MONTH, d.d),
[Year] = YEAR(d.d),
OrderCount = COUNT(o.OrderNumber)
FROM d LEFT OUTER JOIN dbo.OrderTable AS o
ON o.OrderDate >= d.d
AND o.OrderDate < DATEADD(MONTH, 1, d.d)
GROUP BY d.d
ORDER BY d.d;
Since your query Just Can't guess the months you want, you will need to have the Months that you want stored in somewhere, Join them with your table, and then group.
Something like:
;With Months (Month)
AS
(
select 'January' as Month
UNION
select 'February' as Month
UNION
select 'March' as Month
UNION
select 'April' as Month
UNION
select 'May' as Month
UNION
select 'June' as Month
UNION
select 'July' as Month
UNION
select 'August' as Month
UNION
select 'September' as Month
UNION
select 'October' as Month
UNION
select 'November' as Month
UNION
select 'December' as Month
)
--Also you could have them in a "Months" Table
Then Just JOIN this table with your table:
Select
SELECT datename(month, OrderDate) as Month, COUNT(OrderNumber)
FROM Months T1
LEFT JOIN OrderTable T2 on datename(month, T2.OrderDate) = T2.Month
WHERE (T2.OrderDate >= '2012-01-01' and T2.OrderDate <= '2012-06-30')
OR T2.OrderDate IS NULL --So will show you the months with no rows
GROUP BY year(T2.OrderDate), month(T2.OrderDate), datename(month, T2.OrderDate)
Hope it works!
Here is one using recursive CTE:
declare #StartDate datetime = '2015-04-01';
declare #EndDate datetime = '2015-06-01';
-- sample data
declare #orders table (OrderNumber int, OrderDate datetime);
insert into #orders
select 11, '2015-04-02'
union all
select 12, '2015-04-03'
union all
select 13, '2015-05-03'
;
-- recursive CTE
with dates
as (
select #StartDate as reportMonth
union all
select dateadd(m, 1, reportMonth)
from dates
where reportMonth < #EndDate
)
select
reportMonth,
Count = count(o.OrderNumber)
from dates
left outer join #orders as o
on o.OrderDate >= reportMonth
and o.OrderDate < dateadd(MONTH, 1, reportMonth)
group by
reportMonth
option (maxrecursion 0);
;

Resources