看到网上有高手直接用sql查询创建日历,也想自己动手实践一遍。笔者这里的实现和网上的都没有什么区别,思路也没有什么新意。觉得好玩,就把它记下来吧。 
一、准备知识
1、sql的with关键字
关于with和公用表表达式(CTE),可以参考SQL Server 2005新特性之使用with关键字解决递归父子关系
Sql Server2005 Transact-SQL 新兵器学习总结之-公用表表达式(CTE) 。
2、sql的pivot关键字
pivot非常强大,但是对于新手来说,可能连这个单词都很生僻,使用也是举步维艰。pivot的示例可以参考这篇这篇
二、实现
1、实现思路:
使用递归with子句,返回当前月的每一天,然后使用case和max转换为周内日期。
2、辅助表T1


USE [TestDb]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[T1](
    [tid] [int] NOT NULL,
 CONSTRAINT [PK_T1] PRIMARY KEY CLUSTERED 
(
    [tid] ASC
)WITH (IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]

说明:T1表中有且只有一条记录:insert into t1 values(1)
3、生成日历的sql


with x(dy,dm,mth,dw,wk)
as(
select dy,day(dy) dm,datepart(m,dy) mth,datepart(dw,dy) dw,
case when datepart(dw,dy)=1
then datepart(ww,dy)-1
else datepart(ww,dy) end wk
from ( select dateadd(day,-day(getdate())+1,getdate()) dy from t1) x
union all select dateadd(d,1,dy),day(dateadd(d,1,dy)),mth,
datepart(dw,dateadd(d,1,dy)),
case when datepart(dw,dateadd(d,1,dy))=1
then datepart(wk,dateadd(d,1,dy))-1
else datepart(wk,dateadd(d,1,dy)) end
from x where datepart(m,dateadd(d,1,dy))=mth)
select max(case dw when 2 then dm end) as '星期一',
       max(case dw when 3 then dm end) as '星期二',
       max(case dw when 4 then dm end) as '星期三',
       max(case dw when 5 then dm end) as '星期四',
       max(case dw when 6 then dm end) as '星期五',
       max(case dw when 7 then dm end) as '星期六',
       max(case dw when 1 then dm end) as '星期日'
from x group by wk order by wk

图片:
4、生成日历sql语句说明
(1)首先,为当前月的每一天返回一行信息。可以使用sql server支持递归with的with子句来实现。返回的每一行包含的信息:月份日期(dm),星期几(dw),当前月份(mth),iso周序号(wk)。
(2)在递归之前,递归视图x产生的结果(union all的上半部分)如下所示:

select dy,day(dy) dm,datepart(m,dy) mth,datepart(dw,dy) dw,
case when datepart(dw,dy)=1
then datepart(ww,dy)-1
else datepart(ww,dy) end wk
from ( select dateadd(day,-day(getdate())+1,getdate()) dy from t1) x

(3)下一步重复递增dm值(递增次数就是月份对应天数),直到超出当前月为止。在对当前月的每一天进行处理时,也会得到每天对应星期几以及当日的iso周序号。


with x(dy,dm,mth,dw,wk)
as(
select dy,day(dy) dm,datepart(m,dy) mth,datepart(dw,dy) dw,
case when datepart(dw,dy)=1
then datepart(ww,dy)-1
else datepart(ww,dy) end wk
from ( select dateadd(day,-day(getdate())+1,getdate()) dy from t1) x
union all select dateadd(d,1,dy),day(dateadd(d,1,dy)),mth,
datepart(dw,dateadd(d,1,dy)),
case when datepart(dw,dateadd(d,1,dy))=1
then datepart(wk,dateadd(d,1,dy))-1
else datepart(wk,dateadd(d,1,dy)) end
from x where datepart(m,dateadd(d,1,dy))=mth)
select *
from x 

此时,当前月的每一天包含的信息有:月份日期值,月份值,一位数字表示的星期几(1-7分别对应星期日到星期六)以及iso周序号。
(4)使用一个case表达式确定dm(当前月的每一天)中每个值对应星期几。


with x(dy,dm,mth,dw,wk)
as(
select dy,day(dy) dm,datepart(m,dy) mth,datepart(dw,dy) dw,
case when datepart(dw,dy)=1
then datepart(ww,dy)-1
else datepart(ww,dy) end wk
from ( select dateadd(day,-day(getdate())+1,getdate()) dy from t1) x
union all select dateadd(d,1,dy),day(dateadd(d,1,dy)),mth,
datepart(dw,dateadd(d,1,dy)),
case when datepart(dw,dateadd(d,1,dy))=1
then datepart(wk,dateadd(d,1,dy))-1
else datepart(wk,dateadd(d,1,dy)) end
from x where datepart(m,dateadd(d,1,dy))=mth)
select case dw when 2 then dm end as '星期一',
       case dw when 3 then dm end as '星期二',
       case dw when 4 then dm end as '星期三',
       case dw when 5 then dm end as '星期四',
       case dw when 6 then dm end as '星期五',
       case dw when 7 then dm end as '星期六',
       case dw when 1 then dm end as '星期日'
from x 

这里每周的每一天都独占一行,在每行中,包含日期编号的列都与星期名相对应。
(5)最后把每周的所有日期放在一行中
正如本文3中给出的最终sql语句一样,对各列使用聚集函数max,并且按照周序号wk分组排序即可。 
ps:最终结果在sql server2005下测试通过,其他版本未测试。 不过sql server 2005版本下利用dbms自带的函数pivot可以很轻松实现日历的:


Use testdb
go Declare 
    @Date datetime,
    @StartDate datetime,
    @EndDate datetime,
    @FirstIndex int Set @Date =getdate() --输入一个日期,即可算出当月的日历 比如输入20080808,这里取当前日期 Select 
    @StartDate=Convert(char(6),@Date,112)+'01', 
    @EndDate=Dateadd(month,1,@StartDate)-1,
    @FirstIndex=Datediff(day,0,@StartDate)%7 ;
With t As
(
    Select Date=Convert(int,1),Row=(@FirstIndex)/7,Col=@FirstIndex
    Union All
    Select Date=Date+1,Row=(@FirstIndex+Date)/7,Col=(Date+@FirstIndex)%7
    From t
    Where Date<=Datediff(day,@StartDate,@EndDate)
)
Select 
    [星期一]=Isnull(Convert(char(2),[0]),''),
    [星期二]=Isnull(Convert(char(2),[1]),''),
    [星期三]=Isnull(Convert(char(2),[2]),''),
    [星期四]=Isnull(Convert(char(2),[3]),''),
    [星期五]=Isnull(Convert(char(2),[4]),''),
    [星期六]=Isnull(Convert(char(2),[5]),''),
    [星期日]=Isnull(Convert(char(2),[6]),'')
From t
Pivot (Max(Date) For col In([0],[1],[2],[3],[4],[5],[6])) b

pivot真是华丽的强大,强大的华丽啊。

利用sql server直接创建日历的更多相关文章

  1. 利用SQL Server 2008 R2创建自动备份计划

    本文主要利用SQL Server 2008 R2自带的"维护计划"创建一个自动备份数据的任务. 首先,启动 Sql Management studio,确保"SQL Se ...

  2. 四、利用SQL Server 2008 R2创建自动备份计划

    (转) 本文主要利用SQL Server 2008 R2自带的"维护计划"创建一个自动备份数据的任务. 首先,启动 Sql Management studio,确保"SQ ...

  3. ASP.net(C#)利用SQL Server实现注册和登陆功能

    说说我现在吧,楼主现在从事的事IT行业,主攻DotNet技术:当然这次上博客园我也是有备而来,所有再次奉献鄙人拙作,以飨诸位,望诸位不吝赐教. 世界上大多数的工作都是熟练性的工种,编程也不例外,做久了 ...

  4. 在64位SQL Server中创建Oracle的链接服务器

    当我们同时使用SQL Server和Oracle来存储数据时,经常会用到跨库查询.为了方便使用跨库查询,一个最好的办法就是通过创建链接服务器来实现.既可以在SQL Server中创建Oracle的链接 ...

  5. sql server 脚本创建数据库邮件

    sql server 脚本创建数据库邮件代码: --脚本创建数据库邮件 --1.开启数据库邮件 RECONFIGURE WITH OVERRIDE GO RECONFIGURE WITH OVERRI ...

  6. sql server中创建链接服务器图解教程

    转自sql server中创建链接服务器图解教程 1.展开服务器对象-->链接服务器-->右击"新建链接服务器" 注意:必须以数据库管理员身份登录(通常也就是sa帐号) ...

  7. sql server 数据库创建链接服务器访问另外一个sql server 数据库

    继上篇在sql server中创建链接服务器访问oracle数据库:http://www.cnblogs.com/527289276qq/p/4770379.html 本文介绍在sql server中 ...

  8. sql server 数据库创建链接服务器

    本文介绍在sql server中创建链接服务器访问sql server数据库. 方法: 打开SSMS,新建程序,执行下面sql语句块: EXEC sp_addlinkedserver @server= ...

  9. 在64位SQL Server中创建Oracle的链接服务器 Link Server

    有时候我们希望在一个sqlserver下访问另一个sqlserver数据库上的数据,或者访问其他oracle数据库上的数据,要想完成这些操作,我们首要的是创建数据库链接. 数据库链接能够让本地的一个s ...

随机推荐

  1. centos7添加虚拟IP

    1.在网络配置文件中添加虚拟IP,vi /etc/sysconfig/network-scripts/ifcfg-eno16777736 TYPE="Ethernet" BOOTP ...

  2. Python之旅:字典

      Python数据类型 #作用:存多个值,key:value 存取,取值速度快 #定义:key必须是不可变类型,value可以是任意类型 字典是另一种可变容器模型,且可存储任意类型对象. 字典的每个 ...

  3. MVC 中@Html.DropDownListFor() 设置选中项 这么不好使 ? [问题点数:40分,结帖人lkf181]

    http://bbs.csdn.net/topics/390867060 由于不知道错误原因在哪 我尽量把代码都贴出来吧:重点是:在 Controller 类里 我给 SelectListItem集合 ...

  4. js正则取出一个字符串小括号中的内容

    var aa="ldfjsldfj(dsfasjfj3124123)"; var result = aa.match(/\(([^)]*)\)/); // 此时result=[&q ...

  5. Cannot enlarge string buffer containing XX bytes by XX more bytes

    在ELK的数据库报警系统中,发现有台机器报出了下面的错误: 2018-12-04 18:55:26.842 CST,"XXX","XXX",21106,&quo ...

  6. App统计指标定义

    度量(指标) 定义 活跃用户 指启动应用的用户(去重,即1台设备打开多次会被计为1个活跃用户). 是衡量一个应用运营情况最基础的一个指标,用以表示用户规模.通常根据不同的时间限定,有日活跃用户.周活跃 ...

  7. GO_04:GO语言基础条件、跳转、Array和Slice

    1. 判断语句if 1. 条件表达式没有括号(这点其他语言转过来的需要注意) 2. 支持一个初始化表达式(可以是并行方式,即:a, b, c := 1, 2, 3) 3. 左大括号必须和条件语句或 e ...

  8. 自动化工具制作PASCAL VOC 数据集

    自动化工具制作PASCAL VOC 数据集   1. VOC的格式 VOC主要有三个重要的文件夹:Annotations.ImageSets和JPEGImages JPEGImages 文件夹 该文件 ...

  9. ffmpeg的API函数用法 :sws_scale函数的用法-具体应用

    移植ffmpeg过程中,遇到swscale的用法问题,所以查到这篇文章.文章虽然已经过去很长时间,但是还有颇多可以借鉴之处.谢谢“咕咕钟. 转自:http://guguclock.blogspot.c ...

  10. JS中浮点数精度误差解决

    问题出现 0.1 + 0.2 = 0.30000000000000004 问题分析 对于浮点数的四则运算,几乎所有的编程语言都会有类似精度误差的问题,只不过在 C++/C#/Java 这些语言中已经封 ...