(MS SQL Server) Setting Case Statement Column Name With A Formula - sql-server

I would like to use a formula to set column names for case statements but the straightforward approach is not accepted. Here is the example of what I am attempting to do - is there a way to make this work?
select
ID
-- Pull DeDupe_Decision_Count for that ID last month (EOM_Date), where each ID has multiple rows in the data set for each data month
, sum(case when EOM_Date = CONVERT(DATE, DATEADD(s,-1,DATEADD(m,-1,DATEADD(mm, DATEDIFF(m,0,GETDATE()),0))))
then DeDupe_Decision_Count end)
as CONVERT(DATE, DATEADD(s,-1,DATEADD(m,-1,DATEADD(mm, DATEDIFF(m,0,GETDATE()),0))))
-- Pulling the same for two months ago
, sum(case when EOM_Date = CONVERT(DATE, DATEADD(s,-1,DATEADD(m,-2,DATEADD(mm, DATEDIFF(m,0,GETDATE()),0))))
then DeDupe_Decision_Count end)
as CONVERT(DATE, DATEADD(s,-2,DATEADD(m,-1,DATEADD(mm, DATEDIFF(m,0,GETDATE()),0))))
-- Pulling the same for three months ago
, sum(case when EOM_Date = CONVERT(DATE, DATEADD(s,-1,DATEADD(m,-3,DATEADD(mm, DATEDIFF(m,0,GETDATE()),0))))
then DeDupe_Decision_Count end)
as CONVERT(DATE, DATEADD(s,-2,DATEADD(m,-1,DATEADD(mm, DATEDIFF(m,0,GETDATE()),0))))
-- Would like to do the same for 12-24 months
from Mytable
group by ID
I would like to use the formula instead of a static date because this will be updated monthly and include 12-24 months of data. I could use variables and just name the columns last month, 2 months ago, 3 months ago, etc. but I would rather the column name be the date of the month.

