锋利的SQL(第2版)(txt+pdf+epub+mobi电子书下载)


发布时间:2020-07-16 00:20:58

点击下载

作者:张洪举 王晓文

出版社:人民邮电出版社

格式: AZW3, DOCX, EPUB, MOBI, PDF, TXT

锋利的SQL(第2版)

锋利的SQL(第2版)试读:

前言

本书第1版是在4年前出版的,至今仍在被众多的SQL爱好者追捧,甚至有人不辞辛劳地逐页扫描,上传至网络进行分享。本书第 1 版是基于SQL Server 2008 编写的,时光荏苒,目前SQL Server 2014 已经发布,窗口函数等新功能不断丰富和发展,先前需要编写很复杂的代码才能完成的工作,现在寥寥几行就能把问题解决。无论是从代码可读性还是执行效率方面看,SQL Server 2014 都值得我们去深入学习。因此,我们在第 1 版的基础上,对书的内容重新进行了补充和完善。此外,这次我们还补充了一些像“三值逻辑”这样的基础知识,因为在实际工作中,我经常发现,由于存在NULL 值比较问题,导致查询结果不正确,费尽千辛万苦地分析代码,最终却发现犯了低级错误。

当初之所以要写这本书,主要出于两方面的原因:一是伴随着各种数据库技术日新月异的发展,无论是哪种数据库产品,想用有限的篇幅去描述它的全貌,几乎都是不可能完成的任务,所以我就考虑能否抽取出各种数据库产品中一些大家共同关心的内容,进行深入细致的挖掘,而SQL无疑是这方面的首选;二是在与一些开发公司的合作中,发现公司间的SQL应用情况的差异也很大,一些公司出于产品的可移植性考虑,拒绝使用一些新的SQL技术,甚至尽量避免在服务器上部署存储过程。所以我希望在深入地讨论一些常用SQL技术的同时,也尽可能多介绍一些SQL的新技术,从而消除大家对新技术的恐惧感,对新技术的推广能够起到一定的作用。

本书特点

本书既覆盖了改善效率和性能的普通SQL技术,也深入探讨了SQL新技术,更包含一些实用的查询解决方案。希望本书能够成为引领读者进入SQL查询殿堂的捷径。

本书的内容是基于 SQL Server 数据库产品进行讨论的。不过,由于大家都在遵循 ANSI-SQL标准,所以无论是哪种数据库产品的 SQL,彼此之间的差别并不大。数据库开发人员在跨越不同的数据库产品时,一般不会遇到什么障碍。当然,本书不特定于某个 SQL Server 版本,在介绍一些新功能时,会介绍它是在哪个版本添加进来的。并且,本书在改写过程中,在添加新的查询解决方案的同时,保留了先前的解决方案,以便读者能够进行对比分析。

本书在介绍各种查询语法时,更注重对查询逻辑思维方式的引导和介绍,这样可以帮助读者在阅读之后举一反三,提升自己动手解决实际问题的能力。

本书适用读者

本书是按照由浅入深、循序渐进的方式对SQL进行介绍的,既包含了入门知识,也包含了深层次技术的讨论。即使是最基本的查询语句,我们也会尽力为读者提供解决深层次问题的能力。也就是说,同样一个问题,开发人员可以写几十甚至上百行的SQL语句来解决问题,也有可能仅通过一条SELECT语句就可以解决问题。对于SQL而言,虽然代码最简化并不一定是性能最优化,但至少是对思维能力的一种提升。

从这个角度而言,本书可以作为SQL入门书,也可以作为SQL程序员、DBA的参考书。

本书内容与结构

本书共19章,可大致分为基础篇、开发篇、性能调整篇和实战篇4部分。

基础篇包括第1章至第10章。其中,第1章是对查询工具、书写规范等基本内容的介绍,第2章至第5章是对数据库、表和索引的介绍,第5章至第10章则介绍了使用SELECT进行数据查询和使用INSERT、UPDATE、DELETE进行数据修改的各个方面。本次修订新增了SQL理论基础,新增函数介绍、架构概念、标识列和序列对象等一致性约束介绍,内存表、选择列表的同时操作概念,查询的逻辑处理步骤的修订,使用OFFSET-FETCH限制查询结果和限制查询修改,窗口分布函数和窗口偏移函数、MERGE与OUTPUT等,尤其是第9章的窗口计算部分,更是全部重新书写。

开发篇包括第11章至第15章。如果将SELECT、INSERT、UPDATE和DELETE作为基本查询语句,则IF...ELSE、WHILE和TRY...CATCH构造等则可以看作是SQL编程语句。在存储过程、触发器等对象中可以通过这些语句实现一些复杂的逻辑处理。如果你曾经是一位使用VB或VC的程序员,在学习C/S或B/S编程时,你应当掌握这种服务器端的编程工具,从而将业务逻辑计算合理地分布到服务器和客户端。本次修订对CLR 存储过程、触发器、函数等以Visual Studio 2013 为基础重新进行了编写。

性能调整篇包括第16章至第18章。第16章和第17章介绍的是事务处理机制和并发访问控制。其实,无论是微软还是 Oracle、IBM,其数据库产品的核心功能都是一样的,即在保证数据完整性的前提下提供最大的并发支持。数据库系统是通过“锁机制”来实现的,数据库引擎都提供有多种粒度的锁定模式,从而允许用户可以根据需要将资源锁定在适当的级别,尽量减少锁定开销。第18章则讨论了查询优化器的工作原理,重用查询计划,可以减少额外的编译开销,提高服务器性能。

实战篇仅包含第19章的内容,提供了同一时间范围内并发数统计、时间段天数统计、数字范围统计、地域范围内最大数统计等较为常见问题的解决方案。本次修订在原解决方案的基础上新增了窗口函数的解决方案。

系统要求

