Combining daily averages for different time periods in a single query - sql

I have a table with hourly entries for multiple products dating back to 2 years. I am trying to write a query which would look something like this:
PRODUCT, TODAY'S AVERAGE, LAST MONTHS DAILY AVERAGE, YEAR TO DATE DAILY AVERAGE
I am able to achieve this by writing separate queries for each of the averages and then joining them on the PRODUCT NAME. However, I want to be able to do the same, by writing one single query.
Is their a standard algorithm/method that I can apply?

This is an aggregation query. However, it gets variables for each of the time periods you want, and sums by day to do the final calculations.
select product,
sum(DailySum*IsToday) as Today,
sum(1.0*DailySum*IslastMonth) / sum(IslastMonth)
sum(1.0*DailySum*IsYTD) / sum(IsYTD)
from (select product, cast(dt as date) as thedate, sum(val) as DailySum
(case when cast(dt as date) = cast(getdate() as date) then 1 else 0 end) as IsToday,
(case when year(dt) = year(dateadd(month, -1, getdate()) and month(dt) = month(dateadd(month, -1, getdate())
then 1 else 0
end) as IslastMonth,
(case when year(dt) = year(getdate()) tehn 1 else 0
end) as IsYTD
from t
group by product, cast(dt as date)
) t
) t

Related

Matching date with calculated DATEADD date

I am trying to create a table with columns containing the current date, prior year date, and additional column for the total sum revenue as below:
cur_date | py_date | py_rev
I'm trying to compare revenue across any daily period across years. Assume all dates and revenue values are included in the same SQL Server table.
I attempted to use a case statement using [date] = DATEADD(wk,-52,[date]) as the condition to return the appropriate total. The full line code is below:
select
[date] as cur_date,
DATEADD(wk,-52,[date]) py_date,
SUM(case when [date] = DATEADD(wk,-52,[date]) then sum_rev else 0 end) as py_rev
from summary
group by [date]
When running this code the py_date is as expected but py_rev returns 0 as if there is no match. What's most confusing is if I hard code a random date in place of the DATEADD portion then a total is returned. I have also tried CAST to format both date and the DATEADD portion as date with no luck.
Is there something about DATEADD that will not match to other date columns that I'm missing?
If you want the previous years revenue, then lag() is one method. This works assuming that "previous year" means 52 weeks ago and you have records for all dates:
select [date] as cur_date,
dateadd(week, -52, [date]) as py_date,
lag(sum_rev, 52 * 7) over (order by date) as py_rev
from summary;
If you do not have records for all dates, then another approach is needed. You can use a LEFT JOIN:
select s.date, dateadd(week, -52, s.[date]),
sprev.sum_rev as py_rev
from summary s left join
summary sprev
on sprev.date = dateadd(week, -52, s.[date]);

SQL - Use DATEADD in GROUP BY (?)

Im working on a query that is meant to retrive sales by hour, which it does. However, in the used database table all the timestamps are UTC +1, also for sales made in a country with UTC +2.
So what I'm trying to achive is a result that can be used by local business units (a parameter is set depending on who is looking at a report will determine the which country/store to display). So when it's sales in a UTC +2 country the datestamp needs to be modified with +1.
I'm thinking this can be done in the group by, perhaps by using a DATEADD together with a condition that checks the county name. For example, when the country is 'Greece' (column exists in the database), use a DATEADD to add 1 hour to the timestamp.
Is this a possible solution and if so, how is it done?
This is the GROUP BY im using at the moment:
SELECT
DATEPART(hour, sales.OrderDate) AS Hour,
SUM(CASE WHEN FORMAT(sales.OrderDate, 'yyyy-MM-dd') = Cast(GETDATE() AS date) THEN sales.SALES * (1 + sales.vv / 100) END) AS SALES,
COUNT(DISTINCT (CASE WHEN FORMAT(sales.OrderDate, 'yyyy-MM-dd') = Cast(GETDATE() AS date) THEN sales.OID END) ) AS CUSTOMERS,
MAX(CASE WHEN FORMAT(sv.Date_Time, 'yyyy-MM-dd') = Cast(GETDATE() AS date) THEN sv.CC END) AS VISITORS
FROM
[DW].[Tot_Sales] AS sales
LEFT JOIN [DW].[SV_24M] AS sv ON dateadd(hour, datediff(hour, 0, sales.[OrderDate]), 0) = dateadd(HOUR, 0, sv.Date_Time)
AND sales.SID = sv.SID
WHERE
sales.SID = #Store
AND FORMAT(sales.OrderDate, 'yyyy-MM-dd') > DATEADD(year, - 1, Cast(GETDATE() AS date))
GROUP BY
sales.SID,
DATEPART(hour, sales.OrderDate)
ORDER BY
DATEPART(hour, sales.OrderDate)
It is in the columns SALES and CUSTOMERS this needs to be applied, and they are from the table named sales. This is how the result looks:
Resultset
The issue is that the sales and customers occuring at hour 9 actually occured hour 10 in UCT +2. Visitors data arrives in local time (UCT +2 in this case), and therefore it's a mismatch between Visitors and Customers.
Sample data set:
Dataset
If you know the countries then you could make use of AT TIME ZONE in the query to set the appropriate local time.
Refer to this

SQL Over partition by

I basically have a case statement that displays the sum of profit and a month to date total for each person. My idea is i want to display a daily figure of that person as well as their whole month total altogether.
My issue is when i limit results to just yesterday (supposed to be a daily figure) this then effects the calculation of the month value (just calculates the sum for that day rather than the whole month).
This is because the total month values are all out of the scope of the query. Is there anyway to calculate the whole month value for each person correctly without having the limits of where effecting the result.
e.g.
The result:
08/09/17: 25
09/09/17: 25
10/09/17: 25
11/09/17: 25 <<<< but only display one day and month total
Overall Month total: 100
Can this also includes nulls too? I think im almost looking at a dynamically stored month to date value that isn't effected by where clauses.
SELECT SUM(Figure) AS 'Daily Figure',
CASE WHEN
MONTH([DATE]) = MONTH(getdate()) AND
YEAR([DATE]) = YEAR(getdate())
THEN
SUM(Figure)
OVER (PARTITION BY [Name],
MONTH([DATE]))
ELSE 0 END
as [Month To Date Total]
WHERE
dateadd(day,datediff(day,1,GETDATE()),0)
If you want month-to-date and the current amount, then use conditional aggregation:
SELECT NAME,
SUM(CASE WHEN DAY(DATE) = DAY(GETDATE()) - 1 THEN Figure ELSE 0 END) AS DailyFigure,
SUM(Figure) as MonthToDate
WHERE MONTH([DATE]) = MONTH(getdate()) AND
YEAR([DATE]) = YEAR(getdate())
GROUP BY NAME;
This works on all but the first day of the month.

SQL to count records per hour and count how many hours that total record count was greater than a value

Using SQL Server 2014. I have a list of records that are time stamped and I would like to count how many records there are per hour then count how many hours each day that the record count exceeded a given number, say 20.
Here's what I have so far:
select count(distinct datepart(hour, convert(datetime, OrderStateDate))) Count_HoursOver,
datepart(YEAR, convert(datetime, OrderStateDate)) Date_YEAR,
datepart(month, convert(datetime, OrderStateDate)) Date_MONTH,
datepart(day, convert(datetime, OrderStateDate)) Date_DAY
from Reporting.dbo.Orders
group by datepart(YEAR, convert(datetime, OrderStateDate)),
datepart(month, convert(datetime, OrderStateDate)),
datepart(day, convert(datetime, OrderStateDate))
having count(idscript) >= 20
The results aren't correct and I can't make sense of what's being returned and why. Am I using HAVING incorrectly? Any advice would be appreciated!
You kind of have a 2-part question here
I would like to count how many records there are per hour
You can create a query that returns tuples (RecordsPerHour,HOUR,YEAR,MONTH,DAY) as follows:
SELECT
count(*) as RecordsPerHour,
datepart(hour,convert(datetime,OrderStateDate)) as Date_HOUR,
datepart(year,convert(datetime,OrderStateDate)) as Date_YEAR,
datepart(month,convert(datetime,OrderStateDate)) as Date_MONTH,
datepart(day,convert(datetime,OrderStateDate)) as Date_DAY
FROM Reporting.dbo.Orders
GROUP BY
datepart(year,convert(datetime,OrderStateDate)),
datepart(month,convert(datetime,OrderStateDate)),
datepart(day,convert(datetime,OrderStateDate)),
datepart(hour,convert(datetime,OrderStateDate))
then count how many hours each day that the record count exceeded a given number, say 20
To do this, use the query from the first part of your question in a nested query, using a HAVING clause to filter only hours that contain at least 20 orders.
On the outer query, group by (YEAR,MONTH,DAY) to determine the number of hours in that day with at least 20 orders:
SELECT
count(*) as HoursWithAtLeast20Orders,
Date_YEAR,
Date_MONTH,
Date_DAY
FROM
(SELECT
datepart(hour,convert(datetime,OrderStateDate)) as Date_HOUR,
datepart(year,convert(datetime,OrderStateDate)) as Date_YEAR,
datepart(month,convert(datetime,OrderStateDate)) as Date_MONTH,
datepart(day,convert(datetime,OrderStateDate)) as Date_DAY
FROM Reporting.dbo.Orders
GROUP BY
datepart(year,convert(datetime,OrderStateDate)),
datepart(month,convert(datetime,OrderStateDate)),
datepart(day,convert(datetime,OrderStateDate)),
datepart(hour,convert(datetime,OrderStateDate))
HAVING count(*) >=20) as t
GROUP BY
Date_YEAR,
Date_MONTH,
Date_DAY
First round to hour then round to day
SELECT
count(*) as [hours with 20+ Orders],
dateadd(day, datediff(day,'20000101',dt_hour_rounded),'20000101') as dt_day_rounded
FROM (
SELECT
count(*) as OrdersInHour,
dateadd(hour, datediff(hour,'20000101',OrderStateDate),'20000101') as dt_hour_rounded
FROM Reporting.dbo.Orders
GROUP BY dateadd(hour, datediff(hour,'20000101',OrderStateDate),'20000101')
) t
GROUP BY dateadd(day,datediff(day,'20000101',dt_hour_rounded),'20000101')
WHERE OrdersInHour >= 20

SQL Query Average by Day of Week

I am trying to devise a query which will tell me the average number of procedures done on a given weekday as well as the total number of procedures on that week day for the entire time frame. The query I've developed looks like it works, but the values are not adding up correctly.
SELECT [Day], COUNT(*) AS "Week Day Count", AVG(Totals) AS [Avg]
FROM
(
SELECT
w = DATEDIFF(WEEK, 0, CompleteDate),
[Day] = DATENAME(WEEKDAY, CompleteDate),
Totals = COUNT(*)
FROM dbo.[order]
WHERE CompleteDate Between '2015-01-01' AND '2016-04-22'
AND PlacerFld2 IN ('CT','SAMR')
AND OrderStatusID = '2'
GROUP BY
DATEDIFF(WEEK, 0, CompleteDate),
DATENAME(WEEKDAY, CompleteDate),
DATEPART(WEEKDAY, CompleteDate)
) AS q
GROUP BY [Day]
ORDER BY [Day];
I feel like the Average results are correct, however, the "Week Day Count" does not come up nearly as high as I thought it should be and perhaps it's just the way I am computing it.
When I add up the values in the Week Day Count it comes up to be about 365, but when I do the query below, I get about 1750:
SELECT COUNT(*) AS "Total 2015-2016"
FROM [order]
WHERE CompleteDate Between '2015-01-01' AND '2016-04-22'
AND PlacerFld2 IN ('CT','SAMR')
AND OrderStatusID = '2'
I suspect that you actually want the sum of the total:
SUM(Totals) AS "Week Day Count"
Your query is (I think) counting the number of days in the data for each weekday.