墨菲定律

“墨菲定律”是一种心理学效应,由”爱德华·墨菲(Edward A. Murphy)工程师提出的,主要内容,一、任何事都没有表面看起来那么简单;二、所有的事都会比你预计的时间长;三、会出错的事总会出错;四、如果你担心某种情况发生,那么它就更有可能发生。墨菲定律的原句是这样的:如果有两种或两种以上的方式去做某件事情,而其中一种选择方式将导致灾难,则必定有人会做出这种选择。

墨菲定律的原句已经派生出以下的版本:

1.别试图教猫唱歌,这样不但不会有结果,还会惹猫不高兴。
2.别跟傻瓜吵架,不然旁人会搞不清楚,到底谁是傻瓜。
3.不要以为自己很重要,因为没有你,太阳明天还是一样从东方升上来。
4.笑一笑,明天未必比今天好。
5.好的开始,未必就有好结果;坏的开始,结果往往会更糟。
6.你若帮助了一个急需用钱的朋友,他一定会记得你——在他下次急需用钱的时候。
7.有能力的——让他做;没能力的──教他做;做不来的──管理他。
8.你早到了,会议却取消;你准时到,却还要等;迟到,就是迟了。
9.你携伴出游,越不想让人看见,越会遇见熟人。
10.你爱上的人,总以为你爱上他是因为:他使你想起你的老情人。
11.你最后硬着头皮寄出的情书;寄达对方的时间有多长,你反悔的时间就有多长。
12.东西越好,越不中用。
13.一种产品保证60天不会出故障,等于保证第61天一定就会坏掉。
14.东西久久都派不上用场,就可以丢掉;东西一丢掉,往往就必须要用它。
15.你丢掉了东西时,最先去找的地方,往往也是可能找到的最后一个地方。
16.你往往会找到不是你正想找的东西。
17.你出去买爆米花的时候,银幕上偏偏就出现了精彩镜头。
18.另一排总是动的比较快;你换到另一排,你原来站的那一排,就开始动的比较快了;你站的越久,越有可能是站错了排。
19.一分钟有多长? 这要看你是蹲在厕所里面,还是等在厕所外面。
20、计划没有变化快。
21、欠账总是要还的。
22、做恶总是要遭报应的,不是不报,只是时间未到。
23、该来的总是要来的。
24、明天又是一个新的开始。
25、你越是害怕的事物,就越会出现在你的生活中。
26、往往等公车太久没来,就走了的人,刚走公车就来了。
27、关键时刻掉链子。
28、越想要什么就越不能得到什么。
29、人出来混,总是要还的。
30、怕什么,来什么。
31、若想人不知除非己莫为。

CDM、LDM与PDM的区别

概念数据模型设计与逻辑数据模型设计、物理数据模型设计是数据库及数据仓库模型设计的三个主要步骤。

在数据仓库领域有一个概念叫conceptual data model,中文一般翻译为“概念数据模型”。

概念数据模型是最终用户对数据存储的看法,反映了最终用户综合性的信息需求,它以数据类的方式描述企业级的数据需求,数据类代表了在业务环境中自然聚集成的几个主要类别数据。

概念数据模型的内容包括重要的实体及实体之间的关系。在概念数据模型中不包括实体的属性,也不用定义实体的主键。这是概念数据模型和逻辑数据模型的主要区别。

概念数据模型的目标是统一业务概念,作为业务人员和技术人员之间沟通的桥梁,确定不同实体之间的最高层次的关系。

在有些数据模型的设计过程中,概念数据模型是和逻辑数据模型合在一起进行设计的。

在数据仓库领域有一个概念叫logical data model,中文一般翻译为“逻辑数据模型”。

逻辑数据模型反映的是系统分析设计人员对数据存储的观点,是对概念数据模型进一步的分解和细化。逻辑数据模型是根据业务规则确定的,关于业务对象、业务对象的数据项及业务对象之间关系的基本蓝图。

逻辑数据模型的内容包括所有的实体和关系,确定每个实体的属性,定义每个实体的主键,指定实体的外键,需要进行范式化处理。

逻辑数据模型的目标是尽可能详细的描述数据,但并不考虑数据在物理上如何来实现。

逻辑数据建模不仅会影响数据库设计的方向,还间接影响最终数据库的性能和管理。如果在实现逻辑数据模型时投入得足够多,那么在物理数据模型设计时就可以有许多可供选择的方法。

