第一课 了解SQL
1.1 数据库基础
1.1.1 数据库
1. 从SQL的角度看,数据库是一个以某种有组织的方式存储的数据集合。最简单的办法是将数据库想象为一个文件柜。这个文件柜是一个存放数据的物理位置,不管数据是什么,也不管数据是如何组织的。
数据库(database):保存有组织的数据的容器(通常是一个文件或一组文件)。
注意: 误用导致混淆:人们通常用数据库这个术语来代表他们使用的数据库软件,这不是正确的,也因此产生了许多混淆。确切的说,数据库软件应称为数据库管理系统(DBMS)。数据库是通过DBMS创建和操纵的容器,而具体它究竟是什么,形式如何,各种数据库都不一样。
1.1.2 表
表(table):某种特定类型数据的结构化清单。
1. 这里关键一点在于,存储在表中的数据是同一种类型的数据或清单。绝不应该将顾客的清单与订单的清单存储在同一个数据库中,否则以后的检索和访问会很困难。应该创建两个表,每个清单一个表。
表明:使表名成为唯一的,实际上是数据库名和表名等的组合。有的数据库还使用拥有者的名字作为唯一名的一部分。也就是说,虽然在相同数据库中不能两次使用相同的表名,但在不同的数据库中完全可以使用相同的表名。
1. 表具有一些特性,这些特性定义了数据在表中如何存储,包含存储什么样的数据,数据如何分解,各部分信息如何命名等信息。描述表的这组信息就是所谓的(schema),模式可以用来描述数据库中特定的表,也可以用来描述整个数据库(和其中表的关系)。(我理解为模式,是在创建表的过程中发生的)。
模式:关于数据库和表的布局及特性的信息。
1.1.3 列和数据类型
1. 表由列组成。列存储表中某部分的信息。
列(column):表中的一个字段。所有表都是由一个或多个列组成的。
分解数据:正确地将数据分解为多个列极为重要。通过分解这些数据,才有可能利用特定的列对数据进行分类和过滤。你可以根据自己的具体需求来决定把数据分解到何种程度。
1. 数据库中每个列都有相应的数据类型。数据类型(datatype)定义了列可以存储哪些数据种类。
数据类型(datatype):所允许的数据的类型。每个表列都有相应的数据类型,他限制(或允许)该列中存储的数据。
1. 数据类型限定了可存储在列中的数据种类。数据类型还帮助正确的分类数据,并在优化磁盘使用方面起重要作用。因此,在创建表时必须特别关注所用的数据类型。
数据类型兼容:数据类型及其名称是SQL不兼容的一个主要原因。虽然大多数基本数据类型得到了一致的支持,但许多高级的数据类型却没有。更糟的是,偶然会有相同的数据类型在不同的DBMS中具有不同的名称。对此用户毫无办法,重要的是在创建表结构时要记住这些差异(最后一句不理解)。
1.1.4 行
1. 表中的数据是按行存储的,所保存的每个记录存储在自己的行内。
行(row):表中的一个记录
是记录还是行?你可能听到用户在提到行时称其为数据库记录(record)。这两个术语多半是可以交替使用的,但从技术上说,行才是正确的术语。
1.1.5 主键
1. 表中每一行都应该有一列(或几列)可以唯一标识自己。
主键(primary key):一列(或一组列),其值能够唯一标识表中每一行。
1. 唯一标识表中每行的这个列(或这几列)称为主键。主键用来表示一个特定的行。没有主键,更新或删除表中特定行就极为困难,因为你不能保证操作只涉及相关的行。
应该总是定义主键:虽然并不总是需要主键,但多数数据库设计者都会保证他们创建的每个表具有一个主键,以便于以后的数据库操作和管理。
1. 表中的任何列都可以作为主键,只要它满足一下条件:
1. 任意两行都不具有相同的主键值。
2. 每一行都必须具有一个主键值(主键列不允许NULL值)
3. 主键列中的值不允许修改或更新
4. 主键值不能重用(如果某行从表中删除,它的主键不能赋值给以后的新行)
2. 主键通常定义在表的一列上,但并不是必须这么做,也可以一起使用多个列作为主键。在使用多列作为主键时,上述条件必须应用到所有列,所有列值得组合必须是唯一的(但单个列的值可以不唯一)
1.2 什么是SQL
1. SQL是Structured Query Language(结构化查询语言)的缩写。SQL是一种专门用来与数据库沟通的语言。
2. SQL中只有很少的词,这是有意而为的,设计SQL的目的是很好的完成一项任务-提供一种从数据库中读写数据的简单有效的方法。
3. SQL有如下的优点:
1. SQL不是某个特定数据库供应商专有的语言。几乎所有重要的DBMS都支持SQL,所以学习此语言你几乎能与所有数据库打交道。
2. SQL简单易学。它的语句全都是由有很强描述性的英语单词组成,而且这些单词的数目不多。
3. SQL虽然看上去很简单,但实际上是一种强有力的语言,灵活使用其语言元素,可以进行非常复杂和高级的数据库操作。
SQL的扩展:许多DBMS厂商通过增加语句或指令,对SQL进行扩展。这种扩展的目的是提供执行特定操作的额外功能或简化方法。虽然这种扩展很有用,但一般都是针对个别DBMS的,很少有两个以上的供应商支持这种扩张。
第二课 检索数据
1. 这一课介绍如何使用SELECT语句从表中检索一个或多个数据列
2.1 SELECT语句
1. SQL语句是由简单的英语单词构成的。这些单词称为关键字,每个SQL语句都是由一个或多个关键字构成的。最经常使用的SQL语句大概就是SELECT语句了。他的用途是从一个或多个表中检索信息。
关键字(keyword):作为SQL的组成部分的保留字。关键字不能作为表或列的名字。
1. 为了使用SELECT检索表数据,必须至少给出两条信息-想选择什么,以及从什么地方选择。
2.2 检索单个列
1. 从简单的SQL SELECT语句讲起,此语句如下所示:
1. SELECT prod_name FROM Products;
2. 分析:上述语句利用SELECT语句从Products表中检索一个名为prod_name的列。所需的列名写在SELECT关键字后,FROM关键字指出从那个表中检索数据。
结束SQL语句:多条SQL语句必须以分号(;)分隔。果树DBMS不需要在单条SQL语句后加分号。事实上,即使不一定需要,加上分号也肯定没有坏处。
SQL语句和大小写:SQL语句不区分大小写。不过,一定要认识到虽然SQL是不区分大小写的,但是表名、列名和值可能有所不同。
2.3 检索多个列
1. 想要从一个表中检索多个列,仍然使用相同的SELECT语句。唯一不同是必须在SELECT关键字后给出多个列名,列名间必须以逗号分割。
担心逗号:在选择多个列时,一定要在列名之间加上逗号,但最后一个列名不加。如果在最后一个列名后加了逗号,将出现错误。
1. 下面的SELECT语句从Products表中选择3列
1. SELECT prod_id,prod_name,prod_price FROM Products;
数据表示:SQL语句一般返回原始的、无格式的数据。数据的格式化是表示问题,而不是检索问题。因此,表示一般在显示该数据的应用程序中规定。通常很少直接使用实际检索出的数据。
2.4 检索所有列
1. 除了指定所需的列外,SELECT语句还可以检索所有的列而不必逐个列出它们。在实际列名的位置使用星号(*)通配符就可以做到这点,此语句如下所示。
1. SELECT * FROM Products;
1. 分析:如果给定一个通配符(*),则返回表中所有列。列的顺序一般是列在表中出现的物理顺序,但并不总是如此。不过,SQL数据很少这样(通常,数据返回给应用程序,根据需要进行格式化)。因此,这不应该造成什么问题。
使用通配符:一般而言,除非你确实需要表中的每一列,否则最好别使用*通配符。虽然使用通配符能让你自己省事,不用明确列出所需列,但检索不需要的列通常会减低检索和应用程序的性能。
检索未知列:使用通配符有一个大优点。由于不明确指定列明(因为星号检索每一列),所以能检索出名字未知的列。
2.5 检索不同的值
1. 如果你不希望每个值每次都出现,该怎么办呢?办法就是使用DISTINCT关键字,顾名思义,它指示数据库只返回不同的值。
1. 语句:SELECT DISTINCT vend_id FROM Products;
1. 分析:SELECT DISTINCT vend_id告诉DBMS只返回不同(具有唯一性)的vend_id行。如果使用DISTINCT关键字,它必须直接放在列名的前面。
不能部分使用DISTINCT:DISTINCT关键字作用于所有的列,不仅仅是跟在其后的那一列。
2.6 限制结果
1. SELECT语句返回指定表中所有匹配的行,很可能是每一行。如果你只想返回第一行或者一定数量的行,该怎么办呢?这是可行的,如果你使用MySQL,需要使用LIMIT子句,像这样:
1. SELECT prod_name FROM Products LIMIT 5;
1. 分析:上述代码使用SELECT语句来检索单独的一列数据。LIMIT 5 指示MySQL返回不超过5行的数据。
2. 为了得到后面的5行数据,需要指定从哪儿开始以及检索的行数,像这样:
1. SELECT prod_name FROM Products LIMIT 5 OFFSET 5;
1. 分析:LIMIT 5 OFFSET 5指示MySQL返回从第5行起的5行数据。第一个数字是指从哪儿开始,第二个数字是检索的行数。
2. 单独一个LIMIT指定返回的行数。带OFFSET的LIMIT指定从哪儿开始。
第0行:第一个被检索的行是第0行,而不是第1行。因此,LIMIT 1 OFFSET 1会检索第2行,而不是第1行。
MySQL快捷键:MySQL支持简化版的LIMIT 4 OFFSET 3语句,即LIMIT 3,4。使用这个语法,逗号之前的值对应LIMIT,逗号之后的值对应OFFSET。
2.7 使用注释
1. 可以看到,SQL语句是由DBMS处理的指令。如果你希望包括不进行处理和执行的文本,该怎么办呢?为什么你想要这么做呢?原因有以下几点:
1. 随着SQL语句变长,复杂性增加,你就会添加一些描述性的注释,这便于你自己今后参考,或者供项目后续参与人员参考。这些注释需要嵌入在SQL脚本中,但显然不能进行实际的DBMS处理。
2. 这同样适用于SQL文件开始处的内容,他可能包含程序员的联系方式、程序描述以及一些说明。
3. 注释的另一个重要应用是暂时停止要执行的SQL代码。如果你碰到一个长SQL语句,而只想测试它的一部分,那么应该注释掉一些代码,以便DBMS将其视为注释而加以忽略。
2. 很多DBMS都支持各种形式的注释语法。先看行内注释
1. 注释使用--(两个连字符)嵌在行内。--之后的文本就是注释,下面是另一种形式的行内注释(虽然这种形式很少得到支持)。
2. 在一行的开始处使用#,这一整行都将作为注释。
3. 你也可以进行多行注释,注释可以在脚本的任何位置停止和开始。注释从/*开始,到*/结束。这种方式常用于给代码加注释。
第三课 排序检索数据
1. 这一课讲授如何使用SELECT语句的ORDER BY 字句,根据需要排序检索出的数据。
3.1 排序数据
1. 如果SELECT查询语句中不加排序子句,则没有特定的顺序。
2. 其实,检索出的数据并不是随机显示的。如果不排序,数据一般将以它在底层中出现的顺序显示,这有可能是数据最初添加到表中的顺序。但是,如果数据随后进行过更新或删除,那么这个顺序将会受到DBMS重用回收存储空间的方式的影响。因此,如果不明确控制的话,则最终的结果不能(也不应该)依赖改排序顺序。关系数据库设计理论认为,如果不明确规定排序顺序,则不应该假定检索出的数据的顺序有任何意义。
子句(clause):SQL语句由子句构成,有些子句是必须的,有些则是可选的。一个子句通常由一个关键字加上所提供的数据组成。子句的例子有SELECT语句的FROM子句。
3. 为了明确的排序用SELECT语句检索出的数据,可使用ORDER BY子句。ORDER BY子句取一个或多个列的名字,据此对输出进行排序。请看下面的列子:
1. SELECT prod_name FORM Products ORDER BY prod_name;
1. 分析:除了指示DBMS软件对prod_name列以字母顺序排序数据的ORDER BY子句外,这条语句与前面的语句相同。
ORDER BY子句的位置:在指定一条ORDER BY子句时,应该保证它是SELECT语句中最后一条子句。如果它不是最后的子句,将会出现错误消息。
通过非选择列进行排序:通常,ORDER BY子句中使用的列将是为显示而选择的列。但是,实际上并不一定要这样,用非检索的列排序数据是完全合法的。
3.2 按多个列排序
1. 通常需要按不止一个列进行数据排序。例如,如果要显示雇员名单,可能希望按姓和名排序(首先按姓排序,然后在每个姓中按名排序)。如果多个雇员有相同的姓,这样做很有用。
2. 要按多个列排序,简单指定列名,列名间用逗号分开即可(就像选择多个列时那样)
3. 下面的代码检索3个列,并按其中两个列队结果进行排序-首先按价格,然后按名称排序。
1. SELECT prod_id,prod_price,prod_name FROM Products ORDER BY prod_price,prod_name;
4. 重要的是理解在按多个列排序时,排序的顺序完全按规定进行。换句话说,对于上述语句,仅在多行具有相同的prod_price值时才对产品按prod_name进行排序。如果prod_price列中所有的值都是唯一个,则不会按prod_name排序。
3.3 按列位置排序
1. 除了能用列名指出排序顺序外,ORDER BY还支持按相对列位置进行排序。例子如下
1. SELECT prod_id,prod_price,prod_name FROM Products ORDER BY 2, 3;
1. 分析:这个与上面的语句区别在于ORDER BY子句。SELECT清单中指定的是选择列的相对位置而不是列名。ORDER BY 2表示按SELECT清单中的第二个列prod_name进行排序。ORDER BY 2,3((从 1 开始计数))表示先按prod_price,再按prod_name进行排序。
2. 这一技术的主要好处在于不用重新输入列名。但它也有缺点。首先,不明确给出列名有可能造成错用列名排序。其次,在对SELECT清单进行更改时容易错误的对数据进行排序(忘记对ORDER BY子句做相应的改动)。最后,如果进行排序的列不在SELECT清单中,显然不能使用这项技术。
按非选择列排序:显然,当根据不出现在SELECT清单中的列进行排序时,不能采用这项技术。但是,如果有必要,可以混合使用实际列名和相对列位置。
3.4 指定排序方向
1. 数据排序不限于升序排列(从A到Z),这只是默认的排序顺序。还可以使用ORDER BY子句进行降序(从Z到A)排序。为了进行降序排序,必须指定DESC关键字。下面的例子以价格降序来排序产品(最贵的排在最前面):
1. SELECT prod_id,prod_price,prod_name FROM Products ORDER BY prod_price DESC;
2. 如果打算用多个列排序,该怎么办?下面的例子以降序排序产品(最贵的在前面),再加上产品名:
1. SELECT prod_id, prod_price, prod_name FROM Products ORDER BY prod_price DESC, prod_name;
2. DESC关键字只应用到直接位于其前面的列名。在上例中,只对prod_price列指定DESC,对prod_name列不指定。因此,prod_price列以降序排序,而prod_name列(在每个价格内)仍然按标准的升序排序。
在多个列上降序排序:如果想在多个列上进行降序排序,必须对每一列指定DESC关键字。
1. 请注意,DESC是DESCENDING的缩写,这两个关键字都可以使用。与DESC相对的是ASC(或ASCENDING),在升序排序时可以指定他。但实际上,ASC没有多大用处,因为升序是默认的(如果既不指定ASC也不指定DESC,则假定为ASC)。
区分大小写和排序顺序:在对文本性数据进行排序时,A与a相同吗?a位于B之前,还是Z之后?这些问题不是理论问题,其答案取决于数据库的设置方式。在字典(dictionary)排序顺序中,A被视为与a相同,这是大多数数据库管理系统的默认行为。但是,许多DBMS允许数据库管理员在需要时改变这种行为(如果你的数据库包含大量外语字符,可能必须这样做)(这句话不理解)。这里的关键问题是,如果确实需要改变这种排序顺序,用简单的ORDER BY子句可能做不到。你必须请求数据库管理员的帮助。
第四课 过滤数据
1. 这一课将讲授如何使用SELECT语句的WHERE子句指定搜索条件。
4.1 使用WHERE子句
1. 数据库表一般包含大量的数据,很少需要检索表中的所有行。通常只会根据特定操作或报告的需要提取表数据的子集。只检索所需数据需要指定搜索条件(search criteria),搜索条件也称为过滤条件(filter condition)。
2. 在SELECT语句中,数据根据WHERE子句中指定的搜索条件进行过滤。WHERE子句在表名(FROM子句)之后给出,如下所示:
1. SELECT prod_name,prod_price FROM Products WHERE prod_price = 3.49;
1. 分析:这条语句从products表中检索两个列,但不返回所有行,只返回prod_price值为3.49的行。
SQL过滤与应用过滤:数据也可以在应用层过滤。为此,SQL的SELECT语句为客户端应用检索出超过实际所需的数据,然后客户端代码对返回数据进行循环,提取出需要的行。通常这种的做法极为不妥。优化数据库后可以更快速有效的对数据进行过滤。而让客户端应用(或开发语言)处理数据库的工作将会极大地影响应用的性能,并且使所创建的应用完全不具备可伸缩性。此外,如果在客户端过滤数据,服务器不得不通过网络发送多余的数据,这将导致网络带宽的浪费。
4.2 HWERE子句操作符
4.2.1 检查单个值
何时使用引号:单引号用来限定字符串。如果将值与字符串类型的列进行比较,就需要限定引号。用来与数值列进行比较的值不用引号。
4.2.3 范围值检查
1. 要检查某个范围的值,可以使用BETWEEN操作符。其语法与其他WHERE子句的操作符稍有不同,因为他需要两个值,即范围的开始值和结束值。
2. 下面的例子说明如何使用BETWEEN操作符,他检索价格在5美元和10美元之间的所有产品。
1. SELECT prod_name,prod_price FROM Products WHERE prod_price BETWEEN 5 AND 10;
1. 分析:从这个例子可以看到,在使用BETWEEN时,必须指定两个值-所需范围的低端值和高端值。这两个值必须用AND关键字分隔。BETWWEN匹配范围中的所有值,包括指定的开始值和结束值。
4.2.4 空值检查
1. 在创建表时,表设计人员可以指定其中的列是否不包含值。在一个列不包含值时,称其包含空值NULL。
NULL:无值(no value),它与字段包含0、空字符串或仅仅包含空格不同。
1. 确定值是否为NULL,不能简单的检查是否=NULL。SELECT语句有一个特殊的WHERE子句,可用来检查具有NULL值得列。这个WHERE子句就是IS NULL 子句。其语法如下:
1. SELECT prod_name FROM Products WHERE prod_price IS NULL;
NULL和非匹配:通过过滤选择不包含指定值的所有行时,你可能会希望返回含NULL值得行。但是这做不到。因为未知(unknown)有特殊的含义,数据库不知道它们是否匹配,所以在进行匹配过滤或非匹配过滤时,不会返回这些结果。过滤数据时,一定要验证被过滤列中含NULL的行确实出现在返回的数据中。
第五课 高级数据过滤
1. 这一课讲授如何组合WHERE子句以建立功能更强、更高级的搜索条件。我们还将学习如何使用NOT和IN操作符。
5.1 组合WHERE子句
1. 上一课介绍的所有WHERE子句在过滤数据时使用的都是单一的条件。为了进行更强的过滤控制,SQL允许给出多个WHERE子句。这些子句有两种使用方式,即以AND子句或OR子句的方式使用。
操作符(operator)
1. 用来联结或改变WHERE子句中的子句的关键字,也称为逻辑操作符(logical operator)。
5.1.1 AND操作符
1. 要通过不止一个列进行过滤,可以使用AND操作符给WHERE子句附加条件。下面的代码给出了一个例子:
1. 输入:
1. SELECT prod_id,prod_price,prod_name FROM Products WHERE vend_id = 'DLL01' AND prod_price <= 4;
2. 分析:
1. 此SQL语句检索由供应商DLL01制造且价格小于等于4美元的所有产品的名称和价格。这条SELECT语句中的WHERE子句包含两个条件,用AND关键字联结在一起。AND指示DBMS只返回满足所有给定条件的行。如果某个产品由供应商DLL01制造,但价格高于4美元,则不检索它。类似的,如果产品价格小于4美元,但不是由指定供应商制造的也不被检索。
AND
1. 用在WHERE子句中的关键字,用来指示满足所有给定条件的行
说明:没有ORDER BY子句
1. 为了节省空间,也为了减少你的输入。因此,在很多例子里省略了ORDER BY子句。
5.1.2 OR操作符
1. OR操作符与ADN操作符正好相反,他指示DBMS搜索匹配任一条件的行。事实上,许多DBMS在OR WHERE子句的第一个条件得到满足的情况下,就不再计算第二个条件了(在第一个条件满足时,不管第二个条件是否满足,相应的行都将被检索出来)。
1. 输入:
1. SELECT prod_name,prod_price FROM Products WHERE vend_id = 'DLL01' OR vend_id = 'BRS01';
2. 分析:
1. 此SQL语句检索由任一个指定供应商制造的所有产品名和价格。OR操作符告诉DBMS匹配任一条件而不是同时匹配两个条件。如果在这里使用的是AND操作符,则没有数据返回(因为会创建没有匹配行的WHERE子句)。
OR
1. WHERE子句中使用的关键字,用来表示检索匹配任一给定条件的 行。
5.1.3 求值顺序
1. WHERE子句可以包含任意数目的AND和OR操作符。允许两者结合以进行复杂、高级的过滤。
2. 但是,组合AND和OR会带来一个有趣的问题。为了说明这个问题,来看一个例子。假如需要列出价格为10美元及以上,且由DLL01或BRS01制造的所有商品。
1. 输入:
1. SELECT prod_name,prod_price FROM Products WHERE vend_id = 'DLL01' OR vend_id = 'BRS01' AND prod_price >= 10;
2. 上面语句的输出肯定与我们的设想不一致,其原因在于求值的顺序。SQL(想大多数语言一样)在处理OR操作符之前,优先处理AND操作符。当SQL看到上述WHERE子句时,它理解为:由供应商BRS01制造的价格为10美元以上的所有产品,以及由供应商DLL01制造的所有产品,而不管其价格如何。**换句话说,由于AND在求值过程中优先级更高,操作符被错误地组合了。此问题的解决方法是使用圆括号对操作符进行明确分组。**
1. 输入:
1. SELECT prod_name,prod_price FROM Products WHERE (vend_id = 'DLL01' OR vend_id = 'BRS01') AND prod_price >= 10;
2. 分析:
1. 这条SELECT语句与前一条的唯一差别是,将前两个条件用圆括号括了起来。因为圆括号具有比AND或OR操作符更高的求值顺序,所以DBMS首先过滤圆括号内的OR条件。这时,SQL语句变成了选择由供应商DLL01或BRS01制造的且价格在10美元及以上的所有产品,这正是我们希望的结果。
提示:在WHERE子句中使用圆括号
1. 任何时候使用具有AND和OR操作符的WHERE子句,都应该使用圆括号明确地分组操作符。不要过分依赖求值顺序,即使它确实如你希望的那样。使用圆括号没有什么坏处,它能消除歧义。
5.2 IN操作符
1. IN操作符用来指定条件范围,范围内的每个条件都可以进行匹配。IN取一组由逗号分隔、括在圆括号中合法值。
1. 输入:
1. SELECT prod_name,prod_price FROM Products WHERE vend_id IN ('DLL01','BRS01') ORDER BY prod_name;
2. 分析:
1. 此SELECT语句检索由供应商DLL01和BRS01制造的所有产品。IN操作符后跟由逗号分隔的合法值,这些值必须括在圆括号中
2. 你可能会猜测IN操作符完成了与OR相同的功能,恭喜你猜对了!下面的SQL语句完成与上面的例子相同的工作。
1. SELECT prod_name,prod_price FROM Products WHERE vend_id = 'DLL01' OR vend_id = 'BRS01' ORDER BY prod_name;
2. 由上述的两组语句可以看出OR能实现IN相同的功能,那为什么要使用IN操作符呢?其优点如下:
1. 在有很多合法选项时,IN操作符的语法更清楚,更直观。
2. 在与其它AND和OR操作符组合使用IN时,求值顺序更容易管理。
3. IN操作符一般比一组OR操作符执行得更快(在上面这个合法选项很少的例子中,你看不出性能差异)。
4. **IN的最大优点是可以包含其它SELECT语句**,能够动态的建立WHERE子句。
IN
1. WHERE子句中用来指定要匹配值的清单的关键字,功能与OR相等。
5.3 NOT操作符
1. WHERE子句中的NOT操作符有且只有一个功能,那就是否定其后所跟的任何条件。因为NOT从不单独使用(他总是与其它操作符一起使用),所以它的语法与其它操作符有所不同。NOT关键字可以用在要过滤的列前,而不仅是在其后。
NOT
1. WHERE子句中用来否定其后条件的关键字。
例子
1. 下面的例子说明NOT的用法。为了列出除DLL01之外的所有供应商制造的产品,可编写如下的代码:
1. 输入:
1. SELECT prod_name FROM Products WHERE NOT vend_id = 'DLL01' ORDER BY prod_name;
2. 分析:
1. 这里的NOT否定跟在其后的条件,因此,DBMS不是匹配vend_id为DLL01,而是匹配非DLL01之外的所有东西。
2. 这个例子也可以用<>操作符来完成,如下所示:
1. 输入:
1. SELECT prod_name FROM Products WHERE vend_id <> 'DLL01' ORDER BY prod_name;
2. 分析:
1. 为什么使用NOT?对于这里的这种简单的WHERE子句,使用NOT确实没什么优势。但在更复杂的子句中,NOT是非常有用的。例如,在与IN操作符联合使用时,NOT可以非常容易地找出与条件列表不匹配地行。
说明:MariaDB中地NOT
1. MariaDB支持使用NOT否定IN、BETWEEN和EXISTS子句。大多数DBMS允许使用NOT否定任何条件。
第6课 用通配符进行过滤
1. 这一课介绍什么是通配符、如何使用通配符以及怎样使用LIKE操作符进行通配搜索,以便对数据进行复杂过滤。
6.1 LIKE操作符
1. 前面介绍的所有操作符都是针对已知值进行过滤的。不管是匹配一个值还是多个值,检验大于还是小于已知值,或者检查某个范围的值,其共同点是过滤中使用的值都是已知的。但是,这种过滤方式并不是任何时候都好用。例如,怎样搜索产品名中包含文本bean bag的所有产品?用简单的操作符肯定不行,必须使用通配符。利用通配符,可以创建比较特定数据的搜索模式。
通配符(wildcard):用来匹配值的一部分的特殊字符。
搜索模式:由字面值、通配符或两者组合构成的搜索条件。
2. 通配符本身实际上是SQL的WHERE子句中有特殊含义的字符,SQL支持几种通配符。**为在搜索子句中使用通配符,必须使用LIKE操作符**。LIKE指示DBMS,后跟的搜索模式利用通配符匹配而不是简单的相等匹配进行比较。
谓词(predicate):操作符何时不是操作符?答案是,它作为谓词时。从技术上说,LIKE是谓词而不是操作符。虽然最终结果是相同的,但应该对此术语有所了解,以免在SQL文献中或手册中遇到此术语不知所云。
3. **通配符搜索只能用于文本字段(字符串),非文本数据类型不能使用通配符搜索。**
6.1.1 百分号(%)通配符
1. 最常使用的通配符是百分号(%)。在搜索串中,**%表示任何字符出现任意次数**。例如,为了找出所有以词Fish起头的产品,可以发布以下SELECT语句:
1. 输入:
```
SELECT prod_id,prod_name
FROM Products
WHERE prod_name LIKE 'Fish%';
```
2. 分析:
1. 此例子使用了搜索模式'%Fish'。在执行这条子句时,将检索任意以Fish起头的词。%告诉DBMS接受Fish之后的任意字符,不管它有多少字符。
2. 通配符可以在搜索模式中的任意位置使用,并且可以使用多个通配符。下面的例子使用两个通配符,它们位于模式的两端:
1. 输入:
```
SELECT prod_id,prod_name
FROM Products
WHERE prod_name LIKE '%bean bag%';
```
2. 分析
1. 搜索模式'%bean bag%'表示匹配任何位置上包含文本bean bag的值,不论它之前或之后出现什么字符。
3. 通配符也可以出现在搜索模式的中间,虽然这样做不太有用。下面的例子找出以F起头,以y结尾的所有产品:
1. 输入:
```
SELECT prod_name
FROM Products
WHERE prod_name LIKE 'F%y';
```
提示:根据部分信息搜索电子邮件地址:有一种情况下把通配符放在搜索模式中间是很有用的,就是根据邮件地址的一部分来查找电子邮件,例如:WHERE email LIKE ‘b%@forta.com’。
说明:请注意后面所跟的空格,意思是如果输入值时不小心将空格输入在最后,那么空格也会被当作这个值的一部分。(切记不要手贱在值得前后加上空格)。
注意:请注意NULL:通配符开起来像是可以匹配任何东西,但有个例外,这就是NULL。子句WHERE prod_name LIKE '%'不能匹配值为NULL的列值。
6.1.2 下划线(_)通配符
1. 另一个有用的通配符是下划线(_)。下划线的用途与%一样,但它**只匹配单个字符,而不是多个字符**。
说明DB2通配符:DB2不支持通配符_。
说明:Access通配符:如果使用的是Microsoft Access,需要使用?而不是_。
2. 例子:
1. 输入
1. SELECT prod_id,prod_name FROM Products WHERE prod_name LIKE '__ inch teddy bear';
说明:请注意后面所跟的空格:与上一例一样,可能需要给这个模式添加一个通配符。
2. 分析:
1. 这个WHERE子句中的搜索模式给出了后面跟有文本的两个通配符。**与%能匹配0个字符不同,_总是刚好匹配一个字符,不能多也不能少。**
6.1.3 方括号([])通配符
1. 方括号([])通配符用来指定一个字符集,它必须匹配指定位置(通配符的位置)的一个字符。
并不总是支持集合:与前面描述的通配符不一样,并不是所有DBMS都支持用来创建集合的[]。只有微软的Access和SQL Server支持集合。为确定你使用的DBMS是否支持集合,请参阅相应的文档。
6.2 使用通配符的技巧
1. 正如所见,SQL的通配符很有用。但这种功能是有代价的,**即通配符搜索一般比前面讨论的其它搜索要耗费更长处理时间。**这里给出一些使用**通配符时要记住的技巧**。
1. 不要过度使用通配符。如果其它操作符能达到相同的目的,应该使用其它操作符。
2. 在确实需要使用通配符时,也尽量不要把它们用在搜索模式的开始处。把通配符置于开始处,搜索起来是最慢的。
3. 仔细注意通配符的位置。如果放错地方,可能不会返回想要的数据。
第7课 创建计算字段
7.1 计算字段
1. 由于存储在数据库中的数据一般不是应用程序所需要的格式。这就是计算字段可以派上用场的地方了。**计算字段并不是实际存在于数据库表中**。计算字段是运行时在SELECT语句内创建的。
字段(field):基本上与列(column)的意思相同,经常互换使用,不过数据库列一般称为列,而术语字段通常与计算字段一起使用。
2. 需要特别注意,只有数据库知道SELECT语句中那些列是实际的表列,那些列是计算字段。从客户端来看(如应用程序)来看,计算字段的数据与其它列的数据的返回方式相同。
客户端与服务器的格式:在SQL语句内可完成的许多转换和格式化工作都可以直接在客户端应用程序内完成。但一般来说,在数据库服务器上完成这些操作比在客户端中完成要快很多。
7.2 拼接字段
1. Vendors表包含供应商名和地址信息。假如要生成一个供应商报表,需要在格式化的名称(位置)中列出供应商的位置。
2. 此报表需要一个值,而表中数据存储在两个列vend_name和vend_country中。此外需要用括号将vead_country括起来,这些东西都没有存储在数据库表中。这个返回供应商名称和地址的SELECT语句很简单,但我们是如何创建这个组合值呢?
拼接:将值联结到一起(将一个值附加到另一个值)构成单个值。
3. 解决办法是把两个列拼接起来。在SQL中的SELECT语句中,可使用一个特殊的操作符来拼接两个列。根据你所使用的DBMS,此操作符可使用加号(+)或两个竖杆(||)表示。在MySQL和MariaDB中,必须使用特殊函数。
4. 例子:
1. 输入:
1. SELECT vend_name + '(' + vend_country + ')' FROM Vendors ORDER BY vead_name;
2. MySQL的输入:
1. SELECT Concat(vend_name,'(',vend_country,')') FROM Vendors ORDER BY vend_name
3. 分析:
1. 上面这个SQL拼接以下元素:
1. 存储在vend_name列中的名字
2. 包含一个空格和一个左圆括号的字符串
3. 存储在vend_country列中的国家
4. 包含一个右圆括号的字符串。
2. 输出为包含上述四个元素的一个列(计算字段)
4. 如果你想要去掉这些空格。这就可以使用SQL的RTRIM()函数来完成,如下所示:
TRIM函数:RTRIM()(去掉字符串右边的空格)、LTRIM()(去掉字符串左边的空格)以及TRIM()(去掉字符串左右两边的空格)。
使用别名
1. 从前面的输出可以看到,SELECT语句可以很好地拼接地址字段。但是,这个新计算列地名字是什么呢?实际上它没有名字,它只是一个值。如果仅在SQL查询工具中查看一下结果,这样没什么不好。但是一个未命名地列不能用于客户端应用中,因为客户端没办法引用它。
2. 为了解决这个问题,SQL支持列别名。别名(alias)是一个字段或值得替换名。别名用AS关键字赋予。请看下面地SELECT语句:
1. 输入:
1. SELECT Concat(vend_name,'(',vend_country,')') AS vend_title FROM Vendors ORDER BY vend_name;
2. 分析
1. SELECT语句本身与之前使用地相同,只不过这里的计算字段之后跟了文本 AS vend_title。它指示SQL创建一个包含指定计算结果地名为vend_title地计算字段。从输出可以看到,结果与之前地相同,但现在列名为vend_title,任何客户端应用都可以按名称引用这个列,就像它是一个实际地列表一样。
别名地其他用途:别名还有其他用途。常见地用途包括在实际地表列名包含不合法地字符(如空格)时重新命名它,在原来的名字含混或容易误解时扩充它。
别名:别名地名字既可以是一个单词,也可以是一个字符串。如果是后者,字符串应该括在引号中。虽然这种做法是合法地,但不建议这么去做。多单词地名字可读性高,不过会给客户端应用带来各种问题。因此,别名常见地使用是将多个单词地列名重命名为一个单词地名字。
导出列:别名有时也称为导出列(derived column),不管怎么叫,它们所代表地是相同的东西。
7.3 执行算数计算
1. 计算字段的另一个常见用途是对检索出的数据进行算术计算。举个例子,Orders表包含收到的所有订单,OrderItems表包含每个订单中的各项物品。下面的SQL语句检索订单号20008中的所有物品:
1. 输入:
1. SELECT prod_id,quantity,item_price FROM OrderItems WHERE order_num = 20008
2. item_price列包含订单中每项物品的单价。如下汇总物品的价格(单价乘以订购数量):
1. 输入:
1. SELECT prod_id,quantity,item_price,quantity*item_price AS expanded_price FROM OrderItems WHERE order_num = 20008;
2. 分析:
1. 输出中显示的expanded_price列是一个计算字段,此计算为quantity*item_price。客户端应用现在可以使用这个新计算列,就像使用其他列一样。
2. SQL支持基本算数操作符(+,-,*,/)。此外,圆括号可用来区分优先顺序。
如何测试计算:SELECT语句为测试、检验函数和计算提供了很好的方法。虽然SELECT通常用于从表中检索数据,但是省略了FROM字句后就是简单的访问和处理表达式,例如SELECT 3*2;将返回6,SELECT Trim(’ abc ');将返回abc,SELECT Now();使用Now()函数返回当前日期和时间。
第8课 使用函数处理数据
8.1 函数
1. 与大多数其他计算机语言一样,SQL也可以用函数来处理数据。函数一般是在数据上执行的,为数据的转换和处理提供了方便。前面使用的RTRIM()就是一个函数。
函数带来的问题
1. 虽然所有类型的函数一般都可以在每个DBMS中使用,但各个函数的名称和语法可能极其不同。
8.2 使用函数
1. 大多数SQL实现支持以下类型的函数:
1. 用于处理文本字符串(如删除或填充值,转换值为大写或小写)的文本函数。
2. 用于在数值数据上进行算数操作(如返回绝对值,进行代数运算)的数值函数。
3. 用于处理日期和时间值并从这些值中提取特定成分(如返回两个日期之差,检查日期有效性)的日期和时间函数。
4. 返回DBMS正使用的特殊信息(如返回用户登录信息)的系统函数。
2. 我们在上一课看到函数用于SELECT后面的列名,但函数的作用不仅于此。它还可以作为SELECT语句的其他成分,如在WHERE子句中使用,在其他SQL语句中使用等。
8.2.1 文本处理函数
1. 我们以及看过一个文本处理函数的例子,其中使用RTRIM()函数来去除列值右边的空格 。下面是另一个例子,这次使用的是UPPER()函数:
1. 输入:
1. SELECT vend_name,UPPER(vend_name) AS vend_name_upcase FROM Vendors ORDER BY vend_name
2. 常用的文本处理函数
1. LEFT()(或使用子字符串函数):返回字符串左边的字符
2. LENGTH()(也使用DATALENGTH()或LEN()):返回字符串的长度
3. LOWER():将字符串转换为小写
4. LTRIM():去掉字符串左边的空格
5. RIGHT()(或使用子字符串函数):返回字符串右边的字符
6. RTRIM():去掉字符串右边的空格
7. SOUNDEX():返回字符串的SOUNDEX值,这个可以不要关注
8. UPPER():将字符串转换为大写
8.2.2 日期和时间处理函数
1. 日期和时间采用相应的数据类型存储在表中,每种DBMS都有自己的特殊形式。日期和时间值以特殊的格式存储,以便能快速和有效地排序或过滤,并且节省物理存储空间。
2. 应用程序一般不使用日期和时间的储存格式,因此日期和时间函数总是用来读取、统计和处理这些值。由于这个原因,日期和时间函数在SQL中具有重要的作用。**遗憾的是,它们很不一致,可移植性最差。**
8.2.3 数值处理函数
1. 数值处理函数仅处理数值数据。这些函数一般用于代数、三角或几何运算,因此不像字符串或日期-时间处理函数使用那么频繁。
2. 具有讽刺意味的是,在主要DBMS的函数中,数值函数是最一致、最统一的函数。
3. 列出了一些常用的数值处理函数
1. ABS():返回一个数的绝对值
2. COS():返回一个角度的余弦
3. EXP():返回一个属的指数值
4. PI():返回圆周率
5. SIN():返回一个角度的正弦
6. SQRT():返回一个数的平方根
7. TAN():返回一个角度的正切
第9课 汇总数据
1. 这一课介绍什么是SQL的汇聚函数,如何利用它们汇总表的数据。
9.1 汇聚函数
1. 我们经常需要汇总数据而不用把它们实际检索出来,为此SQL提供了专门的函数。使用这些函数,SQL查询可用于检索数据,以便分析和报表生成。
2. 为方便这种类型的检索,SQL给出了5个聚集函数。这些函数能进行上述检索。与前一章介绍的数据处理不同,SQL的聚集函数在各种主要SQL实现中得到了相当一致的支持。
聚集函数(aggregate function):对某些行运行的函数,计算并返回一个值。
3. SQL聚集函数:
1. AVG():返回某列的平均值
2. COUNT():返回某列的行数
3. MAX():返回某列的最大值
4. MIN():返回某列的最小值
5. SUM():返回某列值之和
9.1.1 AVG()函数
1. AVG()通过对表中行数计数并计算其列值之和,求得该列的平均值。AVG()可用来返回所有列的平均值,也可以用来返回特定列或行的平均值。
只用于单个列:AVG()只能用来确定特定数值列的平均值,并且列名必须作为函数参数给出。为了获得多个列的平均值,必须使用多个AVG()函数。
NULL值:AVG()函数忽略列值为NULL的行。
9.1.2 COUNT()函数
1. COUNT()函数进行计数。可利用COUNT()确定表中行的数目或符合特定条件的行的数目。
2. COUNT()函数有两种使用方式:
1. 使用COUNT(*)对表中行的数目进行计数,不管表列中包含的是空值(NULL)还是非空值。
2. 使用COUNT(column)对特定列中具有值的行进行计数,忽略NULL值。
NULL值:如果指定列名,则COUNT()函数会忽略指定列的值为空的行,但如果COUNT()函数中用的是’*’,则不忽略。
9.1.3 MAX()函数
1. MAX()返回指定列中的最大值。MAX()要求指定列名。
对非数值数据使用MAX():虽然MAX()一般用来找出最大的数值或日期值,但许多(并非所有)DBMS允许将它用来返回任意列中的最大值,包括返回文本列中的最大值。在用于文本数据时,MAX()返回按该列排序后的最后一行。
NULL值:MAX()函数忽略列值为NULL的行。
9.1.4 MIN()函数
1. MIN()的功能正好与MAX()功能相反,它返回指定列的最小值。与MAX()一样,MIN()要求指定列名。
对非数值数据使用MAX():虽然MAX()一般用来找出最小的数值或日期值,但许多(并非所有)DBMS允许将它用来返回任意列中的最小值,包括返回文本列中的最小值。在用于文本数据时,MAX()返回按该列排序后的最前面的行。
NULL值:MAX()函数忽略列值为NULL的行。
9.1.5 SUN()函数
1. SUN()用来返回指定列值的和(总计)。
2. SUN()也可以用来合计计算值。
在多个列上进行计算:利用标准的算术操作符,所有聚合函数都可用来执行多个列上的计算
NULL值:SUN()函数忽略列值为NULL的行
9.2 聚集不同值
1. 以上5个聚集函数都可以如下使用:
1. 对所有行执行计算,指定ALL参数或不指定参数(因为ALL是默认行为)
2. 只包含不同的值,指定DISTINCT参数
ALL为默认:ALL参数不需要指定,因为它是默认行为。如果不指定DISTINCT,则假定为ALL
DISTINCT不能用于COUNT():如果指定列名,则DISTINCT只能用于COUNT(),DSITINCT不能用于COUNT()。类似的,DISTINCT必须使用列名,不能用于计算或表达式
将DISTINCT用于MIN()和MAX():虽然DISTINCT从技术上可用于MIN()和MAX(),但这样做实际上没有价值。一个列中的最小值和最大值不管是否只考虑不同值,结果都是相同的。
9.3 组合聚集函数
1. 聚集函数可以多个一起使用。
取别名:在指定别名以包含某个聚集函数的结果时,不应该使用表中实际的列名。虽然这样做也算合法,但许多SQL实现不支持,可能会产生模糊的错误消息