Include date boundaries to segment select results - sql

I want to return in the same row the results of a particular measure on different time range.
For instance "Last 30 days", "Last 7 days", "Last 3 days".
On doing that I've initially used the UNION function, and created multiple sub-queries for every time range. The downside of doing that is I'm collecting the same numbers three times, with a consequent increase in running time.
A colleague suggested me to use the CASE function to segment the time range. I've initially thought to implement it as follows:
select tp.Name,
case when pub.Date between DATEADD(day, -31, getdate()) and DATEADD(day, -1, getdate())
then SUM(Impressions) else 0 end 'Last 30 days Impressions',
case when pub.Date between DATEADD(day, -31, getdate()) and DATEADD(day, -1, getdate())
then SUM(Revenue * rler.Rate) else 0 end 'Last 30 days Revenues',
case when pub.Date between DATEADD(day, -8, getdate()) and DATEADD(day, -1, getdate())
then SUM(Impressions) else 0 end 'Last 7 days Impressions',
case when pub.Date between DATEADD(day, -8, getdate()) and DATEADD(day, -1, getdate())
then SUM(Revenue * rler.Rate) else 0 end 'Last 7 days Revenues',
case when pub.Date between DATEADD(day, -4, getdate()) and DATEADD(day, -1, getdate())
then SUM(Impressions) else 0 end 'Last 3 days Impressions',
case when pub.Date between DATEADD(day, -4, getdate()) and DATEADD(day, -1, getdate())
then SUM(Revenue * rler.Rate) else 0 end 'Last 3 days Revenues'
from ...
where ...
group by tp.Name, tp.Kind, pub.Date
order by 'Last 30 days Impressions'
Unfortunately this will return a row for each Name, Kind and Date which is not what I want. The issue I think relies on the pub.Date in the GROUP BY call. What should I do to overcome the issue?

You can't quite implement your colleague's suggestion because the times overlap. You can do non-overlapping ranges, something like this:
select tp.Name,
(case when pub.Date between DATEADD(day, -3, getdate()) and DATEADD(day, -1, getdate())
then 'Last 3 days Impressions'
when pub.Date between DATEADD(day, -7, getdate()) and DATEADD(day, -1, getdate())
then '4-7 days Impressions'
when pub.Date between DATEADD(day, -31, getdate()) and DATEADD(day, -1, getdate())
then '8-31 days Impressions'
else 'Older'
end) as TimeRange,
SUM(Impressions) as NumImpressions,
. . .
from . . .
where . . .
group by tp.Name,
(case when pub.Date between DATEADD(day, -3, getdate()) and DATEADD(day, -1, getdate())
then 'Last 3 days Impressions'
when pub.Date between DATEADD(day, -7, getdate()) and DATEADD(day, -1, getdate())
then '4-7 days Impressions'
when pub.Date between DATEADD(day, -31, getdate()) and DATEADD(day, -1, getdate())
then '8-31 days Impressions'
else 'Older'
end)

I'm answering my own question. The result can be achieve using the following code:
select tp.Name,
sum(case when pub.Date between DATEADD(day, -31, getdate()) and DATEADD(day, -1, getdate())
then Impressions else 0 end) 'Last 30 days Impressions',
sum(case when pub.Date between DATEADD(day, -31, getdate()) and DATEADD(day, -1, getdate())
then (Revenue * rler.Rate) else 0 end) 'Last 30 days Revenues',
sum(case when pub.Date between DATEADD(day, -8, getdate()) and DATEADD(day, -1, getdate())
then Impressions else 0 end) 'Last 7 days Impressions',
sum(case when pub.Date between DATEADD(day, -8, getdate()) and DATEADD(day, -1, getdate())
then (Revenue * rler.Rate) else 0 end) 'Last 7 days Revenues',
sum(case when pub.Date between DATEADD(day, -4, getdate()) and DATEADD(day, -1, getdate())
then Impressions else 0 end) 'Last 3 days Impressions',
sum(case when pub.Date between DATEADD(day, -4, getdate()) and DATEADD(day, -1, getdate())
then (Revenue * rler.Rate) else 0 end) 'Last 3 days Revenues'
from ...
where ...
group by tp.Name, tp.Kind
order by 'Last 30 days Impressions' desc

Related

How to return just the yyyy-mm-dd from the following SQL queries [duplicate]