在数据仓库领域有一个概念叫physical data model,中文一般翻译为“物理数据模型”。

物理数据模型是在逻辑数据模型的基础上,考虑各种具体的技术实现因素,进行数据库体系结构设计,真正实现数据在数据库中的存放。

物理数据模型的内容包括确定所有的表和列,定义外键用于确定表之间的关系,基于用户的需求可能进行发范式化等内容。在物理实现上的考虑,可能会导致物理数据模型和逻辑数据模型有较大的不同。

物理数据模型的目标是指定如何用数据库模式来实现逻辑数据模型,以及真正的保存数据。

关于参数化查询的几点不足

参数化查询一般是防止SQL注入的万能型解决方案,查询参数的做法是在准备查询语句时,在对应参数的地方使用参数占位符,在执行这个参选准备好的查询时提供一个参数,但是这个查询参数总是被视为一个字面值,这将导致下列问题。

  1. 多个值的列表不可以当成单一参数。如:
    SELECT * FROM [表] WHERE ID IN (?);
  2. 列名、列名无法做为参数:
    SELECT * FROM ? ORDER BY ?;
  3. SQL关键字不能做为参数:
    SELECT * FROM [表] ORDER BY ID ?;

据说只有程序猿才能看懂的小幽默

1.老婆给当程序员的老公打电话:下班顺路买十个包子,如果看到卖西瓜的,买一个。当晚老公手捧一个包子进了家门…老婆怒道:你怎么只买一个包子?!老公甚恐,喃喃道:因为我真看到卖西瓜的了。”

2.一程序员去面试,面试官问:”你毕业才两年,这三年工作经验是怎么来的?!”程序员答:”加班。”

3.宝宝数学很好,2岁就可以从1数到10了。后来,我告诉他0比1还小。今天吃饺子,我说:”宝宝,你数数你想吃几个饺子?””0,1,2,3。”一边说着一边拿起一个饺子,”这是第0个。”老婆怒吼:”下一代还是做程序员的命!”

4.某程序员对书法十分感兴趣,退休后决定在这方面有所建树。于是花重金购买了上等的文房四宝。 一日,饭后突生雅兴,一番磨墨拟纸,并点上了上好的檀香,颇有王羲之风范,又具颜真卿气势, 定神片刻,泼墨挥毫,郑重地写下一行字:hello world。

5.问:程序员最讨厌康熙的哪个儿子。答:胤禩。因为他是八阿哥(bug)

6.程序猿要了3个孩子,分别取名叫Ctrl、Alt 和Delete,如果他们不听话,程序猿就只要同时敲他们一下就会好的…

7.如果没能一次成功,那就叫它1.0版吧。

8.今天在公司听到一句惨绝人寰骂人的话:”你TM就是一个没有对象的野指针!”

9.莫愁前路无知己,总有bug跟着你。

10.如果世界让程序员落魄,程序员就让世界沉默

11.开源客栈日暮,入栈不知归路。debug无尽头,误入代码深处,单步,单步,发现bug无数。

12.横眉冷对万行码,俯首甘为码字牛。问君能有几多愁,恰似调完代码改需求。

13.bug知时节,上线乃发生。随风潜入夜,404无声。野径云俱黑,孤窗火独明。晓看公司处,一只程序猿。

14.程xx遭遇车祸成植物人,医生说她活下来的希望只有万分之一,唤醒更为渺茫。她的同事和亲人没放弃,并根据程xx对testing痴迷的作风,每天都在她身边念:”你测的模块上线后回滚了…”奇迹发生了,程xx醒来第一句话:确认那模块是我测的?

15.两个程序员,

A:哥们,最近手头紧,能借点钱?
B:成啊,要多少?
A:一千成不?
B:咱俩谁跟谁!给你凑个整,这一千零二十四,拿去吧。
16.一个程序员在海滨游泳时溺水身亡。他死前拼命的呼救,当时海滩上有许多救生员,但是没有人救他。因为他一直大喊”F1!””F1!”,谁都不知道”F1″究竟是什么意思。

17.

1)程序员其实可痛苦的了……需求一做一改,一个月就过去了;嚎~需求再一改一调,一季度就过去了;嚎~
2)、程序员最痛苦的事儿是啥,知道不?就是,程序没做完,需求又改了;
3)、程序员最最痛苦的事儿是啥,知道不? 就是,系统好不容易做完了,方案全改了;
4)、程序员最最最痛苦的事儿是啥,知道不? 就是,系统做完了,狗日的客户跑了;
5)、程序员最最最最最痛苦的事儿是啥,知道不? 就是,狗日的客户又回来了,程序给删没了!
18.有一天一个程序员见到了上帝.