本书实例中使用的操作系统是Windows 7,使用的数据库是SQL Server 2014 开发者版本,开发工具是Visual Studio 2013。为使Visual Studio 2013 能够支持SQL Server 2014 的SQL开发,需要将其升级到最新版本。同时,由于SQL Server 示例数据库在2014版本中表结构变动较大,因此对书中的代码重新进行了调整和测试,该示例数据库可从 http://msftdbprodsamples.codeplex.com/releases/view/125550下载。

代码示例

本书中使用的所有代码,请从作者的博客(http://blog.csdn.net/zhanghongju)上获取。

致谢

在本书的完成过程中,得到了诸多 SQL Server 技术专家和爱好者的支持与帮助。他们无私和热情的参与,使本书的内容更加实用和更具指导性,在此一并表示感谢。他们是王向东、秦广、魏兰花、凌亚东、王亚羽、陈雨薇、王光辉、高存亭、桑晓红、王新河、张宪国、李联国、韩燕军。

由于时间仓促,加之作者水平有限,书中不足之处在所难免,敬请读者批评指正。张洪举2015年1月

SQL Server 2014 示例数据库安装步骤

SQL Server 2014 示例数据库安装步骤具体如下。(1)登录http://msftdbprodsamples.codeplex.com/releases/view/125550网站,单击Adventure Works 2014 Full Database Backup.zip 下载数据库文件。(2)下载完成后解压缩,释放后的文件名为AdventureWorks2014.bak。(3)打开Microsoft SQL Server Management Studio,在“对象资源管理器”中右键单击“数据库”节点,在弹出的快捷菜单中选择“还原数据库”菜单项,如图1所示。(4)在打开的“还原数据库”窗口中,单击“设备”后面的“…”按钮,在打开的对话框中单击“添加”按钮,选择释放后的数据库备份文件AdventureWorks2014.bak,如图2所示。依次单击“确定”按钮即可还原数据库。(5)在“对象资源管理器”中右键单击“数据库”节点,在弹出的快捷菜单中选择“刷新”菜单项,即可看到数据库AdventureWorks2014。图1 选择“还原数据库”菜单项图2 选择备份介质

基础篇

第1章 SQL简介

第2章 数据库与架构

第3章 表管理

第4章 索引管理

第5章 基本查询

第6章 子查询

第7章 联接和APPLY运算符

第8章 操作结果集

第9章 窗口计算和表旋转

第10章 数据修改

第1章 SQL 简介

SQL 的全称是结构化查询语言(Structured Query Language),是一种在关系数据库管理系统(Relational Database Management Systems,RDBMS)中查询和管理数据的标准语言。这是一种非常易读和易用的语言,只要稍微有一点英语基础,一些简单的数据查询、操作语句几乎都可以理解。但是,要想精通 SQL,并不是一件很容易的事情,一是因为在数据处理方面有许许多多的个案,要处理好这些个案,除了具有缜密的逻辑思维,还需要多练习和实践;二是与其他编程语言相比, SQL 是一种描述性语言,你只要说出想要什么结果,中间过程由数据库引擎去处理,其他编程语言则需要通过循环语句、判断语句等自己进行处理,如果出现什么问题,可以很快找到导致错误的语句,而对于SQL你就需要对寥寥几行的查询语句深入思考——数据库引擎为什么没有理解我的要求?所以说,数据库引擎的这种“封箱”操作给查找问题带来了麻烦,通常的解决方法就是将一个复杂语句拆解为几个简单语句,逐步验证中间结果的正确性,然后再重新组合为一条语句。从实际工作经验看,我不建议你写很复杂的语句来证明自己的高深,简单明了的语句对于后期的系统维护很有帮助,除非你的语句确实影响了处理效率。从本章开始,打好坚实的基础,并在此基础上不断提升自己的理论知识体系,当感受某个成功喜悦的时候,或许就会发现自己已经站在了SQL的巅峰。

本章我们将讲述一些最基本的SQL内容,如SQL的历史起源、ANSI是什么和一些基本的数据库理论,以及SQL的语法元素和执行SQL的工具等。基本知识学习起来可能比较枯燥。但是,如果你是初学 SQL,这些基本知识对于学习好本书的后续内容却起着至关重要的作用。我也曾见过许多资深的数据库管理员会犯一些低级错误,一上午在不停地分析语句,最后却发现仅仅是“三值”逻辑问题。

1.1 SQL的历史起源

在20世纪60年代,网状数据库系统(如 CODASYL)和分层数据库系统(如IMS TM)是用于自动化银行业务、记账和订单处理系统的一流技术,这些系统是由于商业大型计算机的引入才启用的。而SQL是在20世纪70年代创建的一种基于关系数据库管理系统模型的数据查询、操作语言。1.1.1 CODASYL

CODASYL是美国数据系统语言协会(Conference on Data System Language)的英文缩写,该协会成立于1957年,主要目的是开发一种用于创建商业应用的通用语言。1959年5月28日,该协会召开了首次会议,就语言开发进行讨论。这个语言实际上就是Cobol语言。

1963年6月10日,美国加利福尼亚州的系统开发公司(System Development Corporation)举办了一个题为“基于计算机的数据库开发和管理”(Development and Management of a Computer-centered Data Base)的研讨会,首次提出并定义了数据库(Database)术语,即一组文件(表)的集合,其中文件是数据项(行)的有序集合,而每个数据项由数据以及一个或多个键组成。

1965年,CODASYL 成立了“列表处理任务组”(List Processing Task Force),后更名为“数据库任务组”(Data Base Task Group)。1971年4月,任务组发布了一份重要的报告,报告概述了网状数据模型,被称为CODASYL 或DBTG(即Data Base Task Group的缩写)数据模型。这个模型定义了数据库的几个关键概念,包括定义模式的语法、定义子模式的语法和数据操作语言。1.1.2 IMS

IMS 是信息管理系统(Information Management System)的英文缩写,是IBM公司的产品,这是一款分层数据库管理和事务处理系统。

IMS最初的开发目的是支持美国的阿波罗太空计划。1966年,IBM公司的12名成员、美国洛克维尔(American Rockwell)公司的10名成员和卡特彼勒公司(Caterpillar Tractor)的3名成员被集合起来,开始开发信息控制系统(Information Control System,ICS)和数据语言/接口(Data Language/Interface,DL/I),用于辅助跟踪建造太空船所需要的材料。其中,ICS部分是用于存储和获取数据的数据库部分,而DL/I部分则是用来与之交互的查询语言。

在开发过程中,IBM小组转移到加利福尼亚州南部的洛杉矶,并增加至21名开发人员。1967年,IBM团队完成了ICS的第一个版本。1968年4月开始进行安装测试。1968年8月14日,第一个安装成功,信息显示在美国国家航空航天局洛克维尔部门的IBM 2740打印机终端上。

1969年,ICS被更名为信息管理系统,即IMS。自第一个版本问世以来,IBM一直在不断开发和完善IMS 的功能。2007年,IBM推出了IMS 10 版本。IMS 10 具备了增强的XML 和网络服务功能,并且也是第一个将标准XML查询语言应用于层次结构化数据的数据库系统。1.1.3 RDBMS和SQL

无论是CODASYL,还是IMS,虽然这些系统为早期系统提供了良好的基础,但它们的基本体系结构是将数据的物理操作与逻辑操作混合在一起。当数据的物理位置改变时,也必须更新应用程序来引用新的位置,给数据查询带来了不便。

SQL是一种关系型数据库查询语言,要介绍SQL的起源,就不得不介绍IBM公司的两个重量级人物——E.F.Codd 博士和Don Chamberlin 博士。E.F.Codd 博士最早提出了关系数据库管理系统(Relational Database Management System,RDBMS)模型,而Don Chamberlin 博士则是SQL和XQuery语言的主要创造者之一。他们对数据库的变革起到了革命性的作用。

Don Chamberlin 最初在IBM的T. J. Watson 研究中心(http://www.watson.ibm.com/)工作,当时该中心的主要研究方向是操作系统。Chamberlin 一开始从事的项目是SystemA,但项目很快便以失败而告终。当时担任项目经理的 Leonard Liu(现为 Augmentum 公司 CEO,http://www. augmentum.com)很有远见地预见到数据库的美好前景,他转变了整个小组的研究方向。Chamberlin因此在数据库软件和查询语言方面进行了大量研究,并成为了小组中最好的网状数据库CODASYL专家。

此时的 E.F.Codd 博士在IBM 的 San Jose 研究中心(即现在的 Almaden 研究中心, http://www.almaden.ibm.com/)工作。1969年,Codd在IBM的研究报告“Derivability, Redundancy, and Consistency of Relations Stored in Large Data Banks”(大型数据库中关系模型存储的可导、冗余和一致性)中第一次提出了关系模型,修订版本是Codd于1970年在一篇名为“A Relational Model of Data for Large Shared Data Banks”(大型共享数据库的数据关系模型)的文章中提出的,发表在《美国计算机学会通讯》杂志上。这是一篇里程碑性的论文,它确立了关系数据库的概念。但是,由于IBM正在从事IMS的开发,这种思想对IBM本身产品造成了威胁,所以公司内部最初持压制态度。当然这也与Codd采用了大量的数学方法、不容易理解有关。

1973年,IBM在外部竞争的压力下,开始加强在关系数据库方面的投入。Chamberlin被调到San Jose研究中心,加入新成立的项目System R。System R基于Codd提出的关系数据库管理系统模型。

System R项目包括研究高层的关系数据系统(Relational Data System,RDS)和研究底层的存储系统(Research Storage System,RSS)两个小组,Chamberlin 担任RDS 组的经理。RDS实际上就是一个数据库语言编译器,由于Codd提出的关系代数和关系演算过于数学化,影响了易用性。于是Chamberlin选择了自然语言作为研究方向,其结果就是诞生了结构化英语查询语言(Structured English Query Language,SEQUEL)。后来,由于商标之争,SEQUEL 更名为SQL。

System R是一个具有开创性意义的项目。它第一次实现了结构化查询语言,并已成为标准的关系数据查询语言。同时,它也是第一个证明了关系数据库管理系统可以提供良好事务处理性能的系统。System R系统中的设计决策,以及一些基本算法选择(如查询优化中的动态编程算法)对以后的关系系统都产生了积极影响。

System R本身作为原型虽然并未问世,但鉴于其影响,计算机协会(Association for Computing Machinery,ACM,http://www.acm.org/)还是把1988年的“软件系统奖”授予了System R开发小组。1.1.4 ANSI和SQL方言

ANSI是美国国家标准学会(American National Standards Institute)的英文缩写,该学会成立于1918年。当时,美国的许多企业和专业技术团体,已开始了标准化工作,但因彼此间没有协调,存在不少矛盾和问题。为了进一步提高效率,数百个科技学会、协会组织和团体,均认为有必要成立一个专门的标准化机构,并制定统一的通用标准。1918年,美国材料试验协会(ASTM)、美国机械工程师协会(ASME)、美国矿业与冶金工程师协会(ASMME)、美国土木工程师协会(ASCE)和美国电气工程师学会(AIEE)等组织,共同成立了美国工程标准委员会(AESC)。美国政府的商务部、陆军部和海军部也参与了该委员会的筹备工作。1928年,美国工程标准委员会改组为美国标准协会(ASA)。为致力于国际标准化事业和消费品方面的标准化,1966年8月,又改组为美利坚合众国标准学会(USASI)。1969年10月6日改成现名:美国国家标准学会(ANSI)。

虽然IBM首创了关系数据库理论,但Oracle却是第一家在市场上推出了这套技术的公司。随着时间的推移,SQL的简洁、直观,在市场上获得了不错的反响,从而引起了ANSI的关注,分别在1986年、1989年、1992年、1999年及2003年发布了SQL 标准。SQL Server 2000 遵循ANSI SQL:1992 标准,而SQL Server 2005 和2008还实现了ANSI SQL:1999 和ANSI SQL:2003中的一些重要特性。

数据库生产商在遵循ANSI标准的同时,也会根据自己产品的特点对SQL进行了一些改进和增强,于是也就有了SQL Server 的Transact-SQL、Oracle 的PL/SQL 等语言,我们称之为SQL 方言。在本书中,我们将以Transact-SQL为基础进行SQL语言的介绍。实际上,在学习过程中,大家也没有必要刻意关心哪些语句或关键字是SQL标准,哪些是Transact-SQL的扩展。其实常见的数据库操作,在绝大多数支持SQL语言的数据库中差别并不大,所以数据库开发人员在跨越不同的数据库产品时,一般不会遇到什么障碍。但是对于数据库管理员来说,则需要面对很多挑战,不同数据库产品在管理、维护和性能调整方面区别很大。

1.2 SQL的理论基础

E.F.Codd博士提出的关系数据库管理系统模型基于两个数学分支:集合理论和谓词逻辑。理解集合理论,对于构建数据库结构,减少数据冗余,保证数据一致性方面具有重要意义。所以,即使你是一个资深的DBA,也建议你重温本节内容。1.2.1 集合理论

集合理论(集合论)是由数学家 Georg Cantor 创建的,这是一个基于关系模型的数学分支。Cantor的集合定义如下:

任意集合体M是我们感知或想到的,能够确定的互异对象m(称之为M的元素)的整体。——Joseph W. Dauben 和Georg Cantor(普林斯顿大学出版社,1990)

看似简短的一句话,但是定义中的每个字都有着深刻和重要的意义。一个“集合”应将其视为单个实体,你的焦点应该放在对象的集合上,而不是组成集合的单个对象上。例如,当你对数据库中的雇员表编写查询时,应该将雇员的集合看作一个整体,而不是单个的雇员。这听起来可能并不重要并且很简单,但是很多程序员没有这种思维方式。“互异”的含义是指集合中的每个元素必须是唯一的。对于数据库中的表,我们可以通过定义键约束来强制表中行的唯一性。没有键的话,就不能唯一地标识行,这样的表也就不能称之为“集合”,只能将其看作一个多重集合或一个无序的单位组。“对象”可以是汽车或雇员这样的物理对象,也可以是抽象对象,如质数或线条。“我们感知或想到的”这句话意味着集合的定义是主观的。例如,在教室中,“人”可以被认定为是“学生”的集合,也可以是“教师”的集合。因此,在定义集合方面你具有很大的自由度。在为数据库设计数据模型时,设计过程应仔细考虑应用程序的主观需求,从而为相关实体确定恰当的定义。

需要注意的是,Cantor的集合定义省略掉的内容同样重要。定义中没有提到集合元素间的任何顺序,集合元素的列出顺序并不重要。因为与顺序无关,{b, a, c}与{b, c, a}表示的是同一集合。对于属性(SQL中称之为列)集合,它们组成了关系(SQL中称之为表)的表头,元素应该是按名称标识,而不是按顺序位置标识。

同样,对于元组(SQL中称之为行)而言,它们构成了关系的主体,元素由其键值进行标识,而不是按位置标识。许多程序员很难适应这种观念,对于查询表而言,行之间没有顺序。换句话说,对表的查询可以按任意顺序返回表中的行,除非你基于特定展现目的,明确要求数据以特定的方式进行排序。1.2.2 谓词逻辑

谓词逻辑的渊源可追溯到古希腊,是基于关系模型的另一个数学分支。谓词是一个属性或是一个表示“持有”或“不持有”的表达式,换句话说,也就是“真”或“假”。关系模型就是依靠谓词来维护数据的逻辑完整性并定义其结构的。例如,在一个名为Employees的表中定义了约束,仅允许工资(Salary)大于0的雇员存储在表中。谓词是“工资大于0”(T-SQL 表达式:salary > 0)。

当通过筛选数据来界定子集等操作时,也可以使用谓词。例如,如果需要查询Employees表并要求只返回销售部门的雇员行时,可以在查询筛选器中使用谓词“部门(Department)等于销售(Sales)”(SQL 表达式:department = 'sales')。

在集合理论中,可以使用谓词定义集合,这是非常有用的,因为不能总是通过列出所有元素来定义集合(例如,无限集合),通过特征定义集合往往更加方便简洁。例如,这是使用谓词定义无限集合的例子:所有质数集合的谓词定义是——x是一个仅能被 1 和自身整除的大于 1的正整数。对于任意指定值,谓词可能为“真”,也可能为“假”,而全部质数的集合是谓词为“真”的所有元素的集合。例如,这是一个使用谓词定义有限集合的例子,集合{0,1,2,3,4,5,6,7, 8,9}可以被定义为下面谓词为“真”的所有元素的集合——x是一个大于或等于0且小于或等于9的整数。1.2.3 关系模型

前面提过,关系模型是基于集合理论和谓词逻辑进行数据管理和操作的语义模型,它涉及命题、谓词、关系、元组和属性等概念。关系模型的目标是确保数据的一致性表示,最小化或是没有冗余且不牺牲完整性,并将定义数据的完整性(强制数据一致性)作为模型的一部分。RDBMS应实施关系模型并提供方法来存储、管理、实施完整性和查询数据。

1.命题、谓词和关系

很多人认为“关系”是指表之间的关系,这是不正确的。在数学的集合理论中,关系是集合的表现形式。在关系模型中,关系是相关信息的集合,与SQL中相对应的就是一个表——尽管不能完全对应。关系模型中的一个关键点是,单个的关系应代表一个单个集合(如客户)。需要注意的是,对多个关系操作(例如,两个关系之间的联接)的结果也是一个关系。

关系是由表头和主体组成的。表头包含一组属性(SQL中称之为列),其中每个元素由一个属性名称和类型名称标识。主体包含一个元组(SQL中称之为行)集合,其中每个元素都由键标识。为了简单起见,将表称之为行集。

当为数据库设计数据模型时,要使用关系(表)来表示所有数据。首先要确定需要在数据库中表示的命题。命题应当是一个断定或必须为“真”或“假”的语句。例如,语句“雇员 A 出生于1971年2月12日,在IT部门工作”是一个命题,如果这是一个真命题,它将表现为Employees (雇员)表中的一个行,如果是假命题,则根本不会出现。这个假设被称为“封闭世界假设”(close world assumption,CWA)。

下一步是将命题形式化,即取出实际数据(关系的主体)并定义结构(关系的表头)。可以以参数化命题方式思考谓词,关系的表头包含一个属性集合。属性由属性名称和类型名称进行标识,例如,一个Employees(雇员)关系的表头可能包含下列属性(属性名称和类型名称以成对方式出现):employeeid 整型、firstname 字符串型、lastname 字符串型、birthdate 日期型、departmentid整型。

类型是关系最基本的构造块,它约束了属性是一个可能或有效值的确定集合。例如,INT类型是范围自–2 147 483 648至2 147 483 647所有整数的集合。类型是数据库中谓词的最简单形式之一,因为它限制了该属性的允许值。例如,数据库不会接受一个雇员的出生日期为1971年2月31日的命题(更不用提像“abc”这样的生日)。注意,类型不受像整型或字符串型等基本类型的限制,它也可以是可能值的枚举,例如枚举可能的工作岗位。

2.缺失值

关系模型的一个方面是对于“是否谓词应限制于二值逻辑”的激情辩论。也就是说,在二值谓词逻辑中谓词是“真”或“假”。如果谓词不是“真”,那肯定是“假”。不过,也有人说具有三值(甚至四值)谓词逻辑空间,例如,像账号缺失这种情况。对于谓词,涉及缺失值就会出现既不是“真”也不是“假”的问题——因为它是未知的。例如,一个Employees(雇员)关系的手机属性,假设某些雇员的手机号码丢失了,怎么把这一事实表示在数据库中?这要使用三值逻辑实现,手机属性应当允许有一个代表缺失值的特定标记。然后,在这种缺失值情况下,将该手机属性与一些指定数字比较,谓词也将产生未知。三值谓词逻辑是指来自谓词结果的3 种可能逻辑值——“真”“假”和“未知”(true、false和unknown)。

SQL通过支持NULL标记实现了三值谓词逻辑,以表示缺失值的通用概念。SQL中对NULL和三值谓词逻辑的支持是造成极大混乱和复杂性的根源,一些资深的DBA在对故障查询语句进行分析时,费尽周折却发现是数据中存在NULL值导致的。当然,最好的方法是使用二值谓词逻辑,这是不会造成问题的,但是在实际工作中,你会发现这很难。尤其是对一些现有数据系统改造时,数据质量很差,你只能是尽可能地规范这些数据,而无法将其完全规范到二值逻辑中。

3.约束

关系模型的最大好处是能够把定义数据的完整性作为模型的一部分。数据完整性通过在数据模型中定义的约束规则实现,并由 RDBMS 实施。实施完整性的最简单方式是指定属性类型的nullability(是否支持或不支持 NULL 标记)标志。约束也可以通过模型自身实施,例如,限定关系Orders(orderid, orderdate, duedate, shipdate)中每笔订单有3 个互异日期,关系Employees(empid) 与EmployeeChildren(empid, childname)允许每个雇员有0 或多个子集。

约束的其他例子还包括提供实体完整性的候选键和提供引用完整性的外键。候选键是定义了一个或多个属性的键,防止关系中出现多个相同的元组(SQL中的行),基于候选键的谓词可以唯一地标识行(如雇员)。你可以在关系中定义多个候选键,例如,在Employees 关系中,可以在employeeid、SSN(社会安全号码)等属性上定义候选键。通常,可以任意选择一个候选键作为主键(例如,Employees 关系中的 employeeid),并作为标识行的首选方式。其他的所有候选键称为备用键。

外键用于强制引用完整性。外键定义了关系的一个或多个属性(称为引用关系)引用另一个关系(或同一关系)的候选键,此约束限定了引用关系的外键属性中的值,应是出现在被引用关系(父表)的候选键属性中的值。例如,假设Employees关系具有一个定义在departmentid属性上的外键,它引用Departments关系中的主键属性departmentid,这意味着Employees.departmentid中的值会出现在Departments.departmentid中的值。

4.规范化

关系模型还定义了规范化规则(也称为范式),用于确保每个实体都由单一关系表示。在规范化的数据库中,要在数据修改过程中避免异常,并在不牺牲完整性的情况下保持最低限度冗余。下面简要介绍一下由Codd提出的前3个范式(1NF、2NF和3NF)。(1)1NF

第一范式是说关系(表)中的元组(行)必须是唯一的,并且属性是原子化的。也就是说,如果表正确地表示了关系,它已经符合了第一范式。

通过为表定义一个唯一键就可以实现唯一行。

你只能执行属性类型所定义的操作,我们知道,属性的原子性是主观的,这与集合的定义是主观的一样。例如,Employees关系中雇员姓名应当使用1个(fullname)、2个(firstname和lastname)还是3个(firstname、middlename和lastname)属性表示呢?答案取决于应用程序。如果应用程序需要分别处理雇员的姓名部分(如处于搜索目的),则有必要把它们分开,否则,则不需要。

同样,基于应用程序的需求,属性可能不被完全原子化,也有可能被亚原子化。例如,如果地址属性被作为一个特定应用程序的原子,不将“城市”作为地址的一部分会违反第一范式。(2)2NF

第二范式涉及两个规则。一个规则是数据必须满足第一范式,另一个规则提及非键属性和候选键属性之间的关系。对于每个候选键,每个非键属性必须是对整个候选键的完全函数依赖。也就是说,如果要获取任何非键属性值,需要提供相同元组中候选键的所有属性的值;如果知道候选键的所有属性的值,就可以检索到任何元组的任何属性的任何值。

下面是一个违反第二范式的例子,假设定义了一个名为 Orders 的关系,表示订单和订单行的信息,如图1-1所示。Orders 关系包含下列属性:orderid、productid、orderdate、qty、customerid和companyname。主键定义为orderid和productid。图1-1 符合2NF之前的数据模型

图1-1中违反了第二范式,因为有非键属性仅依赖于候选键(即该示例的主键)的一部分。例如,你可以仅通过orderid找到订单的 orderdate,以及 customerid 和 companyname。要符合第二范式,需要将原来的关系拆分为两个关系,即Orders和OrderDetails,如图1-2所示。Orders关系将包括orderid、orderdate、customerid和companyname属性,主键定义为orderid。OrderDetails关系将包括orderid、productid和qty,主键定义为orderid和productid。图1-2 3NF之前符合2NF的数据模型(3)3NF

第三范式也有两个规则。数据必须满足第二范式,同时,所有非键属性必须依赖于非传递的候选键。通俗地讲,该规则的意思是所有非键属性必须相互独立。换句话说,一个非键属性不能依赖于另一个非键属性。

先前所述的Orders和OrderDetails关系现在已经符合第二范式。请记住,此时的Orders关系包含 orderid、orderdate、customerid 和 companyname 属性,主键定义为 orderid。customerid 和companyname均依赖于主键——orderid。例如,你需要通过orderid主键来查找代表订单中客户的customerid,同样,你需要通过 orderid 主键查找订单中客户的公司名称(companyname)。然而, customerid和companyname也是互相依靠的。为满足第三范式,需要添加包含customerid(作为主键)和 companyname 的 Customers 关系,如图1-3所示,然后就可以从 Orders 关系中删除companyname属性。图1-3 符合3NF后的数据模型

通俗地讲,2NF和3NF通常以这句话概括:“每个非键属性依赖于键,依赖于整个键,并且除了键别无他物”。

1.3 Transact-SQL语言的类型

在介绍了SQL的起源后,来看一下Transact-SQL包括哪些语言类型。首先,为了遵循ANSI SQL标准,Transact-SQL 提供了数据定义语言(Data Definition Language,DDL)语句和数据操纵语言(Data Manipulation Language,DML)语句;其次,为了增强灵活性,Transact-SQL还提供了用于编程的流控制语句和其他语句。

对于语言类型,读者仅做大致了解就可以。在实际应用中,就像没必要区分哪些是SQL标准,哪些是SQL扩展一样,也没有必要区分DDL和DML,它们是一个协同工作的整体。1.3.1 DDL语句

DDL语句用于创建数据库对象,如表、视图、索引等,表1-1中列出了一些常用的DDL语句。表1-1 DDL 常用语句1.3.2 DML语句

DML语句用来检索和修改数据库的内容,表1-2中列出了一些常用的DML语句。表1-2 DML 常用语句1.3.3 编程和流控制语句

许多人在学习编程时,经常询问编程工具的好坏,哪种语言好学,或是在开发上更具备优势。实际上,编程工具可能还有优劣之分,但是编程语言之间差别并不大。尤其是像SQL这样的数据处理语言,只要具备了顺序语句、判断语句和循环语句,就可以处理所有业务逻辑问题。表1-3中列出了一些常用的编程和流控制语句。表1-3 DDL 常用语句续表

下面将对一些常用的编程语句进行介绍。

1.IF...ELSE语句

当条件为“真”时,将执行IF关键字后面的语句,当条件为“假”时,将执行ELSE后面的语句。例如,下面示例的含义是:如果变量@i > 0,设置@MyVar1 = 100,否则设置@MyVar1 = 10,最后使用PRINT语句输出@MyVar1的值。其中的“--”是注释字符。

DECLARE @i AS int, @MyVar1 AS int; -- 声明变量,详细信息参考1.5节的介绍

SET @MyVar1 = 0; -- 设置变量值

SET @i = 1;

IF @i > 0

SET @MyVar1 = 100;

ELSE

SET @MyVar1 = 10;

PRINT CONVERT(char(12),@MyVar1); --输出@MyVar1变量的值

2.BEGIN...END

BEGIN...END提供了执行一组SQL语句的方法,此语句对编写IF...ELSE和WHILE循环非常有用。现在将上面示例中IF...ELSE语句改写为下列形式:

IF @i > 0

SET @MyVar1 = 100;

ELSE

BEGIN

SET @MyVar1 = 10;

PRINT CONVERT(char(12),@MyVar1); --输出@MyVar1变量的值

END

此时IF...ELSE 语句的含义是:如果@i > 0,设置@MyVar1 = 100,否则设置@MyVar1 = 10,并使用PRINT 语句输出@MyVar1 的值。也就是说,SET @MyVar1 = 10 和PRINT语句此时都属于ELSE部分。

3.WHILE循环

在WHILE关键字后面,可以编写一个控制循环执行的条件。在WHILE语句循环体的每一次执行前,都要测试条件。如果条件为真,则执行循环体;否则,将执行循环体后面的语句。

例如,下面的示例中使用了循环和判断语句。首先声明3个变量,@MyVar1用于累加1~100之间的奇数值,@MyVar2用于累加1~100之间的偶数值。

DECLARE @i AS int, @MyVar1 AS int, @MyVar2 AS int; -- 声明变量

SET @MyVar1 = 0; -- 设置变量值

SET @MyVar2 = 0;

SET @i = 1;

WHILE @i < 100 -- 开始循环

BEGIN --指定包含在循环中的语句

IF @i % 2 <> 0 --判断是否为奇数

SET @MyVar1 = @MyVar1 + @i; -- 累加奇数

ELSE

SET @MyVar2 = @MyVar2 + @i; -- 累加偶数

SET @i = @i + 1; --递增@i

END --结束循环

SELECT @MyVar1, @MyVar2, @i; -- 显示变量值

4.GOTO语句和标签

GOTO语句用于将执行流更改到标签处,虽然Transact-SQL和PL/SQL都提供了该语句,但是作为编程而言,我们不推荐使用此编程技术。要编写一个标签,应当在标识符后面加一个冒号。例如,下面的示例使用GOTO语句代替WHILE循环,与上面的WHILE循环示例功能相同。

DECLARE @i AS int, @MyVar1 AS int, @MyVar2 AS int; -- 声明变量

SET @MyVar1 = 0; -- 设置变量值

SET @MyVar2 = 0;

SET @i = 1;

table_loop: --指定标签

IF @i % 2 <> 0 --判断是否为奇数

SET @MyVar1 = @MyVar1 + @i; -- 累加奇数

ELSE

SET @MyVar2 = @MyVar2 + @i; -- 累加偶数

SET @i = @i + 1; --递增@i

IF (@i < 100) GOTO table_loop --跳转到标签处

SELECT @MyVar1, @MyVar2, @i; -- 显示变量值

5.使用WAITFOR语句

WAITFOR语句用于延迟后面语句的执行,可以指定延迟的时间长度或是具体的时间。参考下面的语句:

WAITFOR DELAY '00:1:15';

PRINT N'到时间了';

-- 另一种形式

WAITFOR TIME '10:02:10';

PRINT N'到时间了';

第一个WAITFOR语句使用DELAY关键字指定在1分15秒后执行后面的PRINT语句,最长延迟时间为24小时。第二个WAITFOR语句使用了TIME关键字,指定在10点2分10秒的时候执行后面的PRINT语句。

此语句通常用于并发测试,实际应用中并不多见。例如,可以指定在同一个时间执行多个相同的SQL语句。1.3.4 SQL语句的批处理

应用程序可以将多个SQL语句作为一个批发送到服务器,然后服务器将该批中的语句编译成一个执行计划。在SQL Server 的Management Studio 工具,可以使用GO作为批发送的分隔符号。例如,在图1-4所示的语句中共包含3个批,选中后单击“执行”按钮一起执行。但是由于包含3个GO,所以会被分批发送,其中第1和第2个批都能正常执行,第3个批中由于最后一行INSERT语句的VALUSE关键字错了(正确的应当为VALUES),这个批在将这3个INSERT语句编译成一个执行计划时,将发生错误。因此,这 3 条 INSERT 语句都不会被执行,而不是仅仅发生错误的INSERT语句。从这个示例可以看出,理解批处理Transact-SQL语句的执行方式很重要。图1-4 分批执行SQL语句

1.4 Transact-SQL语法

Transact-SQL具有一些大多数语句都使用或受之影响的元素,包括标识符、数据类型、函数、表达式、运算符和保留关键字等。1.4.1 标识符

数据库对象的名称即为其标识符,如服务器、数据库和数据库对象(例如表、视图、列、索引、触发器、过程、约束及规则等)都可以有标识符。对象标识符是在定义对象时创建的,创建完成后便可以使用标识符引用该对象。例如,下列语句创建一个名为 TableX 的表,其中包含 KeyCol 和Description 列,则TableX、KeyCol 和 Description 都是标识符。

CREATE TABLE TableX (KeyCol INT PRIMARY KEY, Description nvarchar(80));

无论是 TableX、还是 KeyCol 和 Description,这些中间无空格的字符,都称为常规标识符。常规标识符格式规则取决于数据库兼容级别(可以使用 sp_dbcmptlevel 存储过程设置该级别)。当兼容级别为90(SQL Server 2005)、100(SQL Server 2008)或110(SQL Server 2012)时,常规标示符使用下列规则。

第一个字符必须是下列字符之一。

Unicode标准3.2所定义的字母。Unicode中定义的字母包括拉丁字符a~z和A~Z,以及来自其他语言的字母字符。

下划线(_)、at符号(@)或者数字符号(#)。

在SQL Server中,某些位于标识符开头位置的符号具有特殊意义。以at符号开头的标识符表示局部变量或参数。以一个数字符号开头的标识符表示临时表或过程。以两个数字符号(##)开头的标识符表示全局临时对象。

某些Transact-SQL函数的名称以两个at符号(@@)开头。为了避免与这些函数混淆,不应使用以“@@”开头的名称。

后续字符可以包括以下几类。

如Unicode标准3.2中所定义的字母。

基本拉丁字符或其他国家/地区字符中的十进制数字。

at符号、美元符号($)、数字符号或下划线。

此外,常规标识符不能是 Transact-SQL 保留字,不允许嵌入空格或其他特殊字符,不允许使用Unicode标准之外的增补字符。

如果标示符中必须使用空格(如My Table)或其他不符合常规标示符规则的字符,则必须包含在双引号(")或者方括号([ ])内,否则SQL Server 无法正确识别它们。双引号和方括号被称为分隔标示符。例如,下面语句中的My Table 和order 必须包含在分隔标识符内,因为My Table中间有空格,order 是SQL Server 用于ORDER BY 子句的保留字。

SELECT * FROM [My Table] WHERE [order] = 10;

常规标识符和分隔标识符包含的字符数必须在1~128。对于本地临时表,标识符最多可以有116个字符。

在使用双引号作为分隔符时,SQL Server遵从的规则受SET QUOTED_IDENTIFIER设置影响。设置为ON(默认值)时,双引号只能分隔标识符,文字必须由单引号分隔;设置为OFF时,标识符不能加引号,且必须符合所有常规标识符规则。

如果字符串中已经包含有单引号,则应在该单引号前再添加一个单引号。例如,下面的语句用于从My Table 表中查找 Last Name 为O’Brien 的行。

SELECT * FROM "My Table"

WHERE "Last Name" = 'O''Brien';1.4.2 数据类型

大多数SQL语句并不显式引用数据类型,但是,由于语句中所引用对象的数据类型间的交互作用,语句的返回结果会受到影响。下列对象具有数据类型。

表和视图中的列。

存储过程中的参数。

变量。

返回一个或多个特定数据类型数据值的Transact-SQL函数。

具有返回代码(始终为integer数据类型)的存储过程。

为对象分配数据类型时可以为对象定义以下4个属性。

对象包含的数据种类。

所存储值的长度或大小。

数值的精度(仅适用于数字数据类型)。

数值的小数位数(仅适用于数字数据类型)。

1.基本类型

SQL Server 所支持的数据类型大体可分为精确数字、近似数字、日期和时间、字符串、Unicode字符串、二进制字符串和其他数据类型7种类别,详细信息如表1-4所示。表1-4 SQL Server的数据类型续表

2.用户自定义数据类型

除了上面介绍的数据类型,还可以在SQL Server中创建三种用户自定义数据类型。

一种是从基本数据类型创建的别名数据类型,这样做的目的是为了更清楚地说明对象中值的类型。例如,下面的语句创建了一个基于 datetime 的 birthday 数据类型,用于在employee 的emp_birthday列中存储生日数据。

-- 创建一个允许null的birthday数据类型

CREATE TYPE birthday

FROM datetime NULL;

GO

-- 创建一个使用新数据类型的表

CREATE TABLE employee

(emp_id char(5), emp_first_name char(30), emp_last_name char(40), emp_birthday birthday);

另一种是CLR 用户定义数据类型,它是在Microsoft .NET Framework 公共语言运行时(CLR)使用编程方法创建的,这是从SQL Server 2005 开始提供的一种新功能。此外,包括触发器、存储过程、函数、聚合函数,都可以利用CLR 提供的丰富的编程模型来扩展SQL Server 的功能。

最后一种是用户定义表数据类型,也就是说用户可以定义一个表示表结构的数据类型。这是从SQL Server 2008 开始提供的一种新功能。下面的语句首先创建一个名为LocationTableType的表数据类型,然后创建一个基于该类型的变量,并向其中插入数据和查询数据。

-- 创建一个表数据类型LocationTableType

CREATE TYPE LocationTableType AS TABLE

(

FirstName VARCHAR(50),

LastName VARCHAR(50)

);

GO

-- 创建一个基于LocationTableType的变量

DECLARE @MyTable AS LocationTableType;

-- 向变量中插入数据行

INSERT INTO @MyTable VALUES('Ken','Levy');

INSERT INTO @MyTable VALUES('Sara','Ford');

-- 查询数据

SELECT * FROM @MyTable;

GO

3.数据类型的隐式转换

当两个不同数据类型的表达式用运算符组合后,数据类型优先级规则指定将优先级较低的数据类型转换为优先级较高的数据类型。如果此转换不是所支持的隐式转换,则返回错误。当两个操作数表达式具有相同的数据类型时,运算的结果便为该数据类型。优先级顺序是:用户定义数据类型(最高)、sql_variant、xml、datetime、smalldatetime、float、real、decimal、money、smallmoney、bigint、int、smallint、tinyint、bit、ntext、text、image、timestamp、uniqueidentifier、nvarchar、nchar、varchar、char、varbinary、binary(最低)。1.4.3 函数

与其他程序设计语言中的函数相似,SQL Server的函数可以有零个、一个或多个参数,并返回一个标量值或表格形式的值的集合。1.4.4 表达式

表达式是标识符、值和运算符的组合,SQL Server 可以对其求值以获取结果。访问或更改数据时,可在多个不同的位置使用数据。例如,可以将表达式用作要在查询中检索的数据的一部分,也可以用作查找满足一组条件的数据时的搜索条件。

表达式可以是下列任何一种形式:

常量

函数

列名

变量

子查询

CASE、NULLIF或COALESCE1.4.5 运算符

运算符是表达式的组成部分之一,可以使用运算符执行算术、比较、串联或赋值操作。例如,表达式PriceColumn * 1.1中的乘号(*)使价格提高百分之十。1.4.6 注释

注释是程序代码中不执行的文本字符串,也称为备注。注释可用于对代码进行说明或暂时禁用正在进行诊断的部分、SQL语句和批。使用注释对代码进行说明,便于将来对程序代码进行维护。

SQL Server 支持两种类型的注释字符。

--(双连字符)。该注释字符可与要执行的代码处在同一行,也可另起一行。从双连字符开始到行尾的内容均为注释。对于多行注释,必须在每个注释行的前面使用双连字符。

/* ... */(正斜杠-星号字符对)。这些注释字符可与要执行的代码处在同一行,也可另起一行,甚至可以在可执行代码内部。开始注释对(/*)与结束注释对(*/)之间的所有内容均视为注释。对于多行注释,必须使用开始注释字符对(/*)来开始注释,并使用结束注释字符对(*/)来结束注释。批中的注释没有最大长度限制。

下面是一些有效注释的示例。

试读结束[说明:试读内容隐藏了图片]

下载完整电子书


相关推荐

最新文章


© 2020 txtepub下载