This question already has answers here:
How to get a date in YYYY-MM-DD format from a TSQL datetime field?
(25 answers)
Closed 4 years ago.
Below are the queries I created. Query #1 returns 2018-07-06 00:00:00.000 and query #2 returns 2018-07-31 23:59:59.997.
I can't figure out how to modify the queries so that they'll return the result in the yyyy-mm-dd format. Please advice.
Query #1 - get fifth business day of the month
SELECT
FifthWeekDay = DATEADD(dd, CASE
WHEN DATEDIFF(dd, -1, ca.FirstOfMonth) % 7 > 1 -- -1 is a Sunday
THEN 7
ELSE 6 - DATEDIFF(dd, -1, ca.FirstOfMonth) % 7 -- -1 is a Sunday
END, ca.FirstOfMonth - 1)
FROM
(SELECT
DATEADD(mm, (SELECT DATEPART(YEAR, GETDATE())) * 12 - 22801 +
(SELECT DATEPART(M, GETDATE())), 0)) ca(FirstOfMonth)
Query #2 - get last business day of the month
SELECT
DATEADD(ms, -3, DATEADD(mm, DATEDIFF(mm, 0, GETDATE()) + 1, 0))
- CASE DATENAME(dw, DATEADD(ms, -3, DATEADD(mm, DATEDIFF(mm, 0, GETDATE()) + 1, 0)))
WHEN 'SUNDAY' THEN 2
WHEN 'SATURDAY' THEN 1
ELSE 0
END AS LastBusinessCurrentMonth
You can subquery the results you already have and add a cast( as date):
SELECT CAST(LastBusinessCurrentMonth AS DATE) AS LastBusinessCurrentMonth FROM(
SELECT
DATEADD(ms, -3, DATEADD(mm, DATEDIFF(mm, 0, GETDATE()) + 1, 0))
- CASE DATENAME(dw, DATEADD(ms, -3, DATEADD(mm, DATEDIFF(mm, 0, GETDATE()) + 1, 0)))
WHEN 'SUNDAY' THEN 2
WHEN 'SATURDAY' THEN 1
ELSE 0
END AS LastBusinessCurrentMonth) AS X
SELECT CAST(FIFTHWEEKDAY AS DATE) AS FIFTHWEEKDAY FROM(
SELECT
FifthWeekDay = DATEADD(dd, CASE
WHEN DATEDIFF(dd, -1, ca.FirstOfMonth) % 7 > 1 -- -1 is a Sunday
THEN 7
ELSE 6 - DATEDIFF(dd, -1, ca.FirstOfMonth) % 7 -- -1 is a Sunday
END, ca.FirstOfMonth - 1)
FROM
(SELECT
DATEADD(mm, (SELECT DATEPART(YEAR, GETDATE())) * 12 - 22801 +
(SELECT DATEPART(M, GETDATE())), 0)) ca(FirstOfMonth)) AS X
Or you could declare a variable as Date and set the results of your queries to that variable :
DECLARE #DATEVAR DATE
SET #DATEVAR =(
SELECT
DATEADD(ms, -3, DATEADD(mm, DATEDIFF(mm, 0, GETDATE()) + 1, 0))
- CASE DATENAME(dw, DATEADD(ms, -3, DATEADD(mm, DATEDIFF(mm, 0, GETDATE()) + 1, 0)))
WHEN 'SUNDAY' THEN 2
WHEN 'SATURDAY' THEN 1
ELSE 0
END AS LastBusinessCurrentMonth)
SELECT #DATEVAR AS LastBusinessCurrentMonth
SET #DATEVAR = (SELECT
DATEADD(ms, -3, DATEADD(mm, DATEDIFF(mm, 0, GETDATE()) + 1, 0))
- CASE DATENAME(dw, DATEADD(ms, -3, DATEADD(mm, DATEDIFF(mm, 0, GETDATE()) + 1, 0)))
WHEN 'SUNDAY' THEN 2
WHEN 'SATURDAY' THEN 1
ELSE 0
END AS LastBusinessCurrentMonth)
SELECT #DATEVAR AS LastBusinessCurrentMonth

Using CASE in WHERE clause?

