T探讨的深层逻辑是什么?

2026-05-27 06:261阅读0评论SEO问题
  • 内容介绍
  • 文章标签
  • 相关推荐

本文共计4235个文字,预计阅读时间需要17分钟。

T探讨的深层逻辑是什么?

本帖首先探讨IT领域的人的职业规划,再回到技术面,研究T-SQL中的UNION、EXISTS、EXCEPT、INTERSECT运算符。前有好友提及:DBA理解业务是非常必要的,确实如此。不管您追求的是IT的哪种职业,深入理解业务都是至关重要的。

本帖先探讨 IT 人的职涯规划,再回到技术面,研究 T-SQL 的 UNION、EXISTS、EXCEPT、INTERSECT 运算符。

之前有园友提到:「DBA 了解业务是非常必要的」,确实如此。不管您担任的是 IT 的哪种职务,都应深入了解所任职公司的业务和流程,结合既有的 IT 专长,发展出自己在公司的「不可替代性」,甚至去思考老板、公司的好高高层们,想要的是什么? 若您是担任 MIS 或 DBA,每天辛勤努力修机器、备份数据库、维护网络状态、研究最新的前台和 .NET 技术,却无法让老板了解你每天在忙什么,则你的地位,可能远不如一位很懂业务和流程的人,虽然他只会写些 SQL 句子,却能每天产出老板和主管想看的报表,老板不能没有他,他也就创造了自己的不可替代性。 当然做项目的软件公司,和一般产业的信息部门又有所不同,这里只是举例。

那么该学什么技术最保值呢?有没有什么 IT 技术像会计学一样,学一套就能吃一辈子?前台 UI 技术日新月异、层出不穷,感觉自己再也跟不上年轻人熬夜学 ASP.NET 4,5,6,7,8 ...、Silverlight 5,6,7,8,9 ...? 若要论技术的保值性,将 SQL 和数据库学到精通,或许是不错的选择。除了技术变化性比较小 (至少不会像某些技术会忽然推翻旧版本),往上钻研可研究 BI、OLAP、Data Mining、报表、当顾问,往下钻研可深入学 SQL 语句、软件项目开发、备份还原和监控,横向钻研可学习操作系统、Storage、性能优化 .... 等等,至少就技术的「保值性」而言,学一套能「撑」较久,甚至大半辈子,也较有机会接触公司的核心业务和流程 (把公司的业务逻辑都写进存储过程里,公司从此不能没有你)。

当然这里只是举例,并非鼓吹大家都该往此方向投入学习,就像有人说程序员最后都要升级 SA、PM,但这应视个人性格、兴趣、所在的产业而论,并没有标准的答案。但话说回来,要精通 SQL 的撰写也非易事,其为易学难精的功夫,如笔者以前曾经转贴的一篇陈年好文「程序员真情忏悔录」也曾提到这些特性:

做应用软件的人真的没价值吗?有些靠应用软件赚了不少钱的朋友开始偷笑了。软件必须加上专业知识,才能够发挥价值;换句话说,如果您真的「只会写代码」,却没有配合各种领域的专业知识 ,「编程」这项技能本身根本毫无价值可言。

就很像你的计算机装了浏览器,但却没有对外联机的网络一样。如果没有网络本身,浏览器一点价值也没有,更不需要一家公司大费周章地利用平台优势去整倒另外一家公司 。


-你老觉得真正的高手应该精通各门各派的技术 (?),如果你会 XML,他不会,你就觉得你比他厉害。你觉得他的履历上写的技能太少,证照太少,所以你认为你比他优秀?


有人认为写 Java 程序应该善用工具,用 UltraEdit 根本是重新造轮子的行为,所以一开始就学 JBuilder (或 Visual Studio) 的使用者,其实他用 JBuilder 写了老半天 GUI 程序,哪天回头叫他用文本编辑器写个简单的 Frame + Button 他却写不出来,因为他从没弄懂过 Java 的事件处理模型。他只会不断地:选择组件 -> 放在容器里 -> 调整位置和大小 -> 调整属性 -> 按两下 -> 填写事件处理函数,成为一个名副其实的「代码女工」。


有人觉得他精通各家厂商的数据库,所以看不起那些只会下 SQL 命令或是只会写 stored procedure 的人,因为他可是精通 ODBC、JDBC、ADO、ADO.NET 各种程序的写法。问题是,一个精通 SQL 的专家,和只会写 SQL 语句的人,在数据库表格交互参考、数据量很大的时候,要从中取出我们需要的内容,所下的 SQL 指令在效率上,是几秒钟和几个小时的差别。