上帝: 小伙子,我可以满足你一个愿望.
程序员: 我希望中国国家队能再次打进世界杯.
上帝: 这个啊!这个不好办啊,你还说下一个吧!
程序员: 那好!我的下一个愿望是每天都能休息6个小时以上.
上帝: 还是让中国国家打进世界杯.
19.做了几年程序员,厌烦了,想换行了。于是天天猛敲回车键。

20.问:一对程序员恋人面对面坐着工作,你猜他们在做什么?答:面向对象编程。

21.十年生死两茫茫,写程序,到天亮。 千行代码,Bug何处藏。 纵使上线又怎样,朝令改,夕断肠。 领导每天新想法,天天改,日日忙。 相顾无言,惟有泪千行。 每晚灯火阑珊处,程序员,又加班,工作狂.

22.世界上最远的距离,是我在if里你在else里,虽然经常一起出现,但却永不结伴执行

23.十行代码九个警告八个错误竟然敢说七日精通六天学会五湖四海也不见如此三心二意之项目经理简直一等下流。

24.程序猿:我的第一个问题是,对于我第二个和第三个问题,你可不可以只用’能’和’不能’来回答?

老板:”OK!”
我的第二个问题是,如果我的第三个问题是我能不能涨工资?那么你对于我的第三个问题的答案能不能和第二个问题的答案一样?
老板:。。。。。。
25.《程序员的雨巷》开着MyEclipse,独自彷徨在悠长、悠长又寂寥的雨巷,我希望逢着一个和我一样的调试代码的姑娘。她是有需求一样的凌乱,设计一样的荒唐,测试一样的忧愁, 在代码里哀怨,哀怨又彷徨;她彷徨在这寂寥的雨巷。

26.一女同学在食堂吃饭时,一程序猿凑到旁边,”同学,我能和你说话不,我已经一个月没和女生说话了。

27.一同学问我,软件外包是什么。解释了几句还没明白,遂想了一下:包工头知道吧?顿悟!

28.正在码代码ing,医院回来的同事一脸的苦逼样子,问他怎么了?他回答:得了类风湿性关节炎了,我怕会遗传给下一代啊。我一脸的问号:谁说类风湿性关节炎能遗传的?丫一脸诧异:类不是继承的吗?

29.真的勇士,敢于直面惨淡的warning、敢于正视淋漓的error。

30.某小偷潜入某IT公司欲行窃,没想到始终有人,他只好等啊等啊等,结果始终有一大拨人在加班。过了一个月,小偷终于逮到机会溜出来,同伙问:去哪了?小偷:在IT公司呆了一个月。同伙:收获不错吧?小偷苦笑:别TM提了,三十个大夜下来,我现在已经学会写程序了。

31.一同事最近bug特别多,假装关切问他有多少个bug,他回答:10。过几日,再问,他回答:10。我:”这么多天过去了,怎么一点变化都没有?”,他:”哪里没有变化啊,以前是十进制,现在变成十六进制。

32.我很奇怪客栈这个词。难道后入住的必须先退房吗?

33.假如生活欺骗了你,找50个程序员问问为什么编程;假如生活让你想死,找50个程序员问问BUG改完了没有;假如你觉得生活拮据,找50个程序员问问工资涨了没有;假如你觉得活着无聊,找50个程序员问问他们一天都干了什么!

34.话说,决定一个程序员跳槽与否的关键因素是他前同事的现工资~

35.程序员最憋屈的事情就是:你辛辛苦苦熬夜写了一个风格优雅的源文件,被一个代码风格极差的同事改了且没署名,以至于别人都以为你是写的。。。

36.昨天晚上做梦梦见我得到了九阴真经的武功秘籍,打开第一页一看,我X,赫然写着口诀”hello world!”

37.前端工程师说,我去交友网站找女朋友去了。朋友问,找到了么?工程师说,找到了他们页面的一个bug……

38.十年生死两茫茫,百度兴,谷歌亡。微博火,推特无处话凄凉。纵使相逢应不识,MSN死,微信狂。人人佳缘喜还乡,京东刘,泡妞强。淘宝在,海淘忙。视频互相打,论坛靠色狼,陌陌帮上床。聊到马云断肠处,余额宝,怕是活不长。

