看到网上有高手直接用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

  1.  
  2. USE [TestDb]
  3. GO
  4. SET ANSI_NULLS ON
  5. GO
  6. SET QUOTED_IDENTIFIER ON
  7. GO
  8. CREATE TABLE [dbo].[T1](
  9.     [tid] [int] NOT NULL,
  10.  CONSTRAINT [PK_T1] PRIMARY KEY CLUSTERED 
  11. (
  12.     [tid] ASC
  13. )WITH (IGNORE_DUP_KEY = OFF) ON [PRIMARY]
  14. ) ON [PRIMARY]

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

  1.  
  2. with x(dy,dm,mth,dw,wk)
  3. as(
  4. select dy,day(dy) dm,datepart(m,dy) mth,datepart(dw,dy) dw,
  5. case when datepart(dw,dy)=1
  6. then datepart(ww,dy)-1
  7. else datepart(ww,dy) end wk
  8. from ( select dateadd(day,-day(getdate())+1,getdate()) dy from t1) x
  9. union all select dateadd(d,1,dy),day(dateadd(d,1,dy)),mth,
  10. datepart(dw,dateadd(d,1,dy)),
  11. case when datepart(dw,dateadd(d,1,dy))=1
  12. then datepart(wk,dateadd(d,1,dy))-1
  13. else datepart(wk,dateadd(d,1,dy)) end
  14. from x where datepart(m,dateadd(d,1,dy))=mth)
  15. select max(case dw when 2 then dm end) as '星期一',
  16.        max(case dw when 3 then dm end) as '星期二',
  17.        max(case dw when 4 then dm end) as '星期三',
  18.        max(case dw when 5 then dm end) as '星期四',
  19.        max(case dw when 6 then dm end) as '星期五',
  20.        max(case dw when 7 then dm end) as '星期六',
  21.        max(case dw when 1 then dm end) as '星期日'
  22. from x group by wk order by wk

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

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

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

  1.  
  2. with x(dy,dm,mth,dw,wk)
  3. as(
  4. select dy,day(dy) dm,datepart(m,dy) mth,datepart(dw,dy) dw,
  5. case when datepart(dw,dy)=1
  6. then datepart(ww,dy)-1
  7. else datepart(ww,dy) end wk
  8. from ( select dateadd(day,-day(getdate())+1,getdate()) dy from t1) x
  9. union all select dateadd(d,1,dy),day(dateadd(d,1,dy)),mth,
  10. datepart(dw,dateadd(d,1,dy)),
  11. case when datepart(dw,dateadd(d,1,dy))=1
  12. then datepart(wk,dateadd(d,1,dy))-1
  13. else datepart(wk,dateadd(d,1,dy)) end
  14. from x where datepart(m,dateadd(d,1,dy))=mth)
  15. select *
  16. from x 

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

  1.  
  2. with x(dy,dm,mth,dw,wk)
  3. as(
  4. select dy,day(dy) dm,datepart(m,dy) mth,datepart(dw,dy) dw,
  5. case when datepart(dw,dy)=1
  6. then datepart(ww,dy)-1
  7. else datepart(ww,dy) end wk
  8. from ( select dateadd(day,-day(getdate())+1,getdate()) dy from t1) x
  9. union all select dateadd(d,1,dy),day(dateadd(d,1,dy)),mth,
  10. datepart(dw,dateadd(d,1,dy)),
  11. case when datepart(dw,dateadd(d,1,dy))=1
  12. then datepart(wk,dateadd(d,1,dy))-1
  13. else datepart(wk,dateadd(d,1,dy)) end
  14. from x where datepart(m,dateadd(d,1,dy))=mth)
  15. select case dw when 2 then dm end as '星期一',
  16.        case dw when 3 then dm end as '星期二',
  17.        case dw when 4 then dm end as '星期三',
  18.        case dw when 5 then dm end as '星期四',
  19.        case dw when 6 then dm end as '星期五',
  20.        case dw when 7 then dm end as '星期六',
  21.        case dw when 1 then dm end as '星期日'
  22. from x 

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

  1.  
  2. Use testdb
  3. go
  4.  
  5. Declare 
  6.     @Date datetime,
  7.     @StartDate datetime,
  8.     @EndDate datetime,
  9.     @FirstIndex int
  10.  
  11. Set @Date =getdate() --输入一个日期,即可算出当月的日历 比如输入20080808,这里取当前日期
  12.  
  13. Select 
  14.     @StartDate=Convert(char(6),@Date,112)+'01', 
  15.     @EndDate=Dateadd(month,1,@StartDate)-1,
  16.     @FirstIndex=Datediff(day,0,@StartDate)%7 ;
  17. With t As
  18. (
  19.     Select Date=Convert(int,1),Row=(@FirstIndex)/7,Col=@FirstIndex
  20.     Union All
  21.     Select Date=Date+1,Row=(@FirstIndex+Date)/7,Col=(Date+@FirstIndex)%7
  22.     From t
  23.     Where Date<=Datediff(day,@StartDate,@EndDate)
  24. )
  25. Select 
  26.     [星期一]=Isnull(Convert(char(2),[0]),''),
  27.     [星期二]=Isnull(Convert(char(2),[1]),''),
  28.     [星期三]=Isnull(Convert(char(2),[2]),''),
  29.     [星期四]=Isnull(Convert(char(2),[3]),''),
  30.     [星期五]=Isnull(Convert(char(2),[4]),''),
  31.     [星期六]=Isnull(Convert(char(2),[5]),''),
  32.     [星期日]=Isnull(Convert(char(2),[6]),'')
  33. From t
  34. 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. jsp中的js中获取项目路径的方法

    在jsp中加上 <% String path = request.getContextPath(); String basePath = request.getScheme()+":/ ...

  2. sql 事务的四种隔离级别

    在 SQL 标准中定义了四种隔离级别,每一种级别都规定了一个事务中所做的修改,哪些在事务内和事务间是可见的,哪些是不可见的.较低级别的隔离通常可以执行更高的并发,系统的开销也更低. read unco ...

  3. 什么是Docker并且它为什么这么受欢迎

    什么是Docker (why it's so hot than hot) Docker是一个使用容器来方便快捷的创建,部署,运行程序的工具,容器允许开发人员将应用程序的一切打包(镜像),例如库和其他的 ...

  4. python3中SSLError错误处理

    在deepin中安装了python3.6,安装路径为/usr/local/python36,然后通过deepin自带的python2.7的pip安装了virtualenv: sudo pip inst ...

  5. Java泛型底层源码解析--ConcurrentHashMap(JDK1.7)

    1. Concurrent相关历史 JDK5中添加了新的concurrent包,相对同步容器而言,并发容器通过一些机制改进了并发性能.因为同步容器将所有对容器状态的访问都串行化了,这样保证了线程的安全 ...

  6. [Linux]-Linux常用命令之文件解压

    不压缩方式压缩的文件需要不同的命令来解压缩,下面是Linux的各种文件解压命令. 对于.tar结尾的文件: tar -xf 对于.gz结尾的文件 : gzip -d all.gz gunzip all ...

  7. 发现IE 9的一个独有的小bug,并附解决方案

    在最近的项目中,解决了一些浏览器兼容方面的bug,这篇主要描述在IE 9在渲染值为auto的overflow-x属性时,所产生的专属bug及解决办法. 1.问题描述 在做一个收货地址管理静态页面的时候 ...

  8. 创建分区swap分区

    1.将文件系统卸载 #umount /sdc5 2.创建swap分区 #mkswap /dev/sdc5 3.激活swap分区 #swapon -a /dev/sdc5 4.查看swap分区情况 #s ...

  9. 14、BigInteger类简介

    BigInteger类概述 BigInteger类可以让超过Integer范围的数据进行运算,通常在对数字计算比较大的行业中应用的多一些. package com.sutaoyu.usually_cl ...

  10. classList属性

    1.传统方法: 在操作类名的时候,需要通过className属性添加.删除和替换类名.如下面例子: ? 1 <div class="bd user disabled"> ...