SQL 也是个专门学问,要能够巧妙地操作它,必须下非常多功夫做研究,而且一研究可能就是十几年。如果贵公司的项目老是苦于数据库访问的性能不佳,你猜老板会花钱找一个有能力彻底改善所有 SQL 语句中性能问题的稀有专家,还是再找一个号称他什么都会,结果一点用场也派不上的「数据库女工」? 我们常常看到某人列出他的履历,好像会很多就是很厉害。但是当我们完全深入一项技术时 (喔,我是说你真正下过苦功的时候),通常我们会越来越感觉到自己的渺小。



说偏了,回归技术面,以下才是本帖的主题。

-------------------------------------------------
本帖的示例下载:
files.cnblogs.com/WizardWu/111001.zip


SQL Server 2008 R2 的 AdventureWorks 数据库下载:
msftdbprodsamples.codeplex.com/releases/view/55926
msftdbprodsamples.codeplex.com/
---------------------------------------------------

The EXISTS Operator
EXISTS 可称之为运算符,有些书称它为关键词。 EXISTS 和 IN 关键词很类似,但仍有些不同,EXISTS 使用时不会返回数据,而是返回简单的 TRUE / FALSE。如下示例 [1],即使子查询返回 NULL,用 EXISTS 也会得到 TRUE:


USEAdventureWorks2008;
GO

SELECTDepartmentID,Name
FROMHumanResources.Department
WHEREEXISTS(SELECTNULL)

EXISTS 和 IN 在很多情况下可查到相同的结果 [1],两个关键词的用途有些类似,因此网络上常有些 EXISTS 和 IN 谁效率较好的讨论串 [2],此处不再赘述。 以下我们改探讨 EXISTS 和 JOIN 的比较,下方是书上的两个示例 [7],两者的执行结果相同,第一个示例是 EXISTS 搭配「嵌套查询 (nested query)」,第二个示例语法看起来较简洁,是传统大家习惯用的 join 写法:


SELECTe.BusinessEntityID,FirstName,LastName
FROMHumanResources.Employeee
JOINPerson.Personpp
ONe.BusinessEntityID=pp.BusinessEntityID
WHEREEXISTS
(SELECTBusinessEntityID
FROMHumanResources.JobCandidatejc
WHEREe.BusinessEntityID=jc.BusinessEntityID);
上方示例、下方示例,执行结果皆为:
EmployeeID  FirstName  LastName
-----------  ------------  -------------
  212     Peng     Wu
  274     Stephen   Jiang

(2 row(s) affected)


SELECTe.BusinessEntityID,FirstName,LastName
FROMHumanResources.Employeee
JOINPerson.Personpp
ONe.BusinessEntityID=pp.BusinessEntityID
JOINHumanResources.JobCandidatejc
ONe.BusinessEntityID=jc.BusinessEntityID;

两个示例虽然结果相同,但 EXISTS 的性能会较好 (书上说的) [7]。 当我们使用 EXISTS 关键词时,SQL Server 不会用 full row-by-row join,而是在记录当中搜寻,当它找到第一个符合条件的记录时,就会立即停止后续搜寻的动作,并标示为 TRUE,表示不需要再往下找了;反观 inner join 则不会有此种标示的动作。

此外,NOT EXISTS 也有此种标示的功能。当 NOT EXISTS 找到符合条件的数据时,同样也会标示,但标示为 FALSE,表示不需要再往下找了。


除了此一性能差别外,EXISTS 和 join-based 在查询时,各方面并无二致。



Using EXISTS in Other Ways


有时您会看到有些人执行 CREATE 前,会加一些古怪的语句,如下:


IFEXISTS(SELECT*FROMsysobjectsWHEREid=
object_id(N'[Sales].[SalesOrderHeader]')ANDOBJECTPROPERTY(id,N'IsUserTable')=1)
DROPTABLE[Sales].[SalesOrderHeader]
GO

