【发布时间】:2022-01-07 00:41:48
【问题描述】:
我们将WeekNo 和Year 输入到以下格式的表格中:
YYYY-ww (2021-48)
我想要从给定周开始的开始日期和结束日期,没有年份,格式如上。
如何从 SQL 查询或过程中获取?
谢谢,
【问题讨论】:
标签: sql sql-server tsql week-number
我们将WeekNo 和Year 输入到以下格式的表格中:
YYYY-ww (2021-48)
我想要从给定周开始的开始日期和结束日期,没有年份,格式如上。
如何从 SQL 查询或过程中获取?
谢谢,
【问题讨论】:
标签: sql sql-server tsql week-number
(一个额外的答案,因为这个方法有点不同。)
此方法是关于使用包含日期和日期相关内容的计数表。
那么这样的日历表就可以用来查询年份和星期了。
例如
创建日历表
create table REF_Calendar (
cal_date date primary key not null,
cal_year smallint not null,
cal_month tinyint not null,
cal_day tinyint not null,
cal_dayofyear smallint not null,
cal_quarter tinyint not null,
cal_weekday_abbrev char(3) not null,
cal_iso_week tinyint,
cal_df7_week tinyint not null,
cal_df7_weekday tinyint not null,
cal_df1_week tinyint,
cal_df1_weekday tinyint,
cal_mooncycle tinyint,
index idx_Calendar_year_week unique (cal_year, cal_df7_week, cal_df7_weekday)
);
填写日历
SET DATEFIRST 7; -- 7: week starts on sunday
declare @startdate date = '2020-01-01';
declare @enddate date = '2023-01-01';
with rcte_calendar as (
select @startdate as cal_date
union all
select dateadd(day, 1, cal_date)
from rcte_calendar
where cal_date < @enddate
)
insert into ref_calendar
(cal_date, cal_year, cal_month, cal_day, cal_dayofyear, cal_quarter, cal_weekday_abbrev,
cal_df7_week, cal_df7_weekday, cal_mooncycle)
select
cal_date
, datepart(year, cal_date) as cal_year
, datepart(month, cal_date) as cal_month
, datepart(day, cal_date) as cal_day
, datepart(dayofyear, cal_date) as cal_dayofyear
, datepart(quarter, cal_date) as cal_quarter
, lower(left(datename(weekday, cal_date), 3)) as cal_weekday_abbrev
, datepart(week, cal_date) as cal_df7_week
, datepart(weekday, cal_date) as cal_df7_weekday
, abs(1-abs(round(((14-1.0*CONVERT(int, CONVERT(nvarchar(2), cal_date, 131)))/14)
, 1, 1)))*100 as cal_mooncycle
from rcte_calendar cte
where not exists (
select 1
from ref_calendar cal
where cal.cal_date = cte.cal_date
)
option (maxrecursion 0);
更新额外的
SET DATEFIRST 1; -- 1: week starts on monday
update ref_calendar
set cal_df1_week = datepart(week, cal_date)
, cal_df1_weekday = datepart(weekday, cal_date)
, cal_iso_week = datepart(iso_week, cal_date)
where cal_df1_week is null
or cal_df1_weekday is null
or cal_iso_week is null;
查看外观
select top 9 *
from ref_calendar
where cal_mooncycle = 100
and cal_date >= getdate()
and cal_year <= 1 + datepart(year, current_timestamp)
order by cal_date asc;
| cal_date | cal_year | cal_month | cal_day | cal_dayofyear | cal_quarter | cal_weekday_abbrev | cal_iso_week | cal_df7_week | cal_df7_weekday | cal_df1_week | cal_df1_weekday | cal_mooncycle |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 2021-12-17 | 2021 | 12 | 17 | 351 | 4 | fri | 50 | 51 | 6 | 51 | 5 | 100 |
| 2021-12-18 | 2021 | 12 | 18 | 352 | 4 | sat | 50 | 51 | 7 | 51 | 6 | 100 |
| 2021-12-19 | 2021 | 12 | 19 | 353 | 4 | sun | 50 | 52 | 1 | 51 | 7 | 100 |
| 2022-01-16 | 2022 | 1 | 16 | 16 | 1 | sun | 2 | 4 | 1 | 3 | 7 | 100 |
| 2022-01-17 | 2022 | 1 | 17 | 17 | 1 | mon | 3 | 4 | 2 | 4 | 1 | 100 |
| 2022-01-18 | 2022 | 1 | 18 | 18 | 1 | tue | 3 | 4 | 3 | 4 | 2 | 100 |
| 2022-02-14 | 2022 | 2 | 14 | 45 | 1 | mon | 7 | 8 | 2 | 8 | 1 | 100 |
| 2022-02-15 | 2022 | 2 | 15 | 46 | 1 | tue | 7 | 8 | 3 | 8 | 2 | 100 |
| 2022-02-16 | 2022 | 2 | 16 | 47 | 1 | wed | 7 | 8 | 4 | 8 | 3 | 100 |
测试将日历应用于表格
DECLARE @Test TABLE (col char(7));
INSERT INTO @Test VALUES
('2021-01'), ('2021-02')
, ('2021-48')
, ('2022-01'), ('2022-02')
;
select *
from @test t
outer apply (
select
min(cal_date) as startofweek
, max(cal_date) as endofweek
from (values (try_cast(left(col, 4) as int),
abs(try_cast(right(col, 2) as int)))
) as val(yr, ww)
join ref_calendar as cal
on ((cal_year = yr and cal_df7_week = ww)
or (ww = 1 and cal_year = yr-1 and cal_df7_week = 53)
or (ww = 53 and cal_year = yr+1 and cal_df7_week = 1))
) cal
| col | startofweek | endofweek |
|---|---|---|
| 2021-01 | 2020-12-27 | 2021-01-02 |
| 2021-02 | 2021-01-03 | 2021-01-09 |
| 2021-48 | 2021-11-21 | 2021-11-27 |
| 2022-01 | 2021-12-26 | 2022-01-01 |
| 2022-02 | 2022-01-02 | 2022-01-08 |
dbfiddle here
上的演示【讨论】:
这里有两种方法可以计算查询中年-周字符串的开始和结束日期。
1) 没有 UDF
SET DATEFIRST 7; -- 7: Sunday has weekday 1
DECLARE @Test TABLE ([YEAR-WW] char(7));
INSERT INTO @Test VALUES
('2020-53'), ('2021-01'), ('2021-02')
, ('2021-48')
, ('2021-53'), ('2022-01');
SELECT [YEAR-WW]
, [FirstDayOfWeek] = CAST(DATEADD(day, 1-DATEPART(weekday, DATEADD(week, ABS(RIGHT([YEAR-WW],2))-1, LEFT([YEAR-WW],5)+'01-01')), DATEADD(week, ABS(RIGHT([YEAR-WW],2))-1, LEFT([YEAR-WW],5)+'01-01')) AS DATE)
, [LastDayOfWeek] = CAST(DATEADD(day, 6, DATEADD(day, 1-DATEPART(weekday, DATEADD(week, ABS(RIGHT([YEAR-WW],2))-1, LEFT([YEAR-WW],5)+'01-01')), DATEADD(week, ABS(RIGHT([YEAR-WW],2))-1, LEFT([YEAR-WW],5)+'01-01'))) AS DATE)
FROM @Test
ORDER BY 1;
| YEAR-WW | FirstDayOfWeek | LastDayOfWeek |
|---|---|---|
| 2020-53 | 2020-12-27 | 2021-01-02 |
| 2021-01 | 2020-12-27 | 2021-01-02 |
| 2021-02 | 2021-01-03 | 2021-01-09 |
| 2021-48 | 2021-11-21 | 2021-11-27 |
| 2021-53 | 2021-12-26 | 2022-01-01 |
| 2022-01 | 2021-12-26 | 2022-01-01 |
2) 使用此 UDF
CREATE FUNCTION dbo.GetDateFromYearWeek (
@YearWeek VARCHAR(7) = '000101', -- default
@WeekDay INT = 1, -- default
@FirstWeekDayName VARCHAR(9) = 'monday' -- default
) RETURNS DATE
BEGIN
IF @YearWeek = '000101'
SET @YearWeek = CONCAT(DATEPART(year, GETDATE()), '-', DATEPART(week, GETDATE()));
IF @YearWeek NOT LIKE '[0-9][0-9][0-9][0-9]%[0-9-][0-9]'
RETURN NULL;
IF @WeekDay < 1 OR @WeekDay > 7
RETURN NULL;
DECLARE @FirstWeekDay INT = CHARINDEX(LOWER(LEFT(@FirstWeekDayName,3)), ' montuewedthufrisatsun')/3;
IF @FirstWeekDay = 0 -- not found in string
SET @FirstWeekDay = @@DATEFIRST;
DECLARE @Year INT = TRY_CAST(LEFT(@YearWeek, 4) AS INT);
DECLARE @Week INT = ABS(TRY_CAST(RIGHT(@YearWeek, 2) AS INT));
DECLARE @Date DATE = TRY_CAST(CONCAT(@Year,'-01-01') AS DATE);
SET @Date = DATEADD(week, @Week-1, @Date);
DECLARE @DowDiff INT = 0-( (@@DATEFIRST+6-(@FirstWeekday%7))%7 + DATEPART(weekday,@Date) )%7;
SET @Date = DATEADD(day, @DowDiff, @Date);
SET @Date = DATEADD(day, @WeekDay-1, @Date);
RETURN @Date;
END;
CREATE FUNCTION dbo.GetDateFromYearWeekSundayFirst (
@YearWeek VARCHAR(7) = '000101',
@WeekDay INT = 1
) RETURNS DATE
BEGIN
RETURN dbo.GetDateFromYearWeek(@YearWeek, @Weekday, 'sunday');
END;
使用示例
DECLARE @Test TABLE ([YEAR-WW] char(7));
INSERT INTO @Test VALUES
('2020-53'), ('2021-01'), ('2021-02')
, ('2021-48')
, ('2021-53'), ('2022-01'), ('2022-02')
;
SELECT [YEAR-WW]
, [FirstOfWeek_Mon] = dbo.GetDateFromYearWeek([YEAR-WW], 1, 'mon')
, [LastOfWeek_Mon] = dbo.GetDateFromYearWeek([YEAR-WW], 7, 'Monday')
, [FirstOfWeek_Sun] = dbo.GetDateFromYearWeek([YEAR-WW], 1, 'SUNDAY')
, [LastOfWeek_Sun] = dbo.GetDateFromYearWeekSundayFirst([YEAR-WW], 7)
FROM @Test
ORDER BY 1;
| YEAR-WW | FirstOfWeek_Mon | LastOfWeek_Mon | FirstOfWeek_Sun | LastOfWeek_Sun |
|---|---|---|---|---|
| 2020-53 | 2020-12-28 | 2021-01-03 | 2020-12-27 | 2021-01-02 |
| 2021-01 | 2020-12-28 | 2021-01-03 | 2020-12-27 | 2021-01-02 |
| 2021-02 | 2021-01-04 | 2021-01-10 | 2021-01-03 | 2021-01-09 |
| 2021-48 | 2021-11-22 | 2021-11-28 | 2021-11-21 | 2021-11-27 |
| 2021-53 | 2021-12-27 | 2022-01-02 | 2021-12-26 | 2022-01-01 |
| 2022-01 | 2021-12-27 | 2022-01-02 | 2021-12-26 | 2022-01-01 |
| 2022-02 | 2022-01-03 | 2022-01-09 | 2022-01-02 | 2022-01-08 |
dbfiddle here
上的演示【讨论】:
这个怎么样?
declare @dateText varchar(7);
declare @week int;
declare @year int;
set datefirst 1; -- Set first day of week to Monday
set @dateText = '2021-48';
set @year = SUBSTRING(@dateText, 1, 4)
set @week = SUBSTRING(@dateText, 6, 2)
select DATEADD(week, @week, DATEADD(year, @year-1900, 0)) - 4 -
DATEPART(dw, DATEADD(week, @week, DATEADD(year, @year-1900, 0)) - 4) + 1
Output
-----------------
2021-11-29 00:00:00.000
或者你可以创建一个函数:
CREATE FUNCTION dbo.GetStartOfWeek(@dateText varchar(7))
RETURNS Datetime
BEGIN
declare @week int;
declare @year int;
declare @offset int;
select
@offset = @@DATEFIRST - 1,
@year = SUBSTRING(@dateText, 1, 4),
@week = SUBSTRING(@dateText, 6, 2)
RETURN DATEADD(week, @week, DATEADD(year, @year-1900, 0)) - 4 -
DATEPART(dw, DATEADD(week, @week, DATEADD(year, @year-1900, 0) + @offset) - 4) + 1
END;
您可以在 SQL 查询或存储过程中使用:
select dbo.GetStartOfWeek('2021-48') as StartDate, DATEADD(d, 7, dbo.GetStartOfWeek('2021-48')) as EndDate
【讨论】: