How to reduce the query execution time - sql

SELECT dt AS Date
,monthname
,dayname
,(
SELECT COUNT(1)
FROM Calendar
WHERE DATEPART(MM, dt) = DATEPART(MM, c.dt)
AND DATEPART(YEAR, dt) = DATEPART(YEAR, c.dt)
) AS daysInMonth
FROM Calendar AS c
WHERE dt BETWEEN '2000-01-01 00:00:00'
AND '2020-02-01 00:00:00'
the above query is for getting number of days of particular month for a particular date. here iam giving date range and for all the dates between the range iam just showing the days of that month.
The image shows the results for the query and its taking 25secs for ~7500 rows. can someone help me to reduce the time.

Try this one. Here you calculate the total only once instead of 7500 times.
Also create the index for dt field
with monthCount as (
SELECT DATEPART(YEAR, dt) as m_year,
DATEPART(MM, dt) as m_month
COUNT(1) as total
FROM Calendar
GROUP BY
DATEPART(YEAR, dt),
DATEPART(MM, dt)
)
SELECT dt AS Date
,monthname
,dayname
,total
FROM Calendar C
JOIN monthCount M
on DATEPART(YEAR, C.dt) = M.m_year
and DATEPART(MM, C.dt) = M.m_month
WHERE C.dt BETWEEN '2000-01-01 00:00:00'
AND '2020-02-01 00:00:00'

Related

Get total sales of last 12 months even if values are null