This requires dynamic query
DECLARE #sql VARCHAR(max)
SET #sql = 'select
case
when EOM_Date = CONVERT(DATE, DATEADD(s,-1,DATEADD(mm, DATEDIFF(m,0,GETDATE()),0)))
then ''DeDupe_Decision_Count''
end as '+ Quotename(CONVERT(DATE, Dateadd(s, -1, Dateadd(mm, Datediff(m, 0, Getdate()), 0))))
PRINT #sql
exec(#sql)

Related

Rolling 3 Years of data with desired format

Start Date = 2016-03-01
End Date = 2019-02-15
I need the query/Function which should retrive the below result based on start and end date. I have a query to frame the result .But i need some query/function to retrive result set in 5 to 10 sec with performance
You mean like
SELECT * FROM table WHERE [start date]>='2016-03-01' AND [end date]<='2019-02-15'
Am I missing something? This seems too simple to ask as a question
If your problem is performance perhaps consider indexing start and end date columns, and if you're only getting a couple other values from the table include those in the index too.
CREATE INDEX IX_table_startdate_enddate
ON schema.table ([start date], [end date])
INCLUDE (othercolumn1, othercolumn2);
It means that queries like:
SELECT othercolumn1, othercolumn2 FROM table
WHERE [start date]>='2016-03-01' AND [end date]<='2019-02-15'
Can be answered from the index without having to connect the index to the table to pull the desired data
If you still can't wrong enough out of it after that perhaps you have a design flaw in your app; pilling a db for a massive amount of data in a tight interval could be solved another way like only emitting events when data actually changes
It's just occurred to me you're probably looking for a query to generate that result set from nothing. In sqlserver we can use a recursive cte:
DECLARE #dateFrom DATE = '2016-03-01'
DECLARE #dateTo DATE = '2019-02-15'
with d as (
SELECT #datefrom as m
UNION ALL
SELECT DATEADD(MONTH,1,reqDate)
FROM d
WHERE DATEADD(MONTH,1,reqDate) <= #dateto
),
SELECT
CONCAT(
DATENAME(MONTH, #dateFrom), ' ',
CASE WHEN MONTH(d.m) < MONTH(#dateFrom) THEN YEAR(d.m) - 1 ELSE YEAR(d.m) END, '-',
DATENAME(MONTH, #dateTo), ' ',
CASE WHEN MONTH(d.m) < MONTH(#dateFrom) THEN YEAR(d.m) ELSE YEAR(d.m) +1 END
) as range,
MONTH(d.m) as month,
d.m as startdate,--do not use spaces in column names
CASE WHEN #dateTo < EOMONTH(d.m) then #dateTo ELSE EOMONTH(d.m) END as enddate --or dateadd 1 month then dateadd -1 day if you don't have eomonth
FROM d
OPTION(MAXRECURSION 0);
Same format with your result try it.
DECLARE #StartDate DATETIME, #EndDate DATETIME
SET #StartDate = '2016-03-01'
SET #EndDate = '2038-02-15'
;WITH CTEs AS
(
SELECT #StartDate as [Start Date]
UNION ALL
SELECT DATEADD(MONTH,1,[Start Date])
FROM CTEs WHERE DATEADD(MONTH,1,[Start Date]) <= #EndDate
)
SELECT
CONCAT(
DATENAME(MONTH, #StartDate), ' ',
CASE WHEN MONTH([Start Date]) < MONTH(#StartDate) THEN YEAR([Start Date]) - 1 ELSE YEAR([Start Date]) END, '-',
DATENAME(MONTH, #EndDate), ' ',
CASE WHEN MONTH([Start Date]) < MONTH(#StartDate) THEN YEAR([Start Date]) ELSE YEAR([Start Date]) + 1 END
) AS [Range],
MONTH([Start Date]) AS [Month],
CONVERT(VARCHAR(10),[Start Date],101) AS [Start Date],
CONVERT(VARCHAR(10),(CASE WHEN [Start Date] <> DATEFROMPARTS(YEAR(#EndDate),MONTH(#EndDate),1)
THEN EOMONTH([Start Date])
ELSE #EndDate
END),101) AS [End Date]
FROM CTEs
OPTION(MAXRECURSION 0);

Group by Week in Transact-SQL

I'm attempting to get a count of items within a 7-day period, while still being able to report the first date of said seven day period. The closest I've come so far is
WITH w (w, n)
AS
(
SELECT MIN(CAST(CreatedDate AS DATE))
OVER(
PARTITION BY DATEPART(WEEK, CAST(CreatedDate AS DATE))
ORDER BY DATEPART(WEEK, CAST(CreatedDate AS DATE))
)
,COUNT(*)
FROM dbo.Tbl
WHERE
CreatedDate >= CAST(DATEADD(MONTH,-6,GETDATE()) AS DATE)
GROUP BY CAST(CreatedDate AS DATE)
)
SELECT w.w AS [Week of], SUM(w.n) AS [Items]
FROM w
GROUP BY w.w
ORDER BY 1 DESC
But this unfortunately does not work for the first or last week of the year, and will not work if the date range includes more than one year.
Is there a way to group by a seven day period while still being able to get the first date in said period?
You can use the following to get your weeks based on your date value. You will need to play around with the days added to get the right days of the week for your situation, but you will always be guaranteed the same start and end day. In this case the week starts on Sunday and ends on Saturday:
declare #Date datetime
set #Date = '20160802'
select #Date - DATEPART(dw, #Date) + 1 as FirstDateOfWeek
,#Date + (7 - DATEPART(dw, #Date)) as LastDateOfWeek
For a fully bulletproof solution you could add in logic to check and use ##DATEFIRST and assign the adjustment values accordingly.
Once you have your week start value, you can use that in your group by:
select CreatedDate - DATEPART(dw, #Date) + 1 as FirstDateOfWeek
,count(*)
from tbl
group by CreatedDate - DATEPART(dw, #Date) + 1
order by FirstDayOfWeek

I need a SQL query for top 5 upcoming birthdays and recent previous birthdays

I have a SQL query to the today birthdays user.
SELECT * FROM Table_EmployeeInfo WHERE MONTH(DOB) = MONTH(getdate()) and day(DOB)=day(getdate())
But I need top 5 users SQL query for upcoming birthdays, not today users birthday, it should be tomorrow, if no body will tomorrow means take from next days.
Also, I need recent top 5 user birthdays gone, not today users birthday.
You can use ROW_NUMBER() function like this:
SELECT * FROM(
SELECT t.*,
row_number() OVER(order by datepart(mm,t.DOB),datepart(dd,t.DOB)) as rnk
FROM Table_EmployeeInfo t
where t.DOB > getdate())
WHERE rnk <= 5
Messy, but it works:
select * from (
SELECT datediff(dd,DATEADD(yyyy, DATEDIFF(yyyy, DOB, GETDATE()), DOB),getdate()) as daysSinceBD
, DENSE_RANK() OVER( ORDER BY datediff(dd,DATEADD(yyyy, DATEDIFF(yyyy, DOB, GETDATE()), DOB),getdate()) desc ) as Ranking
, * FROM Table_EmployeeInfo
where datediff(dd,DATEADD(yyyy, DATEDIFF(yyyy, DOB, GETDATE()), DOB),getdate()) < 0
)x where ranking <=5 order by 1 desc
Try this below for select upcoming 5 birthdays
SELECT top 5 * FROM Table_EmployeeInfo WHERE CONVERT(DATETIME,
CONVERT(VARCHAR(10), DOB, 111)) > CONVERT(DATETIME,
CONVERT(VARCHAR(10), GETDATE(), 111)) order by dob
This one for past 5 birthdays
SELECT top 5 * FROM Table_EmployeeInfo WHERE CONVERT(DATETIME,
CONVERT(VARCHAR(10), DOB, 111)) < CONVERT(DATETIME,
CONVERT(VARCHAR(10), GETDATE(), 111)) order by dob
I have tested the codes against an old database with Date of Birth data, and they worked.
Since I do not have Date of Birth exact time (hour and etc), I did not handle the situation where I have 10 or more people with birthday on same day.
I have used the "OFFSET - FETCH" approach, that can help you if you need some kind of pagination in the future.
I do not know your scenario and/or data model, but if this query is to be run frequently and database tends to be big, I'd suggest you to consider a computed column "Anniversary", or anything compliant with your database names convention. This is because you have to use a lot of functions to achieve what you need, and this is not good for performance. With a computed column, you have less complexity on queries and a better indexing strategy.
Please find below the code I have written, and let me know if it helps.
-- LAST 5 BIRTHDAYS GONE, NOT FROM TODAY
SELECT
*
FROM
Table_EmployeeInfo AS T
WHERE
DATEPART(DD, T.DOB) < DATEPART(DD, GETDATE())
AND
DATEPART(MM, T.DOB) <= DATEPART(MM, GETDATE())
ORDER BY
DATEPART(MM, T.DOB) DESC
,DATEPART(DD, T.DOB) DESC
OFFSET 0 ROWS FETCH NEXT 5 ROWS ONLY
-- NEXT 5 BIRTHDAYS TO COME, NOT FROM TODAY
SELECT
*
FROM
Table_EmployeeInfo AS T
WHERE
DATEPART(DD, T.DOB) > DATEPART(DD, GETDATE())
AND
DATEPART(MM, T.DOB) >= DATEPART(MM, GETDATE())
ORDER BY
DATEPART(MM, T.DOB) ASC
,DATEPART(DD, T.DOB) ASC
OFFSET 0 ROWS FETCH NEXT 5 ROWS ONLY
P.S.: OFFSET-FETCH will work only on SQL Server 2012 or above. If you are using and older version, try using TOP(5) instead.

SQL Server Get Operational days from Period

I have the table Seasons:
YEAR DATE_FROM DATE_TO
2013 2013-04-21 2013-11-14
2014 2014-04-03 2014-12-03
I need a query that gets as parameters the #year,#month,#type and it returns for
#type=
1)the days that are contained within the period for the #month of the #year
2)the days that have passes since the beginning of the period and until the end of the #month of the #year
Examples:
#type=1, #month= 5, #year = 2013 should returns 31 days
#type=2, #month= 5, #year = 2013 should returns 40 days
Thanx in advance!
You need to do two things. The first is to find the matching row. The second is to summarize as you want. I think the best way to summarize is by transforming #year and #month to the beginning of the month. Then add one month and subtract a day for the end of the month. I think the following captures the logic:
select (case when #type = 1
then datediff(day, thedate,
(case when datediff(day, date_from, dateadd(month, 1, thedate)) > date_to
then dateadd(day, -1, datediff(day, date_from, dateadd(month, 1, thedate)) )
else date_to
end)
)
when #type = 2
then datediff(day, date_from, dateadd(month, 1, thedate)) - 1
end)
from seasons s cross join
(select cast(cast(#year*100000 + #month*100 + 1 as varchar(255)) as date) as thedate
where #year * 100 + #month between year(s.date_from) * 100 + month(s.date_from) and
year(s.date_to) * 100 + month(s.date_to)

Retrieve rows from a certain day but only in a certain hour

I have a query which returns all of the rows for three days ago:
SELECT * FROM table2
WHERE CONVERT(date, given_schedule)
= CONVERT(date, DATEADD(d, -3, GETDATE()))
But I want to know limit the rows to only the hour relative to the current time. So for example, it is currently after 9:00 PM then I only want to retrieve the rows that occurred three days ago and between 9:00 and 10:00 PM.
SELECT columns FROM dbo.table2
WHERE
CONVERT(DATE, given_schedule)
= CONVERT(DATE, DATEADD(DAY, -3, CURRENT_TIMESTAMP))
AND
DATEPART(HOUR, given_schedule)
= DATEPART(HOUR, CURRENT_TIMESTAMP);
To address #Habo's point, you could also do:
DECLARE #s SMALLDATETIME = CURRENT_TIMESTAMP;
SET #s = DATEADD(DAY, -3, DATEADD(MINUTE, -DATEPART(MINUTE, #s), #s));
SELECT columns FROM dbo.table2
WHERE given_schedule >= #s
AND given_schedule < DATEADD(HOUR, 1, #s);
This is, of course, most useful if there is actually an index with given_schedule as the leading column.
You could use the DATEDIFF function and pass in hour as the datepart argument.
SELECT * FROM table2 WHERE DATEDIFF(hour, GETDATE(), given_schedule,) BETWEEN 0 AND 1
See this for more info.

Resources