额,这个部分是修改后追加的,因为质量分太低,所以水点内容,提一提质量分,嘿嘿。
系统表 spt_values
嗯,用来补全数据很常用的一个表,通常写作 master…spt_values,其中 type 为 p 的内容,有 0 到 2017 的连续整数,共2048个数字,对于数据补全缺失,可以结合 id 列或日期列之类的,补全数据的相关文章,可以参考《sqlserver 数据区间补全》
CTE 表
一个可以用来生成或者建立临时数据的方便的方法,当然,也不仅仅可以这么做,同样可以参考老顾的其他文章《【新星计划】数据库 CTE 初识》或自行百度。
CSDN 文盲老顾的博客,https://blog.csdn.net/superwfei
老顾的个人社区,https://bbs.csdn.net/forums/bfba6c5031e64c13aa7c60eebe858a5f?category=10003&typeId=3364713
排名函数
首先了解下 sqlserver 的开窗函数,有 row_number,rank,dense_rank,都可以对行数据进行排序并附加序号,关于row_number就不细说了,本文使用 dense_rank
关于排名函数,可以自行百度一下,也可以看老顾的文章《【新星计划】数据库 排名函数 初识》
日期函数
然后,我们在本文里复习一下日期函数的用法,dateadd,datediff,datepart
dateadd 用来对日期进行计算,比如前一天,后一天,前一小时后一小时之类的,当然,也可以前三十天,后一年之类的。
datediff 用来求两个日期数据的差,差的结果以指定的类型进行返回,比如日期差,分钟差,月份差之类的。
datepart 用来获取日期之间指定位置的数据,比如年、月、日、星期几之类的。
行转列
最后,使用行转列,完成日历的排布
行转列的时候,需要注意一点,如果出去需要转列的字段外,其他字段如果完全相同,则会合并成为同一行,如果所有其他列都不相同,则会每个数据占一行,如果部分相同,部分不同,则相同的合并为同一行,不同的则为多行,也就是本文日历实现的逻辑。
完整代码
declare @year int,@sd date,@ed date
-- 指定公历年份
set @year = 2021
-- 获得当年的第一天与次年的第一天
select @sd = CONVERT(varchar,@year)+'-1-1',@ed=CONVERT(varchar,@year+1)+'-1-1'
;with t as (
-- 先将所有日期补全出来
select dateadd(d,number,@sd) as d
from master..spt_values
where type='p'
and number<DATEDIFF(D,@sd,@ed)
),t1 as (
-- 取得每个日期所属的月份和星期数以及每周的第几天,需要注意,如果设置了星期天归属设置,输出内容可能有差异
-- 使用 dense_rank 排名函数,对每个月的所有日期使用同一个排名,dense_rank与rank的区别在于一个连续,一个非连续
select dense_rank() over(order by convert(varchar(6),d,112)) as m,DATEPART(week,d) w,DATEPART(weekday,d) as wd,DATEPART(day,d) as d
from t
)
select CONVERT(varchar,m)+'月' as 月份,'第'+convert(varchar,w)+'周' 周目
,[1] 周日,[2] 周一,[3] 周二,[4] 周三,[5] 周四,[6] 周五,[7] 周六
from t1 a
pivot(
max(d) for wd in ([1],[2],[3],[4],[5],[6],[7])
)p
月份 周目 周日 周一 周二 周三 周四 周五 周六
------ -------- ----------- ----------- ----------- ----------- ----------- ----------- -----------
1月 第1周 NULL NULL NULL NULL NULL 1 2
1月 第2周 3 4 5 6 7 8 9
1月 第3周 10 11 12 13 14 15 16
1月 第4周 17 18 19 20 21 22 23
1月 第5周 24 25 26 27 28 29 30
1月 第6周 31 NULL NULL NULL NULL NULL NULL
2月 第6周 NULL 1 2 3 4 5 6
2月 第7周 7 8 9 10 11 12 13
2月 第8周 14 15 16 17 18 19 20
2月 第9周 21 22 23 24 25 26 27
2月 第10周 28 NULL NULL NULL NULL NULL NULL
3月 第10周 NULL 1 2 3 4 5 6
3月 第11周 7 8 9 10 11 12 13
3月 第12周 14 15 16 17 18 19 20
3月 第13周 21 22 23 24 25 26 27
3月 第14周 28 29 30 31 NULL NULL NULL
4月 第14周 NULL NULL NULL NULL 1 2 3
4月 第15周 4 5 6 7 8 9 10
4月 第16周 11 12 13 14 15 16 17
4月 第17周 18 19 20 21 22 23 24
4月 第18周 25 26 27 28 29 30 NULL
5月 第18周 NULL NULL NULL NULL NULL NULL 1
5月 第19周 2 3 4 5 6 7 8
5月 第20周 9 10 11 12 13 14 15
5月 第21周 16 17 18 19 20 21 22
5月 第22周 23 24 25 26 27 28 29
5月 第23周 30 31 NULL NULL NULL NULL NULL
6月 第23周 NULL NULL 1 2 3 4 5
6月 第24周 6 7 8 9 10 11 12
6月 第25周 13 14 15 16 17 18 19
6月 第26周 20 21 22 23 24 25 26
6月 第27周 27 28 29 30 NULL NULL NULL
7月 第27周 NULL NULL NULL NULL 1 2 3
7月 第28周 4 5 6 7 8 9 10
7月 第29周 11 12 13 14 15 16 17
7月 第30周 18 19 20 21 22 23 24
7月 第31周 25 26 27 28 29 30 31
8月 第32周 1 2 3 4 5 6 7
8月 第33周 8 9 10 11 12 13 14
8月 第34周 15 16 17 18 19 20 21
8月 第35周 22 23 24 25 26 27 28
8月 第36周 29 30 31 NULL NULL NULL NULL
9月 第36周 NULL NULL NULL 1 2 3 4
9月 第37周 5 6 7 8 9 10 11
9月 第38周 12 13 14 15 16 17 18
9月 第39周 19 20 21 22 23 24 25
9月 第40周 26 27 28 29 30 NULL NULL
10月 第40周 NULL NULL NULL NULL NULL 1 2
10月 第41周 3 4 5 6 7 8 9
10月 第42周 10 11 12 13 14 15 16
10月 第43周 17 18 19 20 21 22 23
10月 第44周 24 25 26 27 28 29 30
10月 第45周 31 NULL NULL NULL NULL NULL NULL
11月 第45周 NULL 1 2 3 4 5 6
11月 第46周 7 8 9 10 11 12 13
11月 第47周 14 15 16 17 18 19 20
11月 第48周 21 22 23 24 25 26 27
11月 第49周 28 29 30 NULL NULL NULL NULL
12月 第49周 NULL NULL NULL 1 2 3 4
12月 第50周 5 6 7 8 9 10 11
12月 第51周 12 13 14 15 16 17 18
12月 第52周 19 20 21 22 23 24 25
12月 第53周 26 27 28 29 30 31 NULL
(63 行受影响)