39.程序员被叫去修电脑,正确的回答方式是:『哦,不好意思啊,我是一个程序员,我并不会修电脑,我其实只会把电脑搞出问题来,老实说,你电脑上的这些问题都是我们程序员搞出来的』

40.时至今日都是我咎由自取,找不到对象就是找不到对象,与任何人无关。程序员生涯的苦文明用语,造就了我娇羞内向,不问红尘的脾气,导致今日岌岌可危的地步,我今天愿意承担一切后果。其实,我很感谢你们让我跌倒在今天,而不是在我依旧文明用语丝的将来,我必须重新梳理自己,坦然面对并修复bug!本码农,生活中写就了一段满是bug的代码,我辜负了老板和老板的老板

41.我应聘了一个大型IT公司的”网络攻击研究部经理” 职务, 面试官问我: 你觉得自己为什么适合这份工作? 我: 我黑进你们的系统, 给我自己发了面试通知.

42.老婆让我教她编程,想看看网页是怎么做的,于是我教她用JQuery, PHP 做些简单的小程序。在动手写出些Hello World的小程序后,老婆说:”编程也不是很难嘛,但是你们这些程序员太装逼了,无论前端还是后端的程序,代码里到处都是”$”,你们是想钱想疯了是吧?

43.有个学生在人人发状态:”找不到对象找不到对象找不到对象找不到对象找不到对象”。我回复:”请确定该对象存在,并正确拼写其名称和路径名”。另一个学生回复:”真的无法获取对象就去声明一个基类吧!

44.昨晚梦见男朋友和别的女人在逛街,梦里我的第一反应是查源代码…结果调试半天查不出来为什么显示的是那个女人不是我,最后含泪把那个女人给注释掉了,再一运行就是我男朋友自己逛街了…醒来囧字脸呆了很久…囧rz

45.一个合格的程序员是不会写出 诸如 “摧毁地球” 这样的程序的,他们会写一个函数叫 “摧毁行星”而把地球当一个参数传进去。

46.如果你的朋友是it民工的,年底吃饭聚餐就不要AA制了,你请他吧,主动买单是不错的选择。千万不要当着他的面提年终奖,出国旅游,买房买车等等。生活不易,需要互相扶持前行。

47.设计师看了程序实现出的界面内心百感交集,就像把女儿嫁给了小混混,最后生出了一窝小流氓似的。

48.真正的程序员喜欢兼卖爆米花,他们利用CPU散发出的热量做爆米花,可以根据米花爆裂的速度听出正在运行什么程序。

49.祝大家在以后的日子里. 男生象Oracle般健壮; 女生象win7般漂亮; 桃花运象IE中毒般频繁; 钱包如Gmail容量般壮大, 升职速度赶上微软打补丁 , 追女朋友像木马一样猖獗, 生活像重装电脑后一样幸福, 写程序敲代码和聊天一样有激情。

50.老板说了一个笑话,全办公室哄堂大笑,有抹眼泪的,有捂肚子的,有捶桌子的。只有小猿没笑,我边笑边问他:你怎么不笑呀?他说:我已经辞职了!

51.程序员最讨厌的四件事:写注释、写文档、别人不写注释、别人不写文档 …

52.一个士兵爱上一个公主,公主告诉他,如果他愿意连续100个晚上守在她的阳台下,她就接受他。于是士兵照做了,他等了一天,两天,三天……直到第九十九天,士兵离开了。为什么士兵不再坚持最后一天?答案很凄美——因为那个士兵是程序员,他从0开始数的.

53.一晚下班回家,一民警迎面巡逻而来。突然对我大喊:站住!

民警:int类型占几个字节?
我:4个。
民警:你可以走了。
我感到很诧异。
我:为什么问这样的问题?
民警:深夜还在街上走,寒酸苦逼的样子,不是小偷就是程序员……

SQL Server——自定义的fn_Split函数

修正SQL2000超长ntext的bug。

CREATE FUNCTION [dbo].[Fn_split] (@expression NTEXT,
                                  @delimiter  NVARCHAR(2) = ',')
RETURNS @expressionstable TABLE (
  [DUMMY] NVARCHAR(4000))