I have following code in which it presently gives month wise Total Sales for current year, I need to get total sales from last month of previous year to current month of this year.
My query is as follows:
;WITH mcte AS (
SELECT DATEADD(year, -1, getdate()) as MONTH_NAME
UNION ALL
SELECT DATEADD(MONTH,1,MONTH_NAME)
FROM mcte
WHERE DATEPART(MONTH,MONTH_NAME) < 12),octe AS(
SELECT (DATENAME (MONTH, DATEADD ( MONTH, DATEPART(MONTH, OI.CreatedDate), -1) )) AS MONTH_NAME,
SUM (OI.ItemQty * OI.TotalPrice) AS TOTAL_SALES
FROM Order_Item OI
GROUP BY MONTH(OI.CreatedDate))
SELECT DATENAME(MONTH,m.MONTH_NAME) + '' + DATENAME(YEAR,m.MONTH_NAME) as
MONTH_NAME, o.TOTAL_SALES FROM mcte m LEFT JOIN octe o ON o.MONTH_NAME = DATENAME(MONTH,m.MONTH_NAME)
and I am getting records
MONTH_NAME TOTAL_SALES
July2019 54023.45
August2019 NULL
December2019 NULL
September2019 NULL
October2019 NULL
November2019 NULL
Here I am only getting data for previous year only, not getting data for current year.Can anyone please guide me on this.
Thank you
You are only generating months up to 12. Try replacing the first CTE with:
WITH mcte AS (
SELECT DATEADD(year, -1, getdate()) as MONTH_NAME
UNION ALL
SELECT DATEADD(MONTH,1,MONTH_NAME)
FROM mcte
WHERE month_name < GETDATE()
),
Note the difference is the WHERE clause.
The entire query should look like this:
WITH months AS (
SELECT DATEFROMPARTS(YEAR(getdate()) - 1, MONTH(getdate()), 1) as month
UNION ALL
SELECT DATEADD(MONTH, 1, month)
FROM months
WHERE EOMONTH(month) < GETDATE()
)
SELECT m.month, SUM(OI.ItemQty * OI.TotalPrice) AS TOTAL_SALES
FROM months m LEFT JOIN
Order_Item OI oi
ON oi.CreatedDate >= m.month AND
oi.CreatedDate < DATEAADD(month, 1, m.month)
GROUP BY m.month
Try doing this:
DECLARE #CurDate DATE = GET_DATE()
DECLARE #OneYearPrior DATE = DATEADD(YEAR, -1, #CurDate)
WITH relevant_months(start_date, month_of_sale, year_of_sale) AS (
SELECT
#CurDate AS start_date,
MONTH(#CurDate) as month_of_sale,
YEAR(#CurDate) as year_of_sale
UNION ALL
SELECT DATEADD(MONTH, -1, start_date) AS start_date,
MONTH(DATEADD(MONTH, -1, start_date)) as month_of_sale,
YEAR(DATEADD(MONTH, -1, start_date)) AS year_of_sale
FROM relevant_months
WHERE DATEADD(MONTH, -1, start_date) >= #OneYearPrior
),
relevant_data AS (
SELECT OI.CreatedDate,
OI.ItemQty,
OI.TotalPrice,
MONTH(OI.CreatedDate), AS month_of_sale,
YEAR(OI.CreatedDate) AS year_of_sale
FROM Order_Item OI
WHERE OI.CreatedDate >= DATEADD(YEAR, -1, GETDATE())
)
SELECT rm.month_of_sale as month, rm.year_of_sale as year,
SUM(rd.ItemQty*rd.TotalPrice) as total_sales
FROM relevant_months rm
LEFT JOIN relevant_data rd
ON rm.month_of_sale = rd.month_of_sale
AND rm.year_of_sale = rd.year_of_sale
GROUP BY rm.month_of_sale, rm.year_of_sale
ORDER BY rm.year_of_sale asc, rm.month_of_sale asc

How to subtract an offset to dates generated through a recursive CTE?

The query below uses a recursive CTE to generate days if there is no data for that specific day. I want to group the daily downtime total starting at 7:15 the previous day until 7:15 the next day and do it over a month. This query works fine but I need to subtract DATEADD(minute, -(7 * 60 + 15) from each day.
WITH dates as (
SELECT CONVERT(date, 'Anydate') as dte
UNION ALL
SELECT DATEADD(day, 1, dte)
FROM dates
WHERE dte < 'Anydate + 1 month later'
)
SELECT CONVERT(datetime,d.dte), ISNULL(SUM(long_stop_minutes), 0) AS downtime
FROM dates d LEFT JOIN
long_stops_table b
ON CAST(t_stamp as DATE) = d.dte AND Type = 'downtime'
GROUP BY CONVERT(datetime, d.dte)
ORDER BY CONVERT(datetime, d.dte) ASC;
Just subtract the appropriate time units. Here is one way:
SELECT d.dte,
COALESCE(SUM(lst.long_stop_minutes), 0) AS downtime
FROM dates d LEFT JOIN
long_stops_table lst
ON CONVERT(date, DATEADD(minute, -(7 * 60 + 15), lst.t_stamp) = d.dte AND
lst.Type = 'downtime'
GROUP BY d.dte
ORDER BY d.dte ASC;
I see no reason to convert dates.dte to a datetime, so I just removed the conversion.

How to get count for each day between certain times

I'm trying to get a total count for each day between 07:00 and 19:00 for the last 7 days. The below query only displays the count for the date 7 days back and not each individual day. Any help would be greatly appreciated. Thanks!
DECLARE #Date AS DATETIME = DATEADD(HOUR, 7, CAST(CAST(DATEADD(DAY, -7, GETDATE()) AS DATE) AS DATETIME))
DECLARE #Date2 AS DATETIME = DATEADD(HOUR, 19, CAST(CAST(DATEADD(DAY, -7, GETDATE()) AS DATE) AS DATETIME))
SELECT CONVERT(NVARCHAR(20), DATE, 120) AS Report_Date, COUNT(DISTINCT GUID) AS ROW_COUNT
FROM TABLE WITH (NOLOCK)
WHERE DATEADD(MINUTE, +270, DATE) >= #Date
AND DATEADD(MINUTE, +270, DATE) < #Date2
GROUP BY CONVERT(NVARCHAR(20), DATE, 120)
As you need past past 7days so use getdate()- 7
SELECT CAST(DATE as DATE) AS Report_Date,
COUNT(DISTINCT GUID) AS ROW_COUNT
FROM t
WHERE DATEPART(HOUR, DATE) >= 7 AND
DATEPART(HOUR, DATE) < 19
and CAST(DATE as DATE)>=getdate()-7 and CAST(DATE as DATE)<=getdate()
GROUP BY CAST(DATE as DATE)
ORDER BY CAST(DATE as DTE)
Don't convert date columns to dates. Use date functions. I don't understand why you are adding 270 minutes to the date.
I would go for a more direct answer to your question:
SELECT CAST(DATE as DATE) AS Report_Date,
COUNT(DISTINCT GUID) AS ROW_COUNT
FROM TABLE
WHERE DATEPART(HOUR, DATE) >= 7 AND
DATEPART(HOUR, DATE) < 19
GROUP BY CAST(DATE as DATE)
ORDER BY CAST(DATE as DTE)
;With T AS
(
SELECT CAST(DATE as DATE) AS Report_Date,COUNT(DISTINCT GUID) AS ROW_COUNT
FROM tbl
WHERE
DATEPART(HOUR, DATE) >= 7 AND DATEPART(HOUR, DATE) < 19
)
SELECT Report_date,Row_Count From T GROUP BY Report_date
ORDER BY Report_date

SELECT SQL data based on date, fill month

I'm selecting year, month and net sales from invoice table. The problem is that if there's no data under specific month, there will be no rows for that month. Can you help me? Net sales should be zero if there is not any data.
SELECT
DATEPART(year, date) as 'year',
DATEPART(month, date) as 'month',
SUM(netsales) as netsales
FROM invoice
WHERE
date >= '2015-01-01'
AND date <= '2016-12-31'
GROUP BY
DATEPART(year, date),
DATEPART(month, date)
Thanks in advance.
You need a calendar table and left join
;with calendar as
(
select cast('2015-01-01' as date) as dates -- start date
union all
select dateadd(mm,1,dates) from cte where dates < '2016-12-31' -- end date
)
SELECT
DATEPART(year, c.dates) as 'year',
DATEPART(month, c.dates) as 'month',
SUM(netsales) as netsales
FROM calendar C left join invoice i on c.dates = cast(i.[date] as date)
GROUP BY
DATEPART(year, date),
DATEPART(month, date)
I have generates dates on the fly using Recursive CTE, but I will always suggest to create a calendar table physically and use it in such queries

How to get the week start and end dates?

I have variable called WeekBeginDate and I want only to pull data for that week. For example, if the beginning of the week date is 07/21/2014 which is Monday in this case, then I want only to pull the data from 07/21/2014 to 7/27/2014.
The variable will always contain the date for the beginning of the week only but I don’t have the date for the end of the week.
The week begins on Monday and ends on Sunday. I can’t figure out how to calculate or sum the number of hours if I only have the date for the beginning of week.
SELECT DT, sum (TOT_HOURS)as TOT_HOURS FROM MYTABLE where DT >= #WeekBeginDate and <=#WeekEndDate group by DT
Note, that I only have the variable for the WeekBeginDate.
just modify your table columns in this CTE it may works :
;WITH workhours AS
(
SELECT DATEADD(DAY
, -(DATEPART(dw, DT) -1)
, DT) AS week_start
, DATEADD(DAY
, 7 - (DATEPART(dw, DT))
, DT) AS week_end
FROM MYTABLE
)
SELECT week_start
, week_end
, SUM(TOT_HOURS) total_hrs_per_week
FROM workhours
GROUP BY week_start
, week_end
You may need to add 6 days to the beginning of the week
and group by something else if you need total weekly hours, i'm calling it "id".
not by dt (or don't group at all if it is a total for the whole table):
SELECT id, DT, sum (TOT_HOURS)as TOT_HOURS FROM MYTABLE
where DT BETWEEN #WeekBeginDate and DATEADD(d,6,#WeekBeginDate)
GROUP BY id
This should be of some use to you. I am casting to date so the 24 hrs of day is considered.
DECLARE #WeekBeginDate DATETIME
SET #WeekBeginDate = '2014-07-28 12:08:31.633';
WITH MYTABLE (DT,TOT_HOURS)
AS (
SELECT '2014-06-27 00:08:31.633',5 UNION ALL
SELECT '2014-07-27 00:08:31.633',5 UNION ALL
SELECT '2014-07-28 00:08:31.633',1 UNION ALL
SELECT '2014-07-29 00:08:31.633',1 UNION ALL
SELECT '2014-07-30 00:08:31.633',1 UNION ALL
SELECT '2014-07-31 00:08:31.633',1 UNION ALL
SELECT '2014-08-01 00:08:31.633',1 UNION ALL
SELECT '2014-08-02 00:08:31.633',1 UNION ALL
SELECT '2014-08-03 00:08:31.633',1
)
SELECT CAST(#WeekBeginDate AS DATE) AS StartDate,
DATEADD(d, 6, CAST(#WeekBeginDate AS DATE)) AS EndDate,
SUM (TOT_HOURS)AS TOT_HOURS
FROM MYTABLE
WHERE CAST(DT AS DATE) BETWEEN CAST(#WeekBeginDate AS DATE) AND DATEADD(d, 6, CAST(#WeekBeginDate AS DATE))
Just add 6 (or 7) days...
SELECT DT, sum (TOT_HOURS)as TOT_HOURS FROM MYTABLE
where DT BETWEEN #WeekBeginDate and #WeekBeginDate + 6 group by DT
select #weekBeginDate = DATEADD(wk, DATEDIFF(wk,0,GETDATE()), 0)
select #WeekEndDate = DATEADD(dd, 6, DATEADD(wk, DATEDIFF(wk,0,GETDATE()), 0))
SELECT DT, sum (TOT_HOURS)as TOT_HOURS FROM MYTABLE where DT >= #WeekBeginDate and <=#WeekEndDate group by DT
Here is where having a calendar table would be very useful,
especially if your logic needs to change if Monday is a holiday.
Basically create a table with pre-calculated values for weeks and just join to it.
http://www.made2mentor.com/2011/04/calendar-tables-why-you-need-one