I need to classify a set of dates as either 'Cur. YTD', 'Lst. YTD', or 'Other'. YTD is based upon getdate(). I have a temp table for testing that has a single column called 'calendar_date' of type DATETIME. I came up with this logic and it appears to work. I'm just wondering if this approach makes good sense from a performance perspective or if something else might be better.
select calendar_date,
case when (MONTH(calendar_date) < MONTH(getdate()))
or (MONTH(calendar_date) = MONTH (getdate())
AND DAY(calendar_date) <= DAY(getdate())) then
case when YEAR(calendar_date) = YEAR(GETDATE()) then 'CYTD'
when YEAR(calendar_date) = YEAR(getdate()) - 1 then 'LYTD'
else 'Other'
end
else 'Other'
end as Tim_Tag_YTD
from #temp1
Your logic looks good and will work as-is.
An alternative which simplifies a little, which assumes you have no future data.
select
calendar_date,
Tim_Tag_YTD = case DATEDIFF(YEAR, calendar_date, GETDATE())
when 0 then 'CYTD'
when 1 then 'LYTD'
else 'Other'
end
from #temp1;
In the case of your logic, you are explicitly putting future data into 'Other', which you can also do like this:
select
calendar_date,
Tim_Tag_YTD = case when calendar_date > GETDATE() then 'Other' else
case DATEDIFF(YEAR, calendar_date, GETDATE())
when 0 then 'CYTD'
when 1 then 'LYTD'
else 'Other'
end
end
from #temp1;
Sometimes something unintuitive performs faster. Something like this might be worth a shot.
set variable #FirstOfLastYear to Jan 1 of last year
using sql server date functions
set #FirstOfThisYear = DateAdd(year, 1, #FirstOfLastYear)
select 'last year' period
, whatever else you need
from #temp1 where calendar_date >= #FirstOfLastYear
and calendar_date < #FirstOfThisYear
union
select 'this year' period
, whatever else you need
from #temp1 where calendar_date >= #FirstOfThisYear
and calendar_date < getDate ()
union
select 'other' period
, whatever else you need
from #temp1 where calendar_date <= #FirstOfLastYear
or calendar_date > getdate()
You'll never know unless you try.
Related
I'm looking for a way to have my sql query change the dates in my query on the 15th of every month so my data pulls have a "rolling 12 months". For example, right now, my query pulls the dates (YYYYQMM) between 2018411 and 2019410. I have it hard coded so it looks like this:
Select *
From Table
WHERE Spend_Period_YYYYQMM >= 2018411 and Spend_Period_YYYYQMM <= 2019410
I would like to have a solution that on Dec 15th it changes the dates to 2018412 and 2019411.
I have a solution to change the dates every month like below but would like the dates to change on the 15th:
Spend_Period_YYYYQMM between to_char(date(current_date - cast('13 month' as interval)),'YYYYQMM')
and to_char(date(current_date - cast('2 month' as interval)),'YYYYQMM')
How should I got about it
We can use some CASE clauses and the EOMONTH function along with GetDate() to get your desired range.
DECLARE #date DATETIME = GETDATE();
select case
when day(#date) <= 14
then EOMONTH(#date, -13)
when day(#date) >= 15
then EOMONTH(#date, -12)
end as RollingYearBegin
,case
when day(#date) <= 14
then EOMONTH(#date, -2)
when day(#date) >= 15
then EOMONTH(#date, -1)
end as RollingYearEnd
into #RollingYear
Then if you need the values formatted in that specific way when you call them, you could either complicate that temp table above a bit more, or just make another little table
Select cast(concat(datepart(yyyy,RollingYearBegin), datepart(q,RollingYearBegin), datepart(MM,RollingYearBegin)) as bigint) as YearBegin
,cast(concat(datepart(yyyy,RollingYearEnd), datepart(q,RollingYearEnd), datepart(MM,RollingYearEnd)) as bigint) as YearEnd
into #YYYYQMM
From #RollingYear
Then, in your query, it would be:
Select *
From Table
WHERE Spend_Period_YYYYQMM BETWEEN (Select YearBegin from #YYYYQMM) AND (Select YearEnd from #YYYYQMM)
SELECT
DAY(table_A.PaymentDate) as date1 ,
(CASE
WHEN MONTH(table_A.PaymentDate) = MONTH(CURRENT_TIMESTAMP)
THEN CAST(SUM(table_A.Total_Amount) As INT)
ELSE 0
END) AS This_month_CNT,
(CASE
WHEN MONTH(table_A.PaymentDate) = MONTH(CURRENT_TIMESTAMP) - 1
THEN CAST(SUM(table_A.Total_Amount) AS INT)
ELSE 0
END) AS last_month_CNT
FROM
Tbl_Pan_Paymentdetails table_A
FULL OUTER JOIN
Tbl_Pan_Paymentdetails table_B ON table_A.PaymentDate = table_B.PaymentDate
WHERE
YEAR(table_A.PaymentDate) = YEAR(CURRENT_TIMESTAMP)
AND table_A.PaymentDate >= DATEADD(MONTH, -2, GETDATE())
GROUP BY
DAY(table_A.PaymentDate),
MONTH(table_A.PaymentDate)
ORDER BY
DAY(table_A.PaymentDate) ;
Not sure I fully understand.
WHERE YEAR(table_A.PaymentDate) = YEAR(CURRENT_TIMESTAMP) AND
table_A.PaymentDate >= DATEADD(MONTH, -2, GETDATE())
Here you are (1) comparing the Year elements of your payment date with CURRENT_TIMESTAMP, and (2) making sure the payment date is greater than the last 2 months based on GETDATE()?
Not sure why you are using both CURRENT_TIMESTAMP and GETDATE(). Either way, I think the second part of that WHERE statement does what you want.
If the current date is January 31, 2015, your currently logic will not return any records from December 2014. The first part of your where statement is filtering them out. If you really want the last 2 months, remove the following from the WHERE statement
YEAR(table_A.PaymentDate) = YEAR(CURRENT_TIMESTAMP) AND
Wondering if you can help a little with the syntax here. Trying to set a variable as a month value dependent upon whether it is past the 25th day of the month.
If it is, then it uses the current month (e.g. the variable 'month' will be 10 if the date is 28th October, but will be 9 if it's the 24th October). So far I've got the following:
select a
case
when (SELECT DAY(GETDATE()) >= 25
then a = (SELECT MONTH(GETDATE()))
else a = (SELECT MONTH(GETDATE()) - 1)
end
I understand you can't use less than or greater than signs, as case statements are only for evaluations (=)? Can anyone suggest another way of doing this?
Thanks.
select #your_variable = case when DAY(GETDATE()) = 25
then MONTH(GETDATE())
else MONTH(GETDATE()) - 1
end
As far as I understand, you need this kind of usage;
select
case
when DAY (GETDATE()) = 25 then MONTH(GETDATE())
else MONTH(GETDATE()) - 1
end
You can use > and < operators with CASE. Is the following what you expected?
SELECT CASE WHEN DAY(GETDATE()) > 24
THEN MONTH(GETDATE())
ELSE MONTH(GETDATE()) + 1
END AS [a]
IF
DAY(GETDATE()) <=25
SELECT DATEADD(month,-1,CURRENT_TIMESTAMP)
Cant figure out what im doing wrong:
I have a table that has a start time and end time column and im trying to figure out the sum of time in minutes for this months portion only.
Eg.
Start date: 27-02-13
End Date: 03-03-13
Over all minutes= 5760
Minutes for March only: 4320
This is my query but its not working properly, i tried to add a where clause to look back the last two months for pervious time entrys. What am I doing wrong?
SELECT
isnull((Sum(DATEDIFF(minute,
CASE when datetime5 < dateadd(mm,datediff(mm,0,GetDate()), 0)
THEN dateadd(mm,datediff(mm,0,GetDate()), 0)
ELSE datetime5 END,
CASE when datetime6 > dateadd(mm,datediff(mm,0,GetDate())+1, 0)
THEN dateadd(mm,datediff(mm,0,GetDate())+1, 0)
ELSE datetime6 END))/60.0),0 )
as Minutes , 0
FROM AllUserData
WHERE tp_ListID in (select tp_ID from Lists where tp_Title = 'D1 Downtime')
and datetime5 >=dateadd(mm,datediff(mm,0,GetDate())-2, 0);
See this SQL Fiddle.
with month_start_finish (month_start, month_finish) as
(
-- here you get the beginning of the current month and the next month
-- needed for filters and calculations
select
dateadd(month, datediff(month, 0, getdate()), 0),
dateadd(month, datediff(month, 0, getdate()) + 1, 0)
)
select start_date, finish_date,
-- here you calculate your minutes
datediff(minute,
case when start_date > month_start then start_date else month_start end,
case when finish_date < month_finish then finish_date else month_finish end
) as minutes_in_this_month
from test, month_start_finish
where start_date < month_finish -- here you remove any periods that
and finish_date > month_start -- are not related to the current month
Note how it works correctly even if the the period is longer than a month.
Select all records that end after the beginning of this month.
Then, the portion of each record that is in this month can be obtained with something like this (pseudo-code):
end - max(start,start_of_this_month)
Take the start of the period or the start of this month, whichever is later.
This should help you simplify your query, I think. Here is the basic idea (pseudo-code again, as I don't know the nuances of SQL Server date operations).
select sum(elapsed_time) from (
select end - max(start,start_of_this_month) as elapsed_time
from your table
where end > start_of_this_month
) time_periods
I have a table
tblAppointment {
App_ID,
App_Date,
User_ID }
I currently have a statement that returns number of appointments grouped by year and month
SELECT
YEAR(App_Date) AS Year,
MONTH(App_Date) AS Month,
count(*) AS "No of Appointments"
FROM
tblapplication
GROUP BY
YEAR(App_Date),
MONTH(App_Date)
Im not sure how to write a select statement to return it with headings {time frame, No of applications}, and then have data in
row 1: time frame = week thus far, no of app = x.
row 2: time frame = month thus far, no of app = y.
ro3 3: time frame = year so far, no of app - z.
I would like to know how many appointments there are for 1. the current week, 2. the current month. 3. the current year, And have each result in its own row.
Any help in the right direction would be greatly appreciated. The actual problem is much greater than this but believe I have simplified it to the crux of the matter for now.
If you're okay with the results on one row, it's relatively easy:
select count(case when datepart(wk, App_Date) = datepart(wk, getdate())
then 1 end) as WeekSofFar
, count(case when datepart(m, App_Date) = datepart(m, getdate())
then 1 end) as MonthSofFar
, count(*) as YearSoFar
from tblApplications
where datepart(y, App_Date) = datepart(y, getdate())
If the separate rows are a must-have, try something like:
select 'WeekSoFar' as Period
, (
select count(*)
from tblApplications
where datepart(y, App_Date) = datepart(y, getdate())
and datepart(wk, App_Date) = datepart(wk, getdate())
) as NumberOfApps
union all
select 'MonthSoFar'
, (
select count(*)
from tblApplications
where datepart(y, App_Date) = datepart(y, getdate())
and datepart(m, App_Date) = datepart(m, getdate())
)
union all
select 'YearSoFar'
, (
select count(*)
from tblApplications
where datepart(y, App_Date) = datepart(y, getdate())
)
I assumed SQL 2008 as you didn't specify.
SELECT
X.TimePeriod,
Count(
CASE X.Which
WHEN 3 THEN
CASE
WHEN App_Date > DateAdd(Day, -DatePart(Weekday, GetDate()), GetDate())
THEN 1
END
WHEN 2 THEN CASE WHEN Month(App_Date) = Month(GetDate()) THEN 1 END
ELSE 1 END
) [No of Appointments]
FROM
tblApplication
CROSS JOIN (
VALUES (1, 'Year to date'), (2, 'Month to date'), (3, 'Week to date')
) X (Which, TimePeriod)
WHERE
App_Date < Convert(date, GetDate() + 1)
AND App_Date >= DateAdd(Year, DateDiff(Year, '19000101', App_Date), '19000101')
GROUP BY
X.Which,
X.TimePeriod
ORDER BY
X.Which
If you have a lot of data in your table and an index on App_Date, this query will perform hugely better than one using date functions on the entire table without filtering.
I also have an embedded assumption that your App_Date values have no time portion (they are all set to 12am). If this is not true please let me know so I can modify case 3 to be correct.
If anyone wants to try the code here's some setup:
CREATE TABLE tblApplication (
App_Date datetime
)
INSERT tblApplication
VALUES
('2/1/2012'), ('2/5/2012'), ('2/10/2012'), ('1/2/2012'), ('1/9/2012'),
('1/15/2012'), ('1/28/2012'), ('12/1/2012'), ('12/5/2012'), ('12/10/2012'),
('11/2/2012'), ('11/9/2012'), ('11/15/2012'), ('11/28/2012')
Sorry about not using 'YYYYMMDD', I wasn't thinking when I typed it out.