AS
  BEGIN
      DECLARE @currentindex INT
      DECLARE @nextindex INT
      DECLARE @returntext NVARCHAR(4000)
      DECLARE @datalength INT
      IF @expression IS NULL
        BEGIN
            INSERT INTO @expressionstable
                        ([DUMMY])
            VALUES      (NULL)
        END
      ELSE
        BEGIN
            SELECT @datalength = Datalength(@expression) / 2
            IF @datalength = 0
              INSERT INTO @expressionstable
                          ([DUMMY])
              VALUES      ('')
            ELSE
              BEGIN
                  SELECT @currentindex = 1
                  WHILE( @currentindex <= @datalength )
                    BEGIN
                        SELECT @nextindex = Charindex(@delimiter, Substring(@expression, @currentindex, 4000))
                                            + @currentindex
                        /*关键在于Charindex第二个参数最长为8000,所以每次需重新截取下一部分*/
                        IF( @nextindex = @currentindex )
                          SELECT @nextindex = @currentindex + @datalength
                        SELECT @returntext = Substring(@expression, @currentindex - 1, @nextindex - @currentindex)
                        INSERT INTO @expressionstable
                                    ([DUMMY])
                        VALUES      (@returntext)
                        SELECT @currentindex = @nextindex + 1
                    END
              END
        END
      RETURN
  END

 

CREATE FUNCTION [dbo].[Fn_split] (@str       VARCHAR(MAX),
                                  @separator VARCHAR(10))
RETURNS TABLE
AS
    RETURN
      (SELECT T0.DUMMY
       FROM   ( (SELECT [DUMMY] = CONVERT(XML, '<DUMMY>'
                                             + Replace(@str, @separator, '</DUMMY><DUMMY>')
                                             + '</DUMMY>')) T1
                OUTER APPLY (SELECT DUMMY = N.v.value('.', 'NVARCHAR(4000)')
                             FROM   T1.[DUMMY].nodes('/DUMMY') N(v)) T0 ))

用法:

SELECT * FROM fn_split('1,2,3,4,5', ',');

重弹Transact-SQL的随机数

Transact-SQL的随机数主要有:

RAND:返回一个介于 0 到 1(不包括 0 和 1)之间的伪随机 float 值。如果未指定 seed,则 SQL Server 数据库引擎随机分配种子值。 使用同一个种子值重复调用 RAND() 会返回相同的结果。对于一个连接,如果使用指定的种子值调用 RAND(),则 RAND() 的所有后续调用将基于使用该指定种子值的 RAND() 调用生成结果。

NEWID:创建 uniqueidentifier 类型的唯一值。NEWID 对每台计算机返回的值各不相同。

CHECKSUM:返回按照表的某一行或一组表达式计算出来的校验和值,用于生成哈希索引。CHECKSUM 值取决于排序规则。使用不同排序规则存储的相同值将返回一个不同的 CHECKSUM 值。(竟然可以把字符串转为数字!!!)

因为RAND()使用同一个种子值重复调用 RAND() 会返回相同的结果,所以会造成了一个后果,SELECT到所有列全部是同一个值,如果这个值需要同其它值进行计算,那就无法真正的随机了。

此时就需要CHECKSUM(NEWID())甚至RAND(CHECKSUM(NEWID()))出场了。

具体怎么用,多试就知道了。

[抛砖引玉]换个思路解决SQL经典问题(二):时间区间-按年分月统计

声明:本篇文章的SQL语句为了体现作者的思路,并非最优,请根据实际需要进行优化。

曾经帮别人解决一个这样的问题:

e_class_table

CREATE TABLE [dbo].[E_Classes] ([ClassID] varchar(12) NOT NULL ,[BeginDate] date NULL ,[EndDate] date NULL ,[StudentCount] smallint NULL);
INSERT INTO [dbo].[E_Classes] ([ClassID], [BeginDate], [EndDate], [StudentCount]) VALUES (N'01', N'2011-10-07', N'2012-03-11', N'49');
INSERT INTO [dbo].[E_Classes] ([ClassID], [BeginDate], [EndDate], [StudentCount]) VALUES (N'02', N'2012-04-05', N'2012-06-20', N'23');
INSERT INTO [dbo].[E_Classes] ([ClassID], [BeginDate], [EndDate], [StudentCount]) VALUES (N'03', N'2012-05-01', N'2012-07-03', N'46');
INSERT INTO [dbo].[E_Classes] ([ClassID], [BeginDate], [EndDate], [StudentCount]) VALUES (N'04', N'2013-03-08', N'2013-06-12', N'42');
INSERT INTO [dbo].[E_Classes] ([ClassID], [BeginDate], [EndDate], [StudentCount]) VALUES (N'05', N'2013-09-19', N'2013-10-26', N'78');
INSERT INTO [dbo].[E_Classes] ([ClassID], [BeginDate], [EndDate], [StudentCount]) VALUES (N'06', N'2013-11-21', N'2014-01-09', N'44');
INSERT INTO [dbo].[E_Classes] ([ClassID], [BeginDate], [EndDate], [StudentCount]) VALUES (N'07', N'2013-12-27', N'2014-02-06', N'36');
INSERT INTO [dbo].[E_Classes] ([ClassID], [BeginDate], [EndDate], [StudentCount]) VALUES (N'08', N'2014-02-28', N'2014-06-20', N'52');