I am getting the following error in my WHERE clause on the very last AND statement AND CAST(cp.EndDate...). I just added the CASE statement to know if the month is January and to compare the cp.startdate >= value1 OR value 2
Error:
An expression of non-boolean type specified in a context where a condition is expected, near 'and'.
My WHERE clause:
WHERE
(CAST(cp.startdate AS DATE) >= CAST(DATEADD(year, -1, DATEADD(month, DATEDIFF(month, 0, getdate()), 0)) AS DATE)
OR --FirstDayOfCurrentMonthPriorYear
CASE
WHEN DATEPART(month,getdate()) = 1 --month is January
THEN CAST(DATEADD(month, DATEDIFF(month, 0, getdate())-1, 0) AS DATE) --FirstDayOfLastMonthPriorYear
ELSE CAST(DATEADD(year, -1, DATEADD(month, DATEDIFF(month, 0, getdate()) -1, 0)) AS DATE) --FirstDayOfLastMonthPriorYear
END)
AND CAST(cp.EndDate AS DATE) <= CAST(DATEADD(day, -1, DATEADD(month, DATEDIFF(month, 0, GETDATE()) + 1, 0)) AS DATE) --LastDayOfCurrentMonthCurrentYear
Not sure what's wrong... any help in implementing this would be appreciated.
From a pure syntax point of view, you need to compare something to your CASE output. Something like this would be syntactically correct.
WHERE
(
CAST(cp.startdate AS DATE) >= CAST(DATEADD(year, -1, DATEADD(month, DATEDIFF(month, 0, getdate()), 0)) AS DATE)
OR
CAST(cp.startdate AS DATE) >= CASE
WHEN DATEPART(month,getdate()) = 1 --month is January
THEN CAST(DATEADD(month, DATEDIFF(month, 0, getdate())-1, 0) AS DATE)
ELSE CAST(DATEADD(year, -1, DATEADD(month, DATEDIFF(month, 0, getdate()) -1, 0)) AS DATE)
END
)
AND CAST(cp.EndDate AS DATE) <= CAST(DATEADD(day, -1, DATEADD(month, DATEDIFF(month, 0, GETDATE()) + 1, 0)) AS DATE)
Here is the previous question
I think this is what you are looking for:
WHERE CAST(cp.startdate AS DATE) >= CASE WHEN DATEPART(MONTH,GETDATE()) = 1
THEN DATEADD(DAY,1,EOMONTH(GETDATE(),-1))
ELSE DATEADD(YEAR,-1,DATEADD(DAY,1,EOMONTH(GETDATE(),-1)))
END --FirstDayOfCurrentMonthPriorYear
AND CAST(cp.EndDate AS DATE) <= EOMONTH(GETDATE()) --LastDayOfCurrentMonthCurrentYear
This checks that the cp.startdate is greater than or equal to the first day of the current month in the prior year, and that the cp.enddate is less than or equal to the last day of the current month. Replace GETDATE() with your date of choice.

SQL how to WHERE CASE WHEN x THEN BETWEEN dates

I am working on a SQL report that is trying to break down the year into 4 quarters. And to keep from having to run a different script depending on the year I am trying to get it to all pull from one. So what I have attempted to do is have a CASE statement in the where clause that will figure out what quarter of the year it is then tell it that x date needs to be BETWEEN dates y and z. This has resulted in many syntax errors and I can't quite find where. Would you guys be able to take a look and let me know if it is even possible?
`AND s.actual_arrival BETWEEN
CASE
WHEN MONTH(DATEADD(dd, -1, GETDATE())) < 4 THEN YEAR(DATEADD(dd, -1, GETDATE())) + '-01-01' AND YEAR(DATEADD(dd, -1, GETDATE())) + '03-31 23:59:59'
WHEN MONTH(DATEADD(dd, -1, GETDATE())) > 3 AND MONTH(DATEADD(dd, -1, GETDATE())) < 7 THEN YEAR(DATEADD(dd, -1, GETDATE())) + '-04-01' AND YEAR(DATEADD(dd, -1, GETDATE())) + '06-30 23:59:59'
WHEN MONTH(DATEADD(dd, -1, GETDATE())) > 6 AND MONTH(DATEADD(dd, -1, GETDATE())) < 10 THEN YEAR(DATEADD(dd, -1, GETDATE())) + '-07-01' AND YEAR(DATEADD(dd, -1, GETDATE())) + '09-31 23:59:59'
WHEN MONTH(DATEADD(dd, -1, GETDATE())) > 9 THEN YEAR(DATEADD(dd, -1, GETDATE())) + '-10-01' AND YEAR(DATEADD(dd, -1, GETDATE())) + '12-31 23:59:59'
END`
Hmmm, wouldn't it be simpler to just do this?
and datepart(quarter, s.actual_arrival) = datepart(quarter, getdate()) and
year(s.actual_arrival) = year(getdate())

Sum of Dates between, T-SQL error