CREATETABLE[Sales].[SalesOrderHeader](
...
...

他们使用 sys.objects、sys.databases,或 INFORMATION_SCHEMA 检视 (view),目的都一样,想在执行 CREATE 前先看看某个对象是否已存在。 但同样是用 EXISTS 做事先的判断,我有更好的写法,请参考以下创建数据库的示例:


USEmaster
GO

IFNOTEXISTS(SELECT'True'FROMsys.databasesWHEREname='DBCreateTest')
BEGIN
CREATEDATABASEDBCreateTest
END
ELSE
BEGIN
PRINT'此数据库已经存在。跳过了CREATEDATABASE语句'
END
GO

第一次执行此语句时,若没有 DBCreateTest 数据库,则创建它;第二次执行时,由于该数据库已存在,因此会印出自定义的提示信息。 因此,仅用一点小技巧,可避免掉不必要的 DROP 动作被执行,这会让您公司的产品在被安装时更有效率。

EXISTS 是很方便的关键词,有时可让查询语句执行得更有效率,有时则可简化 SQL 语句。

The INTERSECT and EXCEPT Operators


接下来介绍的 INTERSECT 和 EXCEPT 关键词,在 SQL Server 和其他厂牌数据库多半都支持。 INTERSECT 和 EXCEPT 在处理两个 result set 时,和 UNION 关键词很类似。 在 MSDN、TechNet 上虽然有对这两个关键词作解释 [3], [6],但半机器的翻译有些不易理解,建议参考下列取自书上的图文示例 [7],可让人一目了然:

  • UNION: 将多个「结果集 (result set)」的「行 (row)」合并,作为单个结果集返回,并移除重复的行。若有重复的行,只留下一个。
  • UNION ALL: 将多个「结果集 (result set)」所有的行合并,不论是否有重复的行。
  • EXCEPT: 提取只在 EXCEPT 左侧存在,但右侧不存在的行,参考下图 1。用更口语化的说法:「只给我 A 里才有,但 B 里没有的行」。
  • INTERSECT: 只提取两个结果集里,都存在的行。 INTERSECT 很类似 inner join,但 INTERSECT 并不会对特定的「列 (column)」去做处理。

由于上述关键词,不会对特定的「列」去做处理,因此在使用上必须符合某些原则 [3],例如:所有查询中的列数和列的顺序必须相同、数据类型必须兼容。


图 1 UNION 中若有重复的行,会被移除,只留下一个

在看本文最后的完整比较示例前,我们先了解 EXCEPT 和 INTERSECT 的基本语法。

EXCEPT

T探讨的深层逻辑是什么?


EXCEPT 用法如下,简单易懂:

<table or tabular result>
EXCEPT
<table or tabular result with same number of columns and type as top query>

同样的查询需求,我们改用 NOT EXISTS 的写法,也能得到和 EXCEPT 写法相同的结果,用法如下 [1], [7]。 本文最后会有完整的示例。

<basequery>
WHERENOTEXISTS
(SELECT1
FROM<tableorresultwithsamenumberofcolumnsandtypeastopquery>
WHERE<basequeryfirstcolumn>=<comparisontablefirstcolumn>[,...])

INTERSECT

至于 INTERSECT 的语法也一样简单易懂,用法和 EXCEPT、UNION 也都类似,如下:

<table or tabular result>
INTERSECT
<table or tabular result with same number of columns and type as top query>


同样的查询需求,我们改用EXISTS 的写法,也能得到和 INTERSECT 写法相同的结果,用法如下 [1], [7]。 本文最后会有完整的示例。


<basequery>
WHEREEXISTS
(SELECT1
FROM<tableorresultwithsamenumberofcolumnsandtypeastopquery>
WHERE<basequeryfirstcolumn>=<comparisontablefirstcolumn>[,...])

此外,INTERSECT 和 EXCEPT 可同时混搭使用,但 INTERSECT 运算符会优先于 EXCEPT[6]:

SELECT*FROMTableA
EXCEPT
SELECT*FROMTableB
INTERSECT
SELECT*FROMTableC



Comparing EXCEPT and INTERSECT with Their EXISTS and NOT EXISTS Equivalents


以下的示例,我们将演示本帖前述的所有内容,先看 EXCEPT、INTERSECT 的示例,再看与他们俩对等的 NOT EXISTS、EXISTS 写法示例 (不同写法,但会返回相同的结果集)。


完整示例 1--Createourtesttablesandpopulatethemwithafewrelevantrows
2CREATETABLEUnionTest1
3(
4idcolintIDENTITY,
5col2char(3),
6);
7
8CREATETABLEUnionTest2
9(
10idcolintIDENTITY,
11col4char(3),
12);
13
14INSERTINTOUnionTest1VALUES('AAA'),('BBB'),('CCC');
15INSERTINTOUnionTest2VALUES('CCC'),('DDD'),('EEE');
16
17
18PRINT'测试1,Sourceandcontentofbothtables:';
19
20SELECT1ASSourceTable,col2ASValueFROMUnionTest1
21UNIONALL
22SELECT2,col4FROMUnionTest2;
23
24
25PRINT'测试2,ResultswithclassicUNION';
26
27SELECTcol2FROMUnionTest1
28UNION
29SELECTcol4FROMUnionTest2;
30
31
32PRINT'测试3-1,ResultswithEXCEPT';
33
34SELECTcol2FROMUnionTest1
35EXCEPT
36SELECTcol4FROMUnionTest2;
37
38
39PRINT'测试3-2,EquivilentofEXCEPTbutusingNOTEXISTS';
40
41SELECTcol2FROMUnionTest1ut1
42WHERENOTEXISTS
43(SELECTcol4FROMUnionTest2WHEREcol4=ut1.col2);
44
45
46PRINT'测试4-1,ResultswithINTERSECT';
47
48SELECTcol2FROMUnionTest1
49INTERSECT
50SELECTcol4FROMUnionTest2;
51
52
53PRINT'测试4-2,EquivilentofINTERSECTbutusingEXISTS';
54
55SELECTcol2FROMUnionTest1ut1
56WHEREEXISTS
57(SELECTcol4FROMUnionTest2WHEREcol4=ut1.col2);
58
59
60PRINT'测试5,INTERSECT运算符优先于EXCEPT';
61SELECTcol2FROMUnionTest1
62EXCEPT
63SELECTcol2FROMUnionTest1
64INTERSECT
65SELECTcol4FROMUnionTest2;
66
67
68--Cleanupafterourselves
69DROPTABLEUnionTest1;
70DROPTABLEUnionTest2;

执行结果:

测试 1 结果      
测试2 结果   
测试3-1、3-2 结果 
测试4-1、4-2 结果 
测试5 结果

如您所见,测试 3-1、3-2 的结果相同,测试 4-1、4-2 的结果也相同,但 SQL Server 的「评估的执行计划」很不同,且 EXCEPT / INTERSECT 的「查询开销」会比 NOT EXISTS / EXISTS 大很多,如同上例中,测试 3-1 比 3-2 的性能差,测试 4-1 比 4-2 性能差。而且经我测试 (书籍作者[7]),大多数的情形,用 EXISTS 的写法,性能都比 EXCEPT / INTERSECT 要好。

虽然如此,但我们不该完全舍弃 EXCEPT / INTERSECT 的使用,因为他们的语法简洁、容易阅读。因此在您的 SQL 语句中,除非两种写法的性能差距很大,不然我仍建议使用 EXCEPT / INTERSECT 写法,以便项目后续的维护。若论 EXISTS 和 EXCEPT / INTERSECT 哪一种写法较好,则属见仁见智的问题,端视您的数据量、执行环境、实测两种写法的效率差距,来决定要用哪一种写法。




参考文档:

[1] EXISTS (Transact-SQL)
msdn.microsoft.com/zh-cn/library/ms188336.aspx

[2] SQL 中 IN 和 EXISTS 用法的区别?
topic.csdn.net/u/20090715/10/ec21e6cc-7265-4c44-a35c-8a0003e73978.html

[3] EXCEPT 和 INTERSECT (Transact-SQL)
msdn.microsoft.com/zh-cn/library/ms188055.aspx

[4] UNION (Transact-SQL)
msdn.microsoft.com/zh-cn/library/ms180026.aspx

[5] 与其他 Transact-SQL 语句一起使用 UNION、EXCEPT 和 INTERSECT
msdn.microsoft.com/zh-cn/library/ms191523.aspx

[6] 使用 EXCEPT 和 INTERSECT 执行半联接操作
msdn.microsoft.com/zh-cn/library/ms191255.aspx



参考书籍:

[7] Professional Microsoft SQL Server 2008 Programming, Chapter 3
www.wrox.com/WileyCDA/WroxTitle/Professional-Microsoft-SQL-Server-2008-Programming.productCd-0470257024.html
www.ppurl.com/pdfpreview/?skey=AQQHNgokAXRXMVA7UAxQP1Z0XTkJYwBtB2ZUZQI6UzQ%3D&page=0



相关文章:

[8] 探讨 .NET 4 新增的 SortedSet 类 (.NET 平台的 EXCEPT、INTERSECT)
www.cnblogs.com/WizardWu/archive/2010/06/17/1759297.html


本文共计4235个文字,预计阅读时间需要17分钟。

T探讨的深层逻辑是什么?

本帖首先探讨IT领域的人的职业规划,再回到技术面,研究T-SQL中的UNION、EXISTS、EXCEPT、INTERSECT运算符。前有好友提及:DBA理解业务是非常必要的,确实如此。不管您追求的是IT的哪种职业,深入理解业务都是至关重要的。

本帖先探讨 IT 人的职涯规划,再回到技术面,研究 T-SQL 的 UNION、EXISTS、EXCEPT、INTERSECT 运算符。

之前有园友提到:「DBA 了解业务是非常必要的」,确实如此。不管您担任的是 IT 的哪种职务,都应深入了解所任职公司的业务和流程,结合既有的 IT 专长,发展出自己在公司的「不可替代性」,甚至去思考老板、公司的好高高层们,想要的是什么? 若您是担任 MIS 或 DBA,每天辛勤努力修机器、备份数据库、维护网络状态、研究最新的前台和 .NET 技术,却无法让老板了解你每天在忙什么,则你的地位,可能远不如一位很懂业务和流程的人,虽然他只会写些 SQL 句子,却能每天产出老板和主管想看的报表,老板不能没有他,他也就创造了自己的不可替代性。 当然做项目的软件公司,和一般产业的信息部门又有所不同,这里只是举例。

那么该学什么技术最保值呢?有没有什么 IT 技术像会计学一样,学一套就能吃一辈子?前台 UI 技术日新月异、层出不穷,感觉自己再也跟不上年轻人熬夜学 ASP.NET 4,5,6,7,8 ...、Silverlight 5,6,7,8,9 ...? 若要论技术的保值性,将 SQL 和数据库学到精通,或许是不错的选择。除了技术变化性比较小 (至少不会像某些技术会忽然推翻旧版本),往上钻研可研究 BI、OLAP、Data Mining、报表、当顾问,往下钻研可深入学 SQL 语句、软件项目开发、备份还原和监控,横向钻研可学习操作系统、Storage、性能优化 .... 等等,至少就技术的「保值性」而言,学一套能「撑」较久,甚至大半辈子,也较有机会接触公司的核心业务和流程 (把公司的业务逻辑都写进存储过程里,公司从此不能没有你)。

当然这里只是举例,并非鼓吹大家都该往此方向投入学习,就像有人说程序员最后都要升级 SA、PM,但这应视个人性格、兴趣、所在的产业而论,并没有标准的答案。但话说回来,要精通 SQL 的撰写也非易事,其为易学难精的功夫,如笔者以前曾经转贴的一篇陈年好文「程序员真情忏悔录」也曾提到这些特性:

做应用软件的人真的没价值吗?有些靠应用软件赚了不少钱的朋友开始偷笑了。软件必须加上专业知识,才能够发挥价值;换句话说,如果您真的「只会写代码」,却没有配合各种领域的专业知识 ,「编程」这项技能本身根本毫无价值可言。

就很像你的计算机装了浏览器,但却没有对外联机的网络一样。如果没有网络本身,浏览器一点价值也没有,更不需要一家公司大费周章地利用平台优势去整倒另外一家公司 。


-你老觉得真正的高手应该精通各门各派的技术 (?),如果你会 XML,他不会,你就觉得你比他厉害。你觉得他的履历上写的技能太少,证照太少,所以你认为你比他优秀?


有人认为写 Java 程序应该善用工具,用 UltraEdit 根本是重新造轮子的行为,所以一开始就学 JBuilder (或 Visual Studio) 的使用者,其实他用 JBuilder 写了老半天 GUI 程序,哪天回头叫他用文本编辑器写个简单的 Frame + Button 他却写不出来,因为他从没弄懂过 Java 的事件处理模型。他只会不断地:选择组件 -> 放在容器里 -> 调整位置和大小 -> 调整属性 -> 按两下 -> 填写事件处理函数,成为一个名副其实的「代码女工」。


有人觉得他精通各家厂商的数据库,所以看不起那些只会下 SQL 命令或是只会写 stored procedure 的人,因为他可是精通 ODBC、JDBC、ADO、ADO.NET 各种程序的写法。问题是,一个精通 SQL 的专家,和只会写 SQL 语句的人,在数据库表格交互参考、数据量很大的时候,要从中取出我们需要的内容,所下的 SQL 指令在效率上,是几秒钟和几个小时的差别。

SQL 也是个专门学问,要能够巧妙地操作它,必须下非常多功夫做研究,而且一研究可能就是十几年。如果贵公司的项目老是苦于数据库访问的性能不佳,你猜老板会花钱找一个有能力彻底改善所有 SQL 语句中性能问题的稀有专家,还是再找一个号称他什么都会,结果一点用场也派不上的「数据库女工」? 我们常常看到某人列出他的履历,好像会很多就是很厉害。但是当我们完全深入一项技术时 (喔,我是说你真正下过苦功的时候),通常我们会越来越感觉到自己的渺小。



说偏了,回归技术面,以下才是本帖的主题。

-------------------------------------------------
本帖的示例下载:
files.cnblogs.com/WizardWu/111001.zip


SQL Server 2008 R2 的 AdventureWorks 数据库下载:
msftdbprodsamples.codeplex.com/releases/view/55926
msftdbprodsamples.codeplex.com/
---------------------------------------------------

The EXISTS Operator
EXISTS 可称之为运算符,有些书称它为关键词。 EXISTS 和 IN 关键词很类似,但仍有些不同,EXISTS 使用时不会返回数据,而是返回简单的 TRUE / FALSE。如下示例 [1],即使子查询返回 NULL,用 EXISTS 也会得到 TRUE:


USEAdventureWorks2008;
GO

SELECTDepartmentID,Name
FROMHumanResources.Department
WHEREEXISTS(SELECTNULL)

EXISTS 和 IN 在很多情况下可查到相同的结果 [1],两个关键词的用途有些类似,因此网络上常有些 EXISTS 和 IN 谁效率较好的讨论串 [2],此处不再赘述。 以下我们改探讨 EXISTS 和 JOIN 的比较,下方是书上的两个示例 [7],两者的执行结果相同,第一个示例是 EXISTS 搭配「嵌套查询 (nested query)」,第二个示例语法看起来较简洁,是传统大家习惯用的 join 写法:


SELECTe.BusinessEntityID,FirstName,LastName
FROMHumanResources.Employeee
JOINPerson.Personpp
ONe.BusinessEntityID=pp.BusinessEntityID
WHEREEXISTS
(SELECTBusinessEntityID
FROMHumanResources.JobCandidatejc
WHEREe.BusinessEntityID=jc.BusinessEntityID);
上方示例、下方示例,执行结果皆为:
EmployeeID  FirstName  LastName
-----------  ------------  -------------
  212     Peng     Wu
  274     Stephen   Jiang

(2 row(s) affected)


SELECTe.BusinessEntityID,FirstName,LastName
FROMHumanResources.Employeee
JOINPerson.Personpp
ONe.BusinessEntityID=pp.BusinessEntityID
JOINHumanResources.JobCandidatejc
ONe.BusinessEntityID=jc.BusinessEntityID;

两个示例虽然结果相同,但 EXISTS 的性能会较好 (书上说的) [7]。 当我们使用 EXISTS 关键词时,SQL Server 不会用 full row-by-row join,而是在记录当中搜寻,当它找到第一个符合条件的记录时,就会立即停止后续搜寻的动作,并标示为 TRUE,表示不需要再往下找了;反观 inner join 则不会有此种标示的动作。

此外,NOT EXISTS 也有此种标示的功能。当 NOT EXISTS 找到符合条件的数据时,同样也会标示,但标示为 FALSE,表示不需要再往下找了。


除了此一性能差别外,EXISTS 和 join-based 在查询时,各方面并无二致。



Using EXISTS in Other Ways


有时您会看到有些人执行 CREATE 前,会加一些古怪的语句,如下:


IFEXISTS(SELECT*FROMsysobjectsWHEREid=
object_id(N'[Sales].[SalesOrderHeader]')ANDOBJECTPROPERTY(id,N'IsUserTable')=1)
DROPTABLE[Sales].[SalesOrderHeader]
GO

CREATETABLE[Sales].[SalesOrderHeader](
...
...

他们使用 sys.objects、sys.databases,或 INFORMATION_SCHEMA 检视 (view),目的都一样,想在执行 CREATE 前先看看某个对象是否已存在。 但同样是用 EXISTS 做事先的判断,我有更好的写法,请参考以下创建数据库的示例:


USEmaster
GO

IFNOTEXISTS(SELECT'True'FROMsys.databasesWHEREname='DBCreateTest')
BEGIN
CREATEDATABASEDBCreateTest
END
ELSE
BEGIN
PRINT'此数据库已经存在。跳过了CREATEDATABASE语句'
END
GO

第一次执行此语句时,若没有 DBCreateTest 数据库,则创建它;第二次执行时,由于该数据库已存在,因此会印出自定义的提示信息。 因此,仅用一点小技巧,可避免掉不必要的 DROP 动作被执行,这会让您公司的产品在被安装时更有效率。

EXISTS 是很方便的关键词,有时可让查询语句执行得更有效率,有时则可简化 SQL 语句。

The INTERSECT and EXCEPT Operators


接下来介绍的 INTERSECT 和 EXCEPT 关键词,在 SQL Server 和其他厂牌数据库多半都支持。 INTERSECT 和 EXCEPT 在处理两个 result set 时,和 UNION 关键词很类似。 在 MSDN、TechNet 上虽然有对这两个关键词作解释 [3], [6],但半机器的翻译有些不易理解,建议参考下列取自书上的图文示例 [7],可让人一目了然:

  • UNION: 将多个「结果集 (result set)」的「行 (row)」合并,作为单个结果集返回,并移除重复的行。若有重复的行,只留下一个。
  • UNION ALL: 将多个「结果集 (result set)」所有的行合并,不论是否有重复的行。
  • EXCEPT: 提取只在 EXCEPT 左侧存在,但右侧不存在的行,参考下图 1。用更口语化的说法:「只给我 A 里才有,但 B 里没有的行」。
  • INTERSECT: 只提取两个结果集里,都存在的行。 INTERSECT 很类似 inner join,但 INTERSECT 并不会对特定的「列 (column)」去做处理。

由于上述关键词,不会对特定的「列」去做处理,因此在使用上必须符合某些原则 [3],例如:所有查询中的列数和列的顺序必须相同、数据类型必须兼容。


图 1 UNION 中若有重复的行,会被移除,只留下一个

在看本文最后的完整比较示例前,我们先了解 EXCEPT 和 INTERSECT 的基本语法。

EXCEPT

T探讨的深层逻辑是什么?


EXCEPT 用法如下,简单易懂:

<table or tabular result>
EXCEPT
<table or tabular result with same number of columns and type as top query>

同样的查询需求,我们改用 NOT EXISTS 的写法,也能得到和 EXCEPT 写法相同的结果,用法如下 [1], [7]。 本文最后会有完整的示例。

<basequery>
WHERENOTEXISTS
(SELECT1
FROM<tableorresultwithsamenumberofcolumnsandtypeastopquery>
WHERE<basequeryfirstcolumn>=<comparisontablefirstcolumn>[,...])

INTERSECT

至于 INTERSECT 的语法也一样简单易懂,用法和 EXCEPT、UNION 也都类似,如下:

<table or tabular result>
INTERSECT
<table or tabular result with same number of columns and type as top query>


同样的查询需求,我们改用EXISTS 的写法,也能得到和 INTERSECT 写法相同的结果,用法如下 [1], [7]。 本文最后会有完整的示例。


<basequery>
WHEREEXISTS
(SELECT1
FROM<tableorresultwithsamenumberofcolumnsandtypeastopquery>
WHERE<basequeryfirstcolumn>=<comparisontablefirstcolumn>[,...])

此外,INTERSECT 和 EXCEPT 可同时混搭使用,但 INTERSECT 运算符会优先于 EXCEPT[6]:

SELECT*FROMTableA
EXCEPT
SELECT*FROMTableB
INTERSECT
SELECT*FROMTableC



Comparing EXCEPT and INTERSECT with Their EXISTS and NOT EXISTS Equivalents


以下的示例,我们将演示本帖前述的所有内容,先看 EXCEPT、INTERSECT 的示例,再看与他们俩对等的 NOT EXISTS、EXISTS 写法示例 (不同写法,但会返回相同的结果集)。


完整示例 1--Createourtesttablesandpopulatethemwithafewrelevantrows
2CREATETABLEUnionTest1
3(
4idcolintIDENTITY,
5col2char(3),
6);
7
8CREATETABLEUnionTest2
9(
10idcolintIDENTITY,
11col4char(3),
12);
13
14INSERTINTOUnionTest1VALUES('AAA'),('BBB'),('CCC');
15INSERTINTOUnionTest2VALUES('CCC'),('DDD'),('EEE');
16
17
18PRINT'测试1,Sourceandcontentofbothtables:';
19
20SELECT1ASSourceTable,col2ASValueFROMUnionTest1
21UNIONALL
22SELECT2,col4FROMUnionTest2;
23
24
25PRINT'测试2,ResultswithclassicUNION';
26
27SELECTcol2FROMUnionTest1
28UNION
29SELECTcol4FROMUnionTest2;
30
31
32PRINT'测试3-1,ResultswithEXCEPT';
33
34SELECTcol2FROMUnionTest1
35EXCEPT
36SELECTcol4FROMUnionTest2;
37
38
39PRINT'测试3-2,EquivilentofEXCEPTbutusingNOTEXISTS';
40
41SELECTcol2FROMUnionTest1ut1
42WHERENOTEXISTS
43(SELECTcol4FROMUnionTest2WHEREcol4=ut1.col2);
44
45
46PRINT'测试4-1,ResultswithINTERSECT';
47
48SELECTcol2FROMUnionTest1
49INTERSECT
50SELECTcol4FROMUnionTest2;
51
52
53PRINT'测试4-2,EquivilentofINTERSECTbutusingEXISTS';
54
55SELECTcol2FROMUnionTest1ut1
56WHEREEXISTS
57(SELECTcol4FROMUnionTest2WHEREcol4=ut1.col2);
58
59
60PRINT'测试5,INTERSECT运算符优先于EXCEPT';
61SELECTcol2FROMUnionTest1
62EXCEPT
63SELECTcol2FROMUnionTest1
64INTERSECT
65SELECTcol4FROMUnionTest2;
66
67
68--Cleanupafterourselves
69DROPTABLEUnionTest1;
70DROPTABLEUnionTest2;

执行结果:

测试 1 结果      
测试2 结果   
测试3-1、3-2 结果 
测试4-1、4-2 结果 
测试5 结果

如您所见,测试 3-1、3-2 的结果相同,测试 4-1、4-2 的结果也相同,但 SQL Server 的「评估的执行计划」很不同,且 EXCEPT / INTERSECT 的「查询开销」会比 NOT EXISTS / EXISTS 大很多,如同上例中,测试 3-1 比 3-2 的性能差,测试 4-1 比 4-2 性能差。而且经我测试 (书籍作者[7]),大多数的情形,用 EXISTS 的写法,性能都比 EXCEPT / INTERSECT 要好。

虽然如此,但我们不该完全舍弃 EXCEPT / INTERSECT 的使用,因为他们的语法简洁、容易阅读。因此在您的 SQL 语句中,除非两种写法的性能差距很大,不然我仍建议使用 EXCEPT / INTERSECT 写法,以便项目后续的维护。若论 EXISTS 和 EXCEPT / INTERSECT 哪一种写法较好,则属见仁见智的问题,端视您的数据量、执行环境、实测两种写法的效率差距,来决定要用哪一种写法。




参考文档:

[1] EXISTS (Transact-SQL)
msdn.microsoft.com/zh-cn/library/ms188336.aspx

[2] SQL 中 IN 和 EXISTS 用法的区别?
topic.csdn.net/u/20090715/10/ec21e6cc-7265-4c44-a35c-8a0003e73978.html

[3] EXCEPT 和 INTERSECT (Transact-SQL)
msdn.microsoft.com/zh-cn/library/ms188055.aspx

[4] UNION (Transact-SQL)
msdn.microsoft.com/zh-cn/library/ms180026.aspx

[5] 与其他 Transact-SQL 语句一起使用 UNION、EXCEPT 和 INTERSECT
msdn.microsoft.com/zh-cn/library/ms191523.aspx

[6] 使用 EXCEPT 和 INTERSECT 执行半联接操作
msdn.microsoft.com/zh-cn/library/ms191255.aspx



参考书籍:

[7] Professional Microsoft SQL Server 2008 Programming, Chapter 3
www.wrox.com/WileyCDA/WroxTitle/Professional-Microsoft-SQL-Server-2008-Programming.productCd-0470257024.html
www.ppurl.com/pdfpreview/?skey=AQQHNgokAXRXMVA7UAxQP1Z0XTkJYwBtB2ZUZQI6UzQ%3D&page=0



相关文章:

[8] 探讨 .NET 4 新增的 SortedSet 类 (.NET 平台的 EXCEPT、INTERSECT)
www.cnblogs.com/WizardWu/archive/2010/06/17/1759297.html