找到下 n 个星期的最佳方法是什么
What is the best way to find next n week days
我从我提出的以下问题中得到了以下代码:
我需要根据今天的日期在我的 table 中找到接下来的 4 个工作日对应的 Day-Of-Week 即,如果今天是 2015-01-24
,结果应该是 1/24, 1/31, 2/7, 2/14
Saturdays
.
TABLE
示例查询
create table #t
(
jobId int,
personId int,
frequencyVal varchar(10)
);
insert into #t values (1,100,'Mondays'),(2,101,'Saturdays');
WITH cte(n) AS
(
SELECT 0
UNION ALL
SELECT n+1 FROM cte WHERE n < 3
)
select #t.jobId, #t.personId, #t.frequencyVal, STUFF(a.d, 1, 1, '') AS FutureDates
from #t
cross apply (SELECT CASE #t.frequencyVal
WHEN 'SUNDAYS' THEN 1
WHEN 'MONDAYS' THEN 2
WHEN 'TUESDAYS' THEN 3
WHEN 'WEDNESDAYS' THEN 4
WHEN 'THURSDAYS' THEN 5
WHEN 'FRIDAYS' THEN 6
WHEN 'SATURDAYS' THEN 7
END)tranlationWeekdays(n)
cross apply (select ',' + CONVERT(varchar(10), CONVERT(date,dateadd(WEEK, cte.n,CONVERT(DATE, DATEADD(DAY, (DATEPART(WEEKDAY, GETDATE()) + tranlationWeekdays.n) % 7, GETDATE()))))) from cte FOR XML PATH('')) a(d);
drop table #t;
预期结果
这是我认为比较简单的方法,我觉得很符合你的要求
请注意,我已将您的 frequency_val
列更改为代表 SQL 服务器角度的星期几的整数,并添加了一个计算列来说明如何轻松地从中导出日期名称。
/*************************************************/
--Set up our sample table
/*************************************************/
declare @t table
(
jobId int,
personId int,
--frequencyVal varchar(10) -- why store a string when a tiny int will do.
frequency_val tinyint,
frequency_day as datename(weekday,frequency_val -1) + 's'
)</p>
<p>insert into @t
values
(1,100,1),--'Mondays'),
(2,101,6),--'Saturdays');
(3,101,7),--'Sundays');
(4,100,2)--'Tuesdays'),
--select * from @t</p>
<p>/*************************************************/
--Declare & initialise variables
/*************************************************/
declare @num_occurances int = 4
declare @from_date date = dateadd(dd,3,getdate()) -- this will allow you to play with the date simply by changing the increment value</p>
<p>/*************************************************/
-- To get a row for each occurance
/*************************************************/
;with r_cte (days_ahead, occurance_date)
as (select 0, convert(date,@from_date,121)
union all
select r_cte.days_ahead +1, convert(date,dateadd(DD, r_cte.days_ahead+1, @from_date),121)
from r_cte
where r_cte.days_ahead < (7 * @num_occurances) -1
)
select t.*, r_cte.occurance_date
from
@t t
inner join r_cte
on DATEPART(WEEKDAY, dateadd(dd,@@DATEFIRST - 1 ,r_cte.occurance_date)) = t.frequency_val</p>
<p>/*************************************************/
--To get a single row with a CSV of every occurance
/*************************************************/
;with r_cte (days_ahead, occurance_date)
as (select 0, convert(date,@from_date,121)
union all
select r_cte.days_ahead +1, convert(date,dateadd(DD, r_cte.days_ahead+1, @from_date),121)
from r_cte
where r_cte.days_ahead < (7 * @num_occurances) -1
)
select
t.*,
STUFF( (select ', '
+ convert(varchar(2),datepart(month,occurance_date),0) + '/'
+ convert(varchar(2),datepart(day,occurance_date),0) as occurance
from r_cte
where DATEPART(WEEKDAY, dateadd(dd,@@DATEFIRST - 1 ,r_cte.occurance_date)) = t.frequency_val
FOR XML PATH (''),TYPE).value('.','varchar(30)')
,1,2,'') occurance_date -- rest of STUFF() function
from
@t t
</pre>
获取当前月份的第一天。
DECLARE @FIRSTDAY DATE=DATEADD(month, DATEDIFF(month, 0, GETDATE()), 0)
创建 table 并插入值
create table #t
(
jobId int,
personId int,
frequencyVal varchar(10)
);
insert into #t values (1,100,'Mondays'),(2,101,'Saturdays');
您可以根据自己的情况使用以下任一查询。
QUERY 1 : Select 特定工作日当月的前 4 周
-- Gets the first day of current month
DECLARE @FIRSTDAY DATE=DATEADD(month, DATEDIFF(month, 0, GETDATE()), 0)
;WITH CTE as
(
-- Will find all dates in current month
SELECT @FIRSTDAY as DATES
UNION ALL
SELECT DATEADD(DAY,1,DATES)
FROM CTE
WHERE DATES < DATEADD(MONTH,1,@FIRSTDAY)
)
,CTE2 AS
(
-- Join the #t table with CTE on the datename+'s'
SELECT jobId,personId,frequencyVal, DATES,
ROW_NUMBER() OVER(PARTITION BY DATENAME(WEEKDAY,CTE.DATES) ORDER BY CTE.DATES) DATECNT
FROM CTE
JOIN #t ON DATENAME(WEEKDAY,CTE.DATES)+'s' = #t.frequencyVal
WHERE MONTH(DATES)= MONTH(GETDATE())
)
-- Converts to CSV and make sure that only 4 days are generated for month
SELECT DISTINCT C2.jobId,C2.personId,frequencyVal,
SUBSTRING(
(SELECT ', ' + CAST(DATEPART(MONTH,DATES) AS VARCHAR(2)) + '/' +
CAST(DATEPART(DAY,DATES) AS VARCHAR(2))
FROM CTE2
WHERE C2.jobId=jobId AND C2.personId=personId AND DATECNT<5
ORDER BY CTE2.DATES
FOR XML PATH('')),2,200000) futureDates
FROM CTE2 C2
例如Query1中
最近的日期(这里我们以星期六为例)
2015-Jan-10
将是 01/03,01/10,01/17,01/24
2015-Jan-24
将是 01/03,01/10,01/17,01/24
QUERY 2 : Select 特定工作日当月最近 4 周的天数
-- Gets the first day in current month
DECLARE @FIRSTDAY DATE=DATEADD(month, DATEDIFF(month, 0, GETDATE()), 0)
;WITH CTE as
(
-- Will find all dates in current
SELECT CAST(@FIRSTDAY AS DATE) as DATES
UNION ALL
SELECT DATEADD(DAY,1,DATES)
FROM CTE
WHERE DATES < DATEADD(MONTH,1,@FIRSTDAY)
)
,CTE2 AS
(
-- Join the #t table with CTE on the datename+'s'
SELECT jobId,personId,frequencyVal,DATES,
-- Get week difference for each weekday
DATEDIFF(WEEK,DATES,GETDATE()) WEEKDIFF,
-- Count the number of weekdays in a month
COUNT(DATES) OVER(PARTITION BY DATENAME(WEEKDAY,CTE.DATES)) WEEKCOUNT
FROM CTE
JOIN #t ON DATENAME(WEEKDAY,CTE.DATES)+'s' = #t.frequencyVal
WHERE MONTH(DATES)= MONTH(GETDATE())
)
-- Converts to CSV and make sure that only nearest 4 week of days are generated for month
SELECT DISTINCT C2.jobId,C2.personId,frequencyVal,
SUBSTRING(
(SELECT ', ' + CAST(DATEPART(MONTH,DATES) AS VARCHAR(2)) + '/' +
CAST(DATEPART(DAY,DATES) AS VARCHAR(2))
FROM CTE2
WHERE C2.jobId=jobId AND C2.personId=personId AND C2.frequencyVal=frequencyVal AND
((WEEKDIFF<3 AND WEEKDIFF>-3 AND WEEKCOUNT = 5) OR WEEKCOUNT <= 4)
ORDER BY CTE2.DATES
FOR XML PATH('')),2,200000) futureDates
FROM CTE2 C2
例如Query2中
最近的日期(这里我们以星期六为例)
2015-Jan-10
将是 01/03,01/10,01/17,01/24
2015-Jan-24
将是 01/10,01/17,01/24,01/31
问题 3:Select 与月份无关的特定工作日的接下来 4 周的日期
;WITH CTE as
(
-- Will find all dates in current month
SELECT CAST(GETDATE() AS DATE) as DATES
UNION ALL
SELECT DATEADD(DAY,1,DATES)
FROM CTE
WHERE DATES < DATEADD(DAY,28,GETDATE())
)
,CTE2 AS
(
-- Join the #t table with CTE on the datename+'s'
SELECT jobId,personId,frequencyVal, DATES,
ROW_NUMBER() OVER(PARTITION BY DATENAME(WEEKDAY,CTE.DATES) ORDER BY CTE.DATES) DATECNT
FROM CTE
JOIN #t ON DATENAME(WEEKDAY,CTE.DATES)+'s' = #t.frequencyVal
)
-- Converts to CSV and make sure that only 4 days are generated for month
SELECT DISTINCT C2.jobId,C2.personId,frequencyVal,
SUBSTRING(
(SELECT ', ' + CAST(DATEPART(MONTH,DATES) AS VARCHAR(2)) + '/' +
CAST(DATEPART(DAY,DATES) AS VARCHAR(2))
FROM CTE2
WHERE C2.jobId=jobId AND C2.personId=personId AND C2.frequencyVal=frequencyVal
AND DATECNT < 5
ORDER BY CTE2.DATES
FOR XML PATH('')),2,200000) futureDates
FROM CTE2 C2
如果 GETDATE()
(如果是星期六)是
,则输出如下
2015-01-05 - 1/10, 1/17, 1/24, 1/31
2015-01-24 - 1/24, 1/31, 2/7, 2/14
我从我提出的以下问题中得到了以下代码:
我需要根据今天的日期在我的 table 中找到接下来的 4 个工作日对应的 Day-Of-Week 即,如果今天是 2015-01-24
,结果应该是 1/24, 1/31, 2/7, 2/14
Saturdays
.
TABLE
示例查询
create table #t
(
jobId int,
personId int,
frequencyVal varchar(10)
);
insert into #t values (1,100,'Mondays'),(2,101,'Saturdays');
WITH cte(n) AS
(
SELECT 0
UNION ALL
SELECT n+1 FROM cte WHERE n < 3
)
select #t.jobId, #t.personId, #t.frequencyVal, STUFF(a.d, 1, 1, '') AS FutureDates
from #t
cross apply (SELECT CASE #t.frequencyVal
WHEN 'SUNDAYS' THEN 1
WHEN 'MONDAYS' THEN 2
WHEN 'TUESDAYS' THEN 3
WHEN 'WEDNESDAYS' THEN 4
WHEN 'THURSDAYS' THEN 5
WHEN 'FRIDAYS' THEN 6
WHEN 'SATURDAYS' THEN 7
END)tranlationWeekdays(n)
cross apply (select ',' + CONVERT(varchar(10), CONVERT(date,dateadd(WEEK, cte.n,CONVERT(DATE, DATEADD(DAY, (DATEPART(WEEKDAY, GETDATE()) + tranlationWeekdays.n) % 7, GETDATE()))))) from cte FOR XML PATH('')) a(d);
drop table #t;
预期结果
这是我认为比较简单的方法,我觉得很符合你的要求
请注意,我已将您的 frequency_val
列更改为代表 SQL 服务器角度的星期几的整数,并添加了一个计算列来说明如何轻松地从中导出日期名称。
/*************************************************/
--Set up our sample table
/*************************************************/
declare @t table
(
jobId int,
personId int,
--frequencyVal varchar(10) -- why store a string when a tiny int will do.
frequency_val tinyint,
frequency_day as datename(weekday,frequency_val -1) + 's'
)</p>
<p>insert into @t
values
(1,100,1),--'Mondays'),
(2,101,6),--'Saturdays');
(3,101,7),--'Sundays');
(4,100,2)--'Tuesdays'),
--select * from @t</p>
<p>/*************************************************/
--Declare & initialise variables
/*************************************************/
declare @num_occurances int = 4
declare @from_date date = dateadd(dd,3,getdate()) -- this will allow you to play with the date simply by changing the increment value</p>
<p>/*************************************************/
-- To get a row for each occurance
/*************************************************/
;with r_cte (days_ahead, occurance_date)
as (select 0, convert(date,@from_date,121)
union all
select r_cte.days_ahead +1, convert(date,dateadd(DD, r_cte.days_ahead+1, @from_date),121)
from r_cte
where r_cte.days_ahead < (7 * @num_occurances) -1
)
select t.*, r_cte.occurance_date
from
@t t
inner join r_cte
on DATEPART(WEEKDAY, dateadd(dd,@@DATEFIRST - 1 ,r_cte.occurance_date)) = t.frequency_val</p>
<p>/*************************************************/
--To get a single row with a CSV of every occurance
/*************************************************/
;with r_cte (days_ahead, occurance_date)
as (select 0, convert(date,@from_date,121)
union all
select r_cte.days_ahead +1, convert(date,dateadd(DD, r_cte.days_ahead+1, @from_date),121)
from r_cte
where r_cte.days_ahead < (7 * @num_occurances) -1
)
select
t.*,
STUFF( (select ', '
+ convert(varchar(2),datepart(month,occurance_date),0) + '/'
+ convert(varchar(2),datepart(day,occurance_date),0) as occurance
from r_cte
where DATEPART(WEEKDAY, dateadd(dd,@@DATEFIRST - 1 ,r_cte.occurance_date)) = t.frequency_val
FOR XML PATH (''),TYPE).value('.','varchar(30)')
,1,2,'') occurance_date -- rest of STUFF() function
from
@t t
</pre>
获取当前月份的第一天。
DECLARE @FIRSTDAY DATE=DATEADD(month, DATEDIFF(month, 0, GETDATE()), 0)
创建 table 并插入值
create table #t
(
jobId int,
personId int,
frequencyVal varchar(10)
);
insert into #t values (1,100,'Mondays'),(2,101,'Saturdays');
您可以根据自己的情况使用以下任一查询。
QUERY 1 : Select 特定工作日当月的前 4 周
-- Gets the first day of current month
DECLARE @FIRSTDAY DATE=DATEADD(month, DATEDIFF(month, 0, GETDATE()), 0)
;WITH CTE as
(
-- Will find all dates in current month
SELECT @FIRSTDAY as DATES
UNION ALL
SELECT DATEADD(DAY,1,DATES)
FROM CTE
WHERE DATES < DATEADD(MONTH,1,@FIRSTDAY)
)
,CTE2 AS
(
-- Join the #t table with CTE on the datename+'s'
SELECT jobId,personId,frequencyVal, DATES,
ROW_NUMBER() OVER(PARTITION BY DATENAME(WEEKDAY,CTE.DATES) ORDER BY CTE.DATES) DATECNT
FROM CTE
JOIN #t ON DATENAME(WEEKDAY,CTE.DATES)+'s' = #t.frequencyVal
WHERE MONTH(DATES)= MONTH(GETDATE())
)
-- Converts to CSV and make sure that only 4 days are generated for month
SELECT DISTINCT C2.jobId,C2.personId,frequencyVal,
SUBSTRING(
(SELECT ', ' + CAST(DATEPART(MONTH,DATES) AS VARCHAR(2)) + '/' +
CAST(DATEPART(DAY,DATES) AS VARCHAR(2))
FROM CTE2
WHERE C2.jobId=jobId AND C2.personId=personId AND DATECNT<5
ORDER BY CTE2.DATES
FOR XML PATH('')),2,200000) futureDates
FROM CTE2 C2
例如Query1中
最近的日期(这里我们以星期六为例)2015-Jan-10
将是01/03,01/10,01/17,01/24
2015-Jan-24
将是01/03,01/10,01/17,01/24
QUERY 2 : Select 特定工作日当月最近 4 周的天数
-- Gets the first day in current month
DECLARE @FIRSTDAY DATE=DATEADD(month, DATEDIFF(month, 0, GETDATE()), 0)
;WITH CTE as
(
-- Will find all dates in current
SELECT CAST(@FIRSTDAY AS DATE) as DATES
UNION ALL
SELECT DATEADD(DAY,1,DATES)
FROM CTE
WHERE DATES < DATEADD(MONTH,1,@FIRSTDAY)
)
,CTE2 AS
(
-- Join the #t table with CTE on the datename+'s'
SELECT jobId,personId,frequencyVal,DATES,
-- Get week difference for each weekday
DATEDIFF(WEEK,DATES,GETDATE()) WEEKDIFF,
-- Count the number of weekdays in a month
COUNT(DATES) OVER(PARTITION BY DATENAME(WEEKDAY,CTE.DATES)) WEEKCOUNT
FROM CTE
JOIN #t ON DATENAME(WEEKDAY,CTE.DATES)+'s' = #t.frequencyVal
WHERE MONTH(DATES)= MONTH(GETDATE())
)
-- Converts to CSV and make sure that only nearest 4 week of days are generated for month
SELECT DISTINCT C2.jobId,C2.personId,frequencyVal,
SUBSTRING(
(SELECT ', ' + CAST(DATEPART(MONTH,DATES) AS VARCHAR(2)) + '/' +
CAST(DATEPART(DAY,DATES) AS VARCHAR(2))
FROM CTE2
WHERE C2.jobId=jobId AND C2.personId=personId AND C2.frequencyVal=frequencyVal AND
((WEEKDIFF<3 AND WEEKDIFF>-3 AND WEEKCOUNT = 5) OR WEEKCOUNT <= 4)
ORDER BY CTE2.DATES
FOR XML PATH('')),2,200000) futureDates
FROM CTE2 C2
例如Query2中
最近的日期(这里我们以星期六为例)2015-Jan-10
将是01/03,01/10,01/17,01/24
2015-Jan-24
将是01/10,01/17,01/24,01/31
问题 3:Select 与月份无关的特定工作日的接下来 4 周的日期
;WITH CTE as
(
-- Will find all dates in current month
SELECT CAST(GETDATE() AS DATE) as DATES
UNION ALL
SELECT DATEADD(DAY,1,DATES)
FROM CTE
WHERE DATES < DATEADD(DAY,28,GETDATE())
)
,CTE2 AS
(
-- Join the #t table with CTE on the datename+'s'
SELECT jobId,personId,frequencyVal, DATES,
ROW_NUMBER() OVER(PARTITION BY DATENAME(WEEKDAY,CTE.DATES) ORDER BY CTE.DATES) DATECNT
FROM CTE
JOIN #t ON DATENAME(WEEKDAY,CTE.DATES)+'s' = #t.frequencyVal
)
-- Converts to CSV and make sure that only 4 days are generated for month
SELECT DISTINCT C2.jobId,C2.personId,frequencyVal,
SUBSTRING(
(SELECT ', ' + CAST(DATEPART(MONTH,DATES) AS VARCHAR(2)) + '/' +
CAST(DATEPART(DAY,DATES) AS VARCHAR(2))
FROM CTE2
WHERE C2.jobId=jobId AND C2.personId=personId AND C2.frequencyVal=frequencyVal
AND DATECNT < 5
ORDER BY CTE2.DATES
FOR XML PATH('')),2,200000) futureDates
FROM CTE2 C2
如果 GETDATE()
(如果是星期六)是
2015-01-05 - 1/10, 1/17, 1/24, 1/31
2015-01-24 - 1/24, 1/31, 2/7, 2/14