How to loop through each value from CTE - sql

I am working only on queries for now and not TSQL. So i want to fetch my results thru query.
I want to display first monday of every month. I have comeup with a query to find the dates of every month in tht year thru CTE. (So my CTE gives 12 dates).
Now , i want to check each date and get the nearest monday. That would be my first monday of that month.
I am unable to go thru each date in my query.
How can i work on this result set?
Thanks
with
Wk_num
as
(select 1 as n
union all
select n=n+1
from wk_num
where n <12)
select dateadd(month,n,getdate()) from wk_num

A relatively simple method use modulo arithmetic:
with cte as (
select convert(date, '2019-01-01') as dte
union all
select dateadd(month, 1, dte)
from cte
where dateadd(month, 1, dte) < '2020-01-01'
)
select dte,
dateadd(day, (9 - datepart(weekday, dte)) % 7, dte) as first_monday
from cte;
This assumes that the weekday numbers starts with 1 on Sunday.

Here is the code to actually get the first day of each month and the first Monday of each month (This doesn't use any complex functions just simple date functions):
with Wk_num as
(select 1 as n
union all
select n=n+1 from wk_num where n <12)
select dateadd(month,n,datefromparts(year(getdate()),month(getdate()),1)) as FirstDay,
dateadd(day,iif(9-datepart(dw,dateadd(month,n,datefromparts(year(getdate()),month(getdate()),1)))>7,2-datepart(dw,dateadd(month,n,datefromparts(year(getdate()),month(getdate()),1))),9-datepart(dw,dateadd(month,n,datefromparts(year(getdate()),month(getdate()),1)))),dateadd(month,n,datefromparts(year(getdate()),month(getdate()),1))) as FirstMonday
from wk_num

Related

Perform a monthly distinct beneficiary count between 1/1/2018 -12/31/2020 based on two date fields

Perform a monthly distinct beneficiary count between 1/1/2018 -12/31/2020 using Microsoft SQL Server.
Below is my code but I have to change it for every month, is there any way to group by each month from 2018 to 2020 with 2 different date fields?
SELECT COUNT(distinct BEN_ID)
FROM LDS_2017andbeyond
WHERE
[DTE_FIRST_SVC] between '2018-01-01' and '2018-01-31'
AND
[DTE_LAST_SVC] between '2018-01-01' and '2018-01-31'
One simple way to group dates on the same month is the EOMONTH function. It'll return the last day of the month for a date.
SELECT
FORMAT(EOMONTH([DTE_FIRST_SVC]), 'yyyy-MM') AS MONTH_FIRST_SVC
, FORMAT(EOMONTH([DTE_LAST_SVC]), 'yyyy-MM') AS MONTH_LAST_SVC
, COUNT(DISTINCT BEN_ID) AS TOTAL_UNIQUE_BEN_ID
FROM LDS_2017andbeyond
WHERE [DTE_FIRST_SVC] BETWEEN '2018-01-01' AND '2020-12-31'
AND [DTE_LAST_SVC] BETWEEN '2018-01-01' AND '2020-12-31'
GROUP BY EOMONTH([DTE_FIRST_SVC]), EOMONTH([DTE_LAST_SVC])
ORDER BY MONTH_FIRST_SVC DESC, MONTH_LAST_SVC DESC
One solution would be to use a recursive CTE. You start with an anchor query and union it to itself with a DATEADD function in the recursive portion. This solution will give you every month month, even if the count is 0, as opposed to just grouping on the data, which will omit any months that arn't present.
Something like:
WITH CTE_Date AS (
SELECT CAST('01/01/2018' AS DATE) AS GroupMonth -- Start Date. Set as far back as necessary. Can use a DATEADD() to make dynamic.
UNION ALL
SELECT DATEADD(month, 1, GroupMonth) AS GroupMonth
FROM CTE_Date
WHERE DATEADD(month, 1, GroupMonth) < '12/31/2020' -- End Date. Remove the where to go to current.
)
SELECT
COUNT(distinct BEN_ID),
CAST(MONTH(d.GroupMonth) AS VARCHAR(2)) + '-' + CAST(YEAR(d.GroupMonth) AS VARCHAR(4)) AS Dt
FROM
LDS_2017andbeyond lds
LEFT OUTER JOIN CTE_Date d ON
MONTH(lds.[DTE_FIRST_SVC]) = MONTH(d.GroupMonth)
AND
YEAR(lds.[DTE_LAST_SVC]) = YEAR(d.GroupMonth)
GROUP BY
CAST(MONTH(d.GroupMonth) AS VARCHAR(2)) + '-' + CAST(YEAR(d.GroupMonth) AS VARCHAR(4))

How to insert weekdays names in sql column based on given date?

I wanted to know how to insert weekday names in a column in sql server based on the current date. I want to insert the days until current day. For example, I want something as per the following column for today (2014-12-04),
day_of_week
-----------
Monday
Tuesday
Wednesday
Thursday
On next Tuesday(2014-12-09), I want to display,
day_of_week
-----------
Monday
Tuesday
Let me know how can I do this in sql server 2012?
Any help will be appreciated.
Thanks.
Something like this should work.
select datename(weekday,n)
from (VALUES (0),(1),(2),(3),(4),(5),(6)) wdays(n)
where n < datepart(weekday,'20141209') - 1
And if you're not using sql 2008 and up you can do
select datename(weekday,n)
from (select 0
union all
select 1
union all
select 2
union all
select 3
union all
select 4
union all
select 5
union all
select 6
) wdays(n)
where n < datepart(weekday,'20141209') - 1
It can also be done using a recursive CTE:
;WITH cte AS
(
SELECT DATENAME(weekday,getdate()) AS NameOfDay,
DAY(getdate()) AS NumberOfDay,
getdate() AS curDate
UNION ALL
SELECT DATENAME(weekday,DATEADD(day, -1, curDate)) As NameOfDay,
DAY(DATEADD(day, -1, curDate)) AS NumberOfDay,
DATEADD(day, -1, curDate) AS curDate
FROM cte
WHERE DAY(GETDATE()) - DAY(DATEADD(day, -1, curDate)) >= 0
)
SELECT NameOfDay
FROM cte
ORDER BY NumberOfDay

Number of days in quarter, SQL Server

I want to calculate the number of days per-quarter if start date and finish dates are given.
for example, one table has two columns, start date and finish date.
start date = 1st september and finish is 14th november.
I want to calculate the number of days present in between these two days that are present in each quarter
Q3 - 30 days
Q4 - 45 days (for this scenario)
Regards.
declare #StartDate date='2012-09-01';
declare #EndDate date='2012-11-14';
select CEILING(month(dateadd(q,datediff(q,0,dateadd(dd,number ,#StartDate)),0))/3.0) as QuarterNo,
COUNT(*) as 'number of days'
from master..spt_values
where type='p'
and dateadd(dd,number ,#StartDate)<=#EndDate
group by dateadd(q,datediff(q,0,dateadd(dd,number ,#StartDate)),0)
SQL fiddle demo
You can use a recursive query to get this. This generates the list of dates between your start and end date and then gets the count of days per quarter:
;with cte (start, enddate) as
(
select startdate, enddate
from yourtable
union all
select dateadd(dd, 1, start), enddate
from cte
where dateadd(dd, 1, start) <= enddate
)
select datepart(q, start) Quarter, count(datepart(q, start)) NoDays
from cte
group by datepart(q, start)
See SQL Fiddle with Demo

Select last 30 days with a sql query

I am looking for the number of Mon,Tues, Wed, Thur, Fri, Sat, Sun in the past 30 days. Can I select the last 30 days date and day of week without an actual database table? Something like
SELECT --everything between
convert(date,GETDATE()), DATENAME(DW, GETDATE())
--and
convert(date,GETDATE() - 30), DATENAME(DW, GETDATE())
You can use a recursive CTE:
;WITH CTE AS
(
SELECT convert(date,GETDATE()) sDate, DATENAME(DW, GETDATE()) sDayofWeek
UNION ALL
SELECT DATEADD(DAY,-1,sDate), DATENAME(DW, DATEADD(DAY,-1,sDate))
FROM CTE
WHERE sDate > GETDATE()-29
)
SELECT * FROM CTE
WITH cteCount AS (
SELECT DATENAME(dw, GETDATE()) dw, 1 ix
UNION ALL
SELECT DATENAME(dw, DATEADD(d, -ix, GETDATE())), ix+1 FROM cteCount WHERE ix<30
)
SELECT dw, COUNT(1) cnt FROM cteCount GROUP BY dw
A couple solutions:
SELECT ... From ... WHERE date > DATEADD(year, -1, GETDATE())
Also, I think this statement will work with MySQL:
select date_sub(now(),interval 30 day)as Datebefore30days;
Well, there are a couple of ways to do it.
You could fill a temp table, using a loop and INSERT statements, and then select the contents of the table. You could create a table-valued UDF to do this, in fact.
You could also create 30 SELECT statements, and UNION them all together. But, frankly, I think you're better off with option 1.
ETA: Thinking about it, if all you want is the number of each day of the week in the past 30 days, you can probably do that just with some math, without returning 30 records.
There are 4 instances of each day of the week in any 30 day period, plus 2 extra days. So all you really need is to know what day of the week the first day in your period is, and the second day. Those days of the week have 5 instances.
I'm pretty lazy and just load a temp table and then do a group by select on that temp table
DECLARE #tmpDates TABLE (calDate DATETIME)
DECLARE #beginDate DATETIME
SET #beginDate = DATEADD(day,-30,GETDATE())
WHILE #beginDate < GETDATE()
BEGIN
INSERT INTO #tmpDates ([calDate]) VALUES (#beginDate)
SET #beginDate = DATEADD(DAY,1,#beginDate)
END
SELECT DATEPART(dw,[calDate]) AS [weekDay], COUNT(1) AS [dayCount]
FROM #tmpDates
GROUP BY DATEPART(dw,[calDate])
Number of times each day of the week got hit in the last 30 days:
SELECT DATENAME(dw,GETDATE())+' 5 times' as results
UNION ALL
SELECT DATENAME(dw,DATEADD(day,-1,GETDATE()))+' 5 times'
UNION ALL
SELECT DATENAME(dw,DATEADD(day,-2,GETDATE()))+' 4 times'
UNION ALL
SELECT DATENAME(dw,DATEADD(day,-3,GETDATE()))+' 4 times'
UNION ALL
SELECT DATENAME(dw,DATEADD(day,-4,GETDATE()))+' 4 times'
UNION ALL
SELECT DATENAME(dw,DATEADD(day,-5,GETDATE()))+' 4 times'
UNION ALL
SELECT DATENAME(dw,DATEADD(day,-6,GETDATE()))+' 4 times'
This really is about dividing 30 by 7
This gives me
results
Thursday 5 times
Wednesday 5 times
Tuesday 4 times
Monday 4 times
Sunday 4 times
Saturday 4 times
Friday 4 times

How can I count and group by between two date?

For example,
the start date = '20100530' and
the end date = '20100602'
How can I write a SQL to display the below result?
month: may, 2 days
month: June, 2 days
Use a recursive CTE to generate all of the dates between the start and end, and then do a simple group and count (caution, not tested, but should be close if not exactly right):
with dates (the_date) as (
select #start_date
UNION ALL
select dateadd(dd, 1, the_date) from dates where the_date <= #end_date
)
select
datepart(mm, the_date) month,
count(*) num_days
from
dates
group by
datepart(mm, the_date)
TBH, you really need to provide more schema and information about the source data. However, given what little we know, you should be able to write:
Select DateName('month', [Date]) As Month
, Cast(DateDiff(d, #StartDate, #EndDate) As varchar(10) - 1) + ' days'
From Table
Where [Date] Between #StartDate And #EndDate
What we'd need to know to refine our solutions is exactly how 2 days is supposed to be calculated. Is it the days between the start and end date? Is it the day of the second parameter?