要求按年和月分别统计全部课程的统计每个月的人天次,即:

class_total

计算方法为 (每门课在当月的有效天数*人数)的总和,如2014年01月的应该为 课程06的 2014-01-01~2014-01-09共计9天*44人=396人天 再加上 课程07的2014-01-01~2014-01-31共计31天*36人=1116人天,那么2014年01月的人天为 396+1116=1512人天次。

这个问题看上去挺麻烦的,但是我们可以换个思路去考虑,先建一张序号表:

CREATE TABLE [dbo].[E_Sequence]([Sequence_ID] [int] IDENTITY(1,1) NOT NULL, CONSTRAINT [PK_E_Sequence] PRIMARY KEY CLUSTERED ([Sequence_ID] ASC)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]);
SET IDENTITY_INSERT [dbo].[E_Sequence] ON
INSERT INTO [dbo].[E_Sequence] (Sequence_ID)
SELECT number FROM [master]..[spt_values] WHERE  type = 'P'
UNION ALL
SELECT number + 2048 FROM [master]..[spt_values] WHERE  type = 'P'
SET IDENTITY_INSERT [dbo].[E_Sequence] OFF

那。。。这张序号表是干什么用的呢?看看下面就知道了:

SELECT Dateadd(day, Sequence_id, '2011-1-1') AS Class_Date FROM E_Sequence

通过这个语句,我们可以生成一个从2011年01月01日开始的4000天,我们将用到这个查询来将课程表的时间段,拆分到每一天去:

SELECT Year(Class_Date)  AS Class_Year,
       Month(Class_Date) AS Class_Month,
       StudentCount
FROM   (SELECT Dateadd(day, Sequence_id, '2011-1-1') AS Class_Date
        FROM   E_Sequence) t1,
       E_Classes t2
WHERE  t1.Class_Date BETWEEN t2.BeginDate AND t2.EndDate

看看效果:

class_split

OK,这样,是不是就很明了了呢?“年”字段有了,“月”字段有了,“学生数”是不是就可以简单地加了?然后再一个行列转换就可以实现了(SQL2000请自行转为Case When End):

SELECT Class_Year      AS 年分,
       Isnull([1], 0)  AS 一月,
       Isnull([2], 0)  AS 二月,
       Isnull([3], 0)  AS 三月,
       Isnull([4], 0)  AS 四月,
       Isnull([5], 0)  AS 五月,
       Isnull([6], 0)  AS 六月,
       Isnull([7], 0)  AS 七月,
       Isnull([8], 0)  AS 八月,
       Isnull([9], 0)  AS 九月,
       Isnull([10], 0) AS 十月,
       Isnull([11], 0) AS 十一月,
       Isnull([12], 0) AS 十二月
FROM   (SELECT Year(Class_Date)  AS Class_Year,
               Month(Class_Date) AS Class_Month,
               studentcount
        FROM   (SELECT Dateadd(day, Sequence_id, '2011-1-1') AS Class_Date
                FROM   E_Sequence) t1,
               e_classes t2
        WHERE  t1.Class_Date BETWEEN t2.begindate AND t2.enddate) t 
        PIVOT (Sum(studentcount) FOR Class_Month IN
        ([1], [2], [3], [4], [5], [6], [7], [8], [9], [10], [11], [12])) tpivot

看到了吧?有时,引入一张外表,可以让我们的思路更清楚。

如果将

SELECT Dateadd(day, Sequence_id, '2011-1-1') AS Class_Date FROM E_Sequence

这句生成的查询,直接做成一张日期的序列表再加上索引的话,那在性能上会快更多,具体优化就不多讲了。

继续留个尾巴:

请只统计2012年07月16日到2013年06月15日之间,排除双休日的每个月的人天次。