I'm trying to check if one of the join_date or date_of_change (date fields) are within the range and count them but I get an error:
sqlserver.jdbc.SQLServerException: The conversion from int to
TIMESTAMP is unsupported.
SUM(CASE WHEN (join_date BETWEEN DATEADD(day, -8, GETDATE()) AND DATEADD(day, -1, GETDATE())) OR (date_of_change BETWEEN DATEADD(day, -8, GETDATE()) AND DATEADD(day, -1, GETDATE())) THEN 1 ELSE 0 END) AS Total
Could someone explain to me what I'm doing wrong.
Original Code:
SELECT DISTINCT mtype, CASE WHEN (join_date BETWEEN DATEADD(day, -8,
GETDATE()) AND DATEADD(day, -1, GETDATE())) OR (date_of_change BETWEEN
DATEADD(day, -8, GETDATE()) AND DATEADD(day, -1, GETDATE())) THEN 1 ELSE 0
END AS Total FROM T0 GROUP BY mype, join_date,date_of_change
As #Alex K has said there could be another statement in the batch causing the problem as there doesn't seem to be any TimeStamp involved in this query.
Answering your last comment about the GROUP BY, you can simplify the query the following way:
SELECT
mtype, COUNT(1) as Total
FROM
T0
WHERE
(join_date BETWEEN DATEADD(day, -8, GETDATE()) AND DATEADD(day, -1, GETDATE()))
OR (date_of_change BETWEEN DATEADD(day, -8, GETDATE()) AND DATEADD(day, -1, GETDATE()))
GROUP BY
mype
But I am afraid that if the error is in a different statement.
I wrote the query again and it worked, must be a typo somewhere

First business day of the current month - SQL Server

How could I get the first business day of the current month?
Without create a function, only select.
something like that:
SELECT CONVERT(VARCHAR(25),DATEADD(dd,-(DAY(GETDATE())-1), GETDATE()), 101)
somebody knows please?
Thanks.
A Simple case statement could do it
SELECT CASE
WHEN DATENAME(WEEKDAY, dateadd(mm, DATEDIFF(MM, 0, getdate()), 0)) = 'Saturday'
THEN dateadd(mm, DATEDIFF(MM, 0, getdate()), 0) + 2
WHEN DATENAME(WEEKDAY, dateadd(mm, DATEDIFF(MM, 0, getdate()), 0)) = 'Sunday'
THEN dateadd(mm, DATEDIFF(MM, 0, getdate()), 0) + 1
ELSE dateadd(mm, DATEDIFF(MM, 0, getdate()), 0)
END
This will literally give you what you're asking for -- the first business day in a month if we define a business day as "any day that's not a Saturday or a Sunday". But this is is a very narrow definition of "business day" that is not appropriate when taking into account holidays and cultural differences, so it generalizes poorly. The typical solution for this problem is to create a table that actually holds the working days (which is generated somewhere just before he year, or calculated in advance if that's feasible), and simply look it up in that.
SELECT DATEADD(DAY,
CASE
(DATEPART(WEEKDAY, DATEADD(MONTH, DATEDIFF(MONTH, 0, GETDATE()), 0)) + ##DATEFIRST - 1) % 7
WHEN 6 THEN 2
WHEN 7 THEN 1
ELSE 0
END,
DATEADD(MONTH, DATEDIFF(MONTH, 0, GETDATE()), 0)
)
This solution uses ##DATEFIRST to avoid any language issues -- using DATEPART(WEEKDAY, ...) on its own or DATENAME() only works if we assume a specific region.
If you have more flexibility in your SELECT statement, you might be able to use something like this:
;With Daterange As
(
Select DateAdd(Month, DateDiff(Month, 0, GetDate()), 0) As Date Union All
Select DateAdd(Day, 1, Date) As Date
From DateRange
Where Date < DateAdd(Day, 6, DateAdd(Month, DateDiff(Month, 0, GetDate()), 0))
)
Select Convert(Date, Min(Date)) FirstBusinessDay
From Daterange
Where DatePart(WeekDay, Date) Not In (7, 1)
Try this.
SELECT CASE
WHEN Datename(dw, Dateadd(dd, -Datepart(dd, Getdate()) + 1, Getdate())) = 'Saturday' THEN Dateadd(dd, -Datepart(dd, Getdate()) + 3, Getdate())
WHEN Datename(dw, Dateadd(dd, -Datepart(dd, Getdate()) + 1, Getdate())) = 'Sunday' THEN Dateadd(dd, -Datepart(dd, Getdate()) + 2, Getdate())
ELSE Dateadd(dd, -Datepart(dd, Getdate()) + 1, Getdate())
END
Explanation :
Find the first day of the month.
Dateadd(dd, -Datepart(dd, Getdate()) + 1, Getdate())
Then check the day of the previous date by using Datename function.
Datename(dw, Dateadd(dd, -Datepart(dd, Getdate()) + 1, Getdate()))
If the Datename is Saturday the add 2 days to the first day of the month . if it is sunday then add 1 day to the first day of the month else get the first day of the month