[SQL必知必会] 读书笔记

第1课 数据库

这一课介绍SQL究竟是什么,它能做什么事情。

 

1.1 数据库基础

下面是一些数据库概念的简要介绍,如果你刚开始接触数据库,可以由此了解必需的基本知识。

 

1.1.1 数据库

数据库这个术语的用法很多,但就本书而言(从SQL的角度来看),数据库是一个以某种有组织的方式存储的数据集合。最简单的办法是将数据库想象为一个文件柜。这个文件柜是一个存放数据的物理位置,不管数据是什么,也不管数据是如何组织的。 

数据库(database) 保存有组织的数据的容器(通常是一个文件或一组文件)。

 

注意:误用导致馄淆 人们通常用数据库这个术语来代表他们使用的数据库软件,这是不正确的,也因此产生了许多混淆。确切地说,数据库软件应称为数据库管理系统(即DBMS)。数据库是通过DBMS创建和操纵的容器,而具体它宄竟是什么,形式如何,各种数据库都不一样。

 

1.1.2 表

你往文件柜里放资料时,并不是随便将它们扔进某个抽屉就完事了,而是在文件柜中创建文件,然后将相关的资料放入特定的文件中。在数据库领域中,这种文件称为表。表是一种结构化的文件,可用来存储某种特定类型的数据。表可以保存顾客清单、产品目录,或者其他信息清单。

表(table) 某种特定类型数据的结构化清单。

 

1.1.3 列和数据类型

表由列组成。列存储表中某部分的信息。理解列的最好办法是将数据库表想象为一个网格,就像个电子表格那样。网格中每一列存储着某种特定的信息。例如,在顾客表中,一列存储顾客编号,另一列存储顾客姓名。

列 Ccolumn) 表中的一个字段。所有表都是由一个或多个列组成的。

数据库中每个列都有相应的数据类型。数据类型(datatype)定义了列可以存储哪些数据种类。例如,如果列中存储的是数字(或许是订单中的物品数),则相应的数据类型应该为数值类型。如果列中存储的是日期、文本、注释、金额等,则应该规定好恰当的数据类型。

数据类型 所允许的数据的类型。每个表列都有相应的数据类型,它限制(或允许)该列中存储的数据。

 

1.1.4 行

表中的数据是按行存储的,所保存的每个记录存储在自己的行内。如果将表想象为网格,网格中垂直的列为表列,水平行为表行。例如,顾客表可以每行存储一个顾客。表中的行编号为记录的编号。

行(row) 表中的一个记录。

 

1.1.5 主键

表中每一行都应该有一列(或几列)可以唯一标识自己,称为主键列,主键列不允许NULL值。例如,顾客表可以使用顾客编号,而订单表可以使用订单编号。

主键(primary key) 一列(或一组列),其值能够唯一标识表中每一行。


1.2 什么是SQL

SQL (发音为字母S-Q-L或sequel)是Structured Query Language(结构化查询语言)的缩写。SQL是一种专门用来与数据库沟通的语言。

与其他语言(如英语或Java、C、PHP这样的编程语言)不一样,SQL中只有很少的词,这是有意而为的。设计SQL的目的是很好地完成一项任务 一 提供一种从数据库中读写数据的简单有效的方法。

SQL有如下的优点。

  • SQL不是某个特定数据库供应商专有的语言。几乎所有重要的DBMS都支持SQL,所以学习此语言使你几乎能与所有数据库打交道。
  • SQL简单易学。它的语句全都是由有很强描述性的英语单词组成,而且这些单词的数目不多。
  • SQL虽然看上去很简单,但实际上是一种强有力的语言,灵活使用其语言元素,可以进行非常复杂和高级的数据库操作。

 

1.3 动手实践

与其他任何语言一样,学习SQL的最好方法是自己动手实践。为此,需要一个数据库和用来测试SQL语句的应用系统。

附录A给出了具体的样例表,并介绍了获得(或创建)它们的详细步骤。附录B介绍在各种应用程序中执行SQL所需的步骤。这里给出下载样例表的地址:http://forta.com/books/0672336073/。

(笔者使用的是MySQL5.6,所以下面的语句若不做介绍,皆默认使用MySQL版本的)


第2课 检索数据

这一课介绍如何使用SELECT语句从表中检索一个或多个数据列。

 

2.1 SELECT 语句

正如第1课所述,SQL语句是由简单的英语单词构成的。这些单词称为关键字,每个SQL语句都是由一个或多个关键字构成的。最经常使用的SQL语句大概就是SELECT语句了。它的用途是从一个或多个表中检索信息。

关键字(keyword) 作为SQL组成部分的保留字。关键字不能用作表或列的名字。附录E列出了某些经常使用的保留字。

为了使用SELECT检索表数据,必须至少给出两条信息一想选择什么,以及从什么地方选择。

 

2.2 检索单个列

我们将从简单的SQL SELECT语句讲起,此语句如下所示:

输入▼


SELECT prod_name FROM Products;


分析▼


上述语句利用SELECT语句从Products表中检索一个名为prod_name的列。所需的列名写在SELECT关键字之后,FROM关键字指出从哪个表中检索数据。

提示:SQL语句和大小写 请注意,SQL语句不区分大小写,因此SELECT与select是相同的。同样,写成Select也没有关系。许多SQL开发人员喜欢对SQL关键字使用大写,而对列名和表名使用小写,这样做使代码更易于阅读和调试。不过,一定要认识到虽然SQL是不区分大小写的,但是表名、列名和值可能有所不同(这有赖于具体的DBMS及其如何配置)。 提示:使用空格 在处理SQL语句时,其中所有空格都被忽略。SQL语句可以写成长长的一行,也可以分写在多行。多数SQL开发人员认为,将SQL语句分成多行更容易阅读和调试。

 

2.3 检索多个行

要想从一个表中检索多个列,仍然使用相同的SELECT语句。唯一的不同是必须在SELECT关键字后给出多个列名,列名之间必须以逗号分隔。

输入▼


SELECT prod_id, prod_name, prod_price FROM Products;


分析▼


这条语句使用SELECT语句从表Products中选择多个数据。在这个例子中,指定了3个列名,列名之间用逗号分隔。

 

2.4 检索所有列

除了指定所需的列外(如上所述,一个或多个列),SELECT语句还可以检索所有的列而不必逐个列出它们。在实际列名的位置使用星号(*)通配符可以做到这点,如下所示。

输入▼


SELECT * FROM Products;


分析▼


如果给定一个通配符(*),则返回表中所有列。列的顺序一般是列在表定义中出现的物理顺序,但并不总是如此。

 

2.5 检索不同的值

如前所述,SELECT语句返回所有匹配的行。但是,如果你不希望每个值每次都出现,该怎么办呢?办法就是使用DISTINCT关键字,顾名思义,它指示数据库只返回不同的值。

输入▼


SELECT DISTINCT vend_id FROM Products;


分析▼


SELECT DISTINCT vend_id告诉DBMS只返回不同(具有唯一性)的vend_id行。如果使用DISTINCT关键字,它必须直接放在列名的前面。

警告:不能部分使用DISTINCT DISTINCT关键字作用于所有的列,不仅仅是跟在其后的那一列。例如,你指定SELECT DISTINCT vend_id,prod_price,除非指定的两列完全相同,否则所有的行都会被检索出来。

 

2.6 限制结果

SELECT语句返回指定表中所有匹配的行,很可能是每一行。如果你只想返回第一行或者一定数量的行,该怎么办呢?这是可行的,然而遗憾的是,各种数据库中的这一 SQL实现并不相同。

如果你使用MySQL、MariaDB、PostgreSQL或者SQLite,需要使用LIMIT子句,像这样:

输入▼


SELECT prod_name FROM Products LIMIT 5;


分析▼


上述代码使用SELECT语句来检索单独的一列数据。LIMIT 5指示MySQL等DBMS返回不超过5行的数据。

为了得到后面的5行数据,需要指定从哪儿开始以及检索的行数,像这样:

输入▼


SELECT prod_name FROM Products LIMIT 5 OFFSET 5;


分析▼


LIMIT 5 OFFSET 5指示MySQL等DBMS返回从第5行起的5行数据。第一个数字是指从哪儿开始,第二个数字是检索的行数。

警告:第0行 第一个被检索的行是第0行,而不是第1行。因此,LIMIT 1 OFFSET 1会检索第2行,而不是第1行。 提示:MySQL和MariaDB快捷键 MySQL和MariaDB支持简化版的LIMIT 4 OFFSET 3语句,即LIMIT 3,4。使用这个语法,之前的值对应LIMIT,之后的值对应OFFSET。

 

2.7 使用注释

我们先来看行内注释:

输入▼


SELECT prod_name FROM Products; --这是一条注释


分析▼


注释使用--(两个连字符)嵌在行内。--之后的文本就是注释。

你也可以进行多行注释,注释可以在脚本的任何位置停止和开始。

输入▼


/* SELECT prod_name, vend_id

FROM Products; */

SELECT prod_name FROM Products;


分析▼


注释从/*开始,到*/结束,/*和*/之间的任何内容都是注释。这种方式常用于给代码加注释,就如这个例子演示的。


第3课 排序检索数据

这一课讲授如何使用SELECT语句的ORDER BY子句,根据需要排序检索出的数据。

 

3.1 排序数据

为了明确地排序用SELECT语句检索出的数据,可使用ORDER BY子句。ORDER BY子句取一个或多个列的名字,据此对输出进行排序。请看下面的例子:

输入▼


SELECT prod_name FROM Products
ORDER BY prod_name;


分析▼


ORDER BY子句,指示DBMS软件对prod_name列以字母顺序排序数据。

注意:ORDER BY子句的位置 在指定一条ORDER BY子句时,应该保证它是SELECT语句中最后一条子句。如果它不是最后的子句,将会出现错误消息。

 

3.2 按多个列排序

下面的代码检索3个列,并按其中两个列对结果进行排序 一 首先按价格,然后按名称排序。

输入▼


SELECT prod_id, prod_price, prod_name FROM Products
ORDER BY prod_price, prod_name;


分析▼


对于上述例子中的输出,仅在多个行具有相同的prod_price值时才对产品按prod_name进行排序。如果prod_price列中所有的值都是唯一的,则不会按prod_name排序。(先对prod_price排序,再对prod_name排序)

 

3.3 按列位置排序

除了能用列名指出排序顺序外,ORDER BY还支持按相对列位置进行排序。为理解这一内容,我们来看个例子:

输入▼


SELECT prod_id, prod_price, prod_name FROM Products
ORDER BY 2, 3;


分析▼


SELECT清单中指定的是选择列的相对位置而不是列名。ORDER BY 2 表示按SELECT清单中的第二个列prod_name进行排序。ORDER BY 2, 3 表示先按prod_price,再按prod_name进行排序。

 

3.4 指定排序方向

数据排序不限于升序排序(从A到Z),这只是默认的排序顺序。还可以使用ORDER BY子句进行降序(从Z到A)排序。为了进行降序排序,必须 指定DESC关键字。例子如下:

输入▼


SELECT prod_id, prod_price, prod_name FROM Products
ORDER BY prod_price DESC, prod_name;


分析▼


DESC关键字只应用到直接位于其前面的列名。在上例中,只对prod_price列指定DESC,对prod_name列不指定。因此,prod_price列以降序排序,而prod_name列(在每个价格内)仍然按标准的升序排序。

警告:在多个列上降序排序 如果想在多个列上进行降序排序,必须对每一列指定DESC关键字。


第4课 过滤数据

这一课将讲授如何使用SELECT语句的WHERE子句指定搜索条件。

 

4.1 使用WHERE子句

在SELECT语句中,数据根据WHERE子句中指定的搜索条件进行过滤。WHERE子句在表名(FROM子句)之后给出,如下所示:

输入▼


SELECT prod_name, prod_price FROM Products

WHERE prod_price = 3.49;


分析▼


这条语句从products表中检索两个列,但不返回所有行,只返回prod_price值为3.49的行。

 

4.2 WHERE子句操作符

SQL支持下表列出的所有条件操作符。

操作符说明
=等于
!=不等于
<小于
<=小于等于
>大于
>=大于等于
BETWEEN在指定的两个值之间
IS NULL为NULL值

 

4.2.1 范围内检查

要检查某个范围的值,可以使用BETWEEN操作符。其语法与其他WHERE子句的操作符稍有不同,因为它需要两个值,即范围的开始值和结束值。下面的例子说明如何使用BETWEEN操作符,它检索价格在5美元和10美元之间的所有产品:

输入▼


SELECT prod_name, prod_price FROM Products
WHERE prod_price BETWEEN 5 AND 10;


分析▼


从这个例子可以看到,在使用BETWEEN时,必须指定两个值——所需范围的低端值和高端值。这两个值必须用AND关键字分隔。

 

4.2.2 空值检查

确定值是否为NULL,不能简单地检查是否=NULL。SELECT语句有一个特殊的WHERE子句,可用来检查具有NULL值的列。这个WHERE子句就是IS NULL子句。其语法如下:

输入▼


SELECT prod_name FROM Products

WHERE prod_price IS NULL;


分析▼


这条语句返回所有没有价格(空prod_price字段,不是价格为0)的产品。


第5课 高级数据过滤

这一课讲授如何组合WHERE子句以建立功能更强、更高级的搜索条件。我们还将学习如何使用NOT和IN操作符。

 

5.1 组合WHERE子句

第4课介绍的所有WHERE子句在过滤数据时使用的都是单一的条件。为了进行更强的过滤控制,SQL允许给出多个WHERE子句。这些子句有两种使用方式,即以AND子句或OR子句的方式使用。

 

5.1.1 AND操作符

要通过不止一个列进行过滤,可以使用AND操作符给WHERE子句附加条件。下面的代码给出了一个例子:

输入▼


SELECT prod_id, prod_price, prod_name FROM Products

WHERE vend_id = 'DLL01' AND prod_price <= 4;


分析▼


此SQL语句检索由供应商DLL01制造且价格小于等于4美元的所有产品的名称和价格。

 

5.1.2 OR操作符

OR操作符与AND操作符正好相反,它指示DBMS检索匹配任一条件的行。事实上,许多DBMS在OR WHERE子句的第一个条件得到满足的情况下, 就不再计算第二个条件了。例子如下:

输入▼


SELECT prod_name, prod_price FROM Products

WHERE vend_id = 'DLL01' OR vend_id = 'BRS01';


分析▼


此SQL语句检索由任一个指定供应商制造的所有产品的产品名和价格。

 

5.2 IN操作符

IN操作符用来指定条件范围,范围中的每个条件都可以进行匹配。IN取一组由逗号分隔、括在圆括号中的合法值。下面的例子说明了这个操作符:

输入▼


SELECT prod_name, prod_price FROM Products

WHERE vend_id IN ( 'DLL01', 'BRS01' );


分析▼


此SELECT语句检索由供应商DLL01和BRS01制造的所有产品。IN操作符后跟由逗号分隔的合法值,这些值必须括在圆括号中。 你可能会猜测IN操作符完成了与OR相同的功能,恭喜你猜对了!

 

5.3 NOT操作符

WHERE子句中的NOT操作符有且只有一个功能,那就是否定其后所跟的任何条件。NOT关键字可以用在要过滤的列前,而不仅是在其后。下面的例子说明了这个操作符:

输入▼


SELECT prod_name FROM Products

WHERE NOT vend_id = 'DLL01';


分析▼


这里的NOT否定跟在其后的条件,因此,DBMS不是匹配vend_id为DLL01,而是匹配非DLL01之外的所有东西。


第6课 用通配符进行过滤

这一课介绍什么是通配符、如何使用通配符以及怎样使用LIKE操作符进行通配搜索,以便对数据进行复杂过滤。

 

6.1 LIKE操作符

利用通配符,可以创建比较特定数据的搜索模式。例如,如果你想找出名称包含bean bag的所有产品,可以构造一个通配符搜索模式,找出在产品名的任何位置出现bean bag的产品。

 

6.1.1 百分号(%)通配符

最常使用的通配符是百分号(%)。在搜索串中,%表示任何字符出现任意次数。例如,为了找出所有以词Fish起头的产品,可发布以下SELECT语句:

输入▼


SELECT prod_id, prod_name FROM Products

WHERE prod_name LIKE 'Fish%';


分析▼


此例子使用了搜索模式'Fish%’。在执行这条子句时,将检索任意以Fish起头的词。%告诉DBMS接受Fish之后的任意字符,不管它有多少字符。

 

6.1.2 下划线(_)通配符

另一个有用的通配符是下划线(_)。下划线的用途与%—样,但它只匹配单个字符,而不是多个字符。例子如下:

输入▼


SELECT prod_id,prod_name FROM Products

WHERE prod_name LIKE '__ inch teddy bear';


分析▼


这个WHERE子句中的搜索模式给出了后面跟有文本的两个通配符。

 

6.1.3 方括号([])通配符

方括号([])通配符用来指定一个字符集,它必须匹配指定位置(通配符的位置)的一个字符。例如,找出所有名字以J或M起头的联系人,可进行如下查询:

输入▼


SELECT cust_contact FROM Customers

WHERE cust_contact LIKE '[JM]%';


分析▼


这一搜索模式使用了两个不同的通配符。[JM]匹配任何以方括号中字母开头的联系人名,它也只能匹配单个字符。[JM]之后的%通配符匹配第一个字符之后的任意数目的字符,返回所需结果。


第7课 创建计算字段

这一课介绍什么是计算字段,如何创建计算字段,以及如何从应用程序中使用别名引用它们。

 

7.1 计算字段

存储在数据库表中的数据一般不是应用程序所需要的格式,下面举几个例子。

  • 需要显示公司名,同时还需要显示公司的地址,但这两个信息存储在不同的表列中。
  • 物品订单表存储物品的价格和数量,不存储每个物品的总价格(用价格乘以数量即可)。但为打印发票,需要物品的总价格。
  • 需要根据表数据进行诸如总数、平均数的计算。

在上述每个例子中,存储在表中的数据都不是应用程序所需要的。我们需要直接从数据库中检索出转换、计算或格式化过的数据,而不是检索出数据,然后再在客户端应用程序中重新格式化。

这就是计算字段可以派上用场的地方了。与前几课介绍的列不同,计算字段并不实际存在于数据库表中。计算字段是运行时在SELECT语句内创建的。

 

7.2 拼接字段

为了说明如何使用计算字段,我们来举一个简单例子,创建由两列组成的标题。在SQL中的SELECT语句中,可使用一个特殊的操作符来拼接两个列。根据你所使用的DBMS,此操作符可用加号(+)或两个竖杠(II)表示。在MySQL和MariaDB中,必须使用特殊的函数。

下面是MySQL和MariaDB中使用的语句:

输入▼


SELECT Concat(vend_name, ' ', vend_country) FROM Vendors;


分析▼


使用Concat函数拼接vend_name,一个空格' '和vend_country。

 

使用别名

SELECT语句可以很好地拼接地址字段,但这个新计算列的名字是什么呢?实际上它没有名字,它只是一个值。如 果仅在SQL查询工具中查看一下结果,这样没有什么不好。但是,一个未命名的列不能用于客户端应用中,因为客户端没有办法引用它。

为了解决这个问题,SQL支持列别名。别名(alias)是一个字段或值的替换名。别名用AS关键字赋予。请看下面的SELECT语句:

输入▼


SELECT Concat(vend_name, ' ', vend_country) AS vend_title FROM Vendors;


分析▼


这里的计算字段之后跟了文本AS vend_title,它指示SQL创建一个包含指定计算结果的名为vend_title的计算字段。现在列名为vend_title,任何客户端应用都可以按名称引用这个列,就像它是一个实际的表列一样。

 

7.3 执行算数计算

计算字段的另一常见用途是对检索出的数据进行算术计算。例如,可以汇总2008年这一年订单物品的价格(单价乘以订购数量):

输入▼


SELECT prod_id, quantity, item_price,quantity*item_price AS expanded_price

FROM OrderItems WHERE order_num = 20008;


分析▼


这里的计算字段之后跟了文本AS vend_title,它指示SQL创建一个包含指定计算结果的名为vend_title的计算字段。现在列名为vend_title,任何客户端应用都可以按名称引用这个列,就像它是一个实际的表列一样。

SQL支持基本的加减乘除这4种算术操作符。


第8课 使用数据处理函数

这一课介绍什么是函数,DBMS支持何种函数,以及如何使用这些函数;还将讲解为什么SQL函数的使用可能会带来问题。

8.1 函数

与大多数其他计算机语言一样,SQL也可以用函数来处理数据。函数一般是在数据上执行的,为数据的转换和处理提供了方便。

函数带来的问题

与几乎所有DBMS都等同地支持SQL语句(如SELECT)不同,每一个DBMS都有特定的函数。事实上,只有少数几个函数被所有主要的DBMS等同地支持。虽然所有类型的函数一般都可以在每个DBMS中使用,但各个函数的名称和语法可能极其不同。这就表示为特定SQL实现编写的代码在其他实现中可能不正常。

 

8.2 使用函数

大多数SQL实现支持以下类型的函数。

  • 用于处理文本字符串(如删除或填充值,转换值为大写或小写)的文本函数。
  • 用于在数值数据上进行算术操作(如返回绝对值,进行代数运算)的数值函数。
  • 用于处理日期和时间值并从这些值中提取特定成分(如返回两个日期之差,检查日期有效性)的日期和时间函数。
  • 返回DBMS正使用的特殊信息(如返回用户登录信息)的系统函数。

 

8.2.1 文本处理函数

下面例子使用的是UPPER()函数:

输入▼


SELECT vend_name, UPPER(vend_name) AS vend_name_upcase FROM Vendors;


分析▼


UPPER()将文本转换为大写。

下表列出了一些常用的文本处理函数。

函 数说 明
LEFT()(或使用子字符串函数)返回字符串左边的字符
LENGTH() (也使用 DATALENGTH()或LEN())返回字符串的长度
LOWER() (Access使用 LCASE())将字符串转换为小写
LTRIM()去掉字符串左边的空格
RIGHT() (或使用子字符串函数)返回字符串右边的字符
RTRIM()去掉字符串右边的空格
SOUNDEX()返回字符串的SOUNDEX值
UPPER() (Access使用UCASE())将字符串转换为大写

 

8.2.2 日期和时间处理函数

MySQL和MariaDB用户可使用名为YEAR()的函数从日期中提取年份:

输入▼


SELECT order_num FROM Orders

WHERE YEAR(order_date) = 2012;


分析▼


DBMS提供的功能远不止简单的日期成分提取。大多数DBMS具有比较日期、执行基于日期的运算、选择日期格式等的函数。但是,可以看到,不同DBMS的日期-时间处理函数可能不同。关于具体DBMS支持的日期-时间处理函数,请参阅相应的文档。

 

8.2.3 数值处理函数

数值处理函数仅处理数值数据。这些函数一般主要用于代数、三角或几何运算,因此不像字符串或日期-时间处理函数使用那么频繁。具有讽刺意味的是,在主要DBMS的函数中,数值函数是最一致、最统一的函数。下表列出了一些常用的数值处理函数。

函 数说 明
ABS()返回一个数的绝对值
COS()返回一个角度的余弦
EXP()返回一个数的指数值
PI()返回圆周率
SIN()返回一个角度的正弦
SQRT()返回一个数的平方根
TAN()返回一个角度的正切


第9课 汇总数据

这一课介绍什么是SQL的聚集函数,如何利用它们汇总表的数据。

 

9.1 聚集函数

我们经常需要汇总数据而不用把它们实际检索出来,为此SQL提供了专门的函数。使用这些函数,SQL查询可用于检索数据,以便分析和报表生成。这种类型的检索例子有:

  • 确定表中行数(或者满足某个条件或包含某个特定值的行数);
  • 获得表中某些行的和;
  • 找出表列(或所有行或某些特定的行)的最大值、最小值、平均值。

为方便这种类型的检索,SQL给出了5个聚集函数,见下表。这些函数能进行上述检索。与前一章介绍的数据处理函数不同,SQL的聚集函数在各种主要SQL实现中得到了相当一致的支持。

函 数说 明
AVG()返回某列的平均值
COUNT()返回某列的行数
MAX()返回某列的最大值
MIN()返回某列的最小值
SUM()返回某列值之和

 

9.1.1 AVG()函数

AVG()通过对表中行数计数并计算其列值之和,求得该列的平均值。下面的例子使用AVG()返回Products表中所有产品的平均价格:

输入▼


SELECT AVG(prod_price) AS avg_price FROM Products

WHERE vend_id = 'DLL01';


分析▼


此SELECT语句返回值avg_price,它包含Products表中所有产品的平均价格。

 

9.1.2 COUNT()函数

C0UNT()函数进行计数。可利用C0UNT()确定表中行的数目或符合特定条件的行的数目。C0UNT()函数有两种使用方式:

  • 使用C0UNT()对表中行的数目进行计数,不管表列中包含的是空值(NULL)还是非空值。
  • 使用COUNT(column)对特定列中具有值的行进行计数,忽略NULL值。

输入▼


SELECT COUNT(*) AS num_cust FROM Customers;


分析▼


在此例子中,利用C0UNT(*)对所有行计数,不管行中各列有什么值。计数值在num_cust中返回。

 

9.2 聚集不同值

以上5个聚集函数都可以如下使用:

  • 对所有行执行计算,指定ALL参数或不指定参数(因为ALL是默认行为)。
  • 只包含不同的值,指定DISTINCT参数。

下面的例子使用AVG()函数返回特定供应商提供的产品的平均价格。它与上面的SELECT语句相同,但使用了DISTINCT参数,因此平均值只考虑各个不同的价格:

输入▼


SELECT AVG(DISTINCT prod_price) AS avg_price FROM Products

WHERE vend_id = 'DLL01';


分析▼


使用了DISTINCT后,会排除相同的价格。

 

9.3 组合聚集函数

目前为止的所有聚集函数例子都只涉及单个函数。但实际上,SELECT语句可根据需要包含多个聚集函数。请看下面的例子:

输入▼


SELECT COUNT(*) AS num_items,MIN(prod_price) AS price_min,

MAX(prod_price) AS price_max, AVG(prod_price) AS price_avg FROM Products;


分析▼


这里用单条SELECT语句执行了4个聚集计算,返回4个值(Products表中物品的数目,产品价格的最高值、最低值以及平均值)。


第10课 分组数据

这一课介绍如何分组数据,以便汇总表内容的子集。这涉及两个新SELECT语句子句:GROUP BY子句和HAVING子句。

 

10.1 数据分组

如果要返回每个供应商提供的产品数目,该怎么办?或者返回只提供一项产品的供应商的产品,或者返回提供10个以上产品的供应商的产品, 怎么办?这就是分组大显身手的时候了。使用分组可以将数据分为多个逻辑组,对每个组进行聚集计算。

 

10.2 创建分组

分组是使用SELECT语句的GROUP BY子句建立的。理解分组的最好办法是看一个例子:

输入▼


SELECT vend_id,COUNT(*) AS num_prods FROM Products GROUP BY vend_id;


输出▼


+---------+-----------+
| vend_id | num_prods |
+---------+-----------+
| BRS01 | 3 |
| DLL01 | 4 |
| FNG01 | 2 |
+---------+-----------+

分析▼


上面的SELECT语句指定了两个列:vend_id包含产品供应商的ID,num_prods为计算字段,GROUP BY子句指示DBMS 按vend_id排序并分组数据。这就会对每个vend_id而不是整个表计算num_prods—次。

 

10.3 过滤分组

除了能用GROUP BY分组数据外,SQL还允许过滤分组,规定包括哪些分组,排除哪些分组。例如,你可能想要列出至少有两个订单的所有顾客。

SQL为此提供了另一个子句,就是HAVING子句。HAVING非常类似于WHERE。事实上,目前为止所学过的所有类
型的WHERE子句都可以用HAVING来替代。唯一的差别是,WHERE过滤行,而HAVING过滤分组。

输入▼


SELECT cust_id,COUNT(*) AS orders FROM Orders

GROUP BY cust_id HAVING COUNT(*) >= 2;


分析▼


这条SELECT语句的前三行类似于上面的语句。最后一行增加了HAVING子句,它过滤COUNT(*) >= 2(两个以上订单)的那些分组。


第11课 使用子查询

这一课介绍什么是子查询,如何使用它们。

 

11.1 子查询

SQL还允许创建子查询(subquery),即嵌套在其他查询中的查询。为什么要这样做呢?理解这个概念的最好方法是考察几个例子。

 

11.2 利用子查询进行过滤

订单存储在两个表中。每个订单包含订单编号、客户 ID、订单日期,在Orders表中存储为一行。各订单的物品存储在相关的Orderltems表中。Orders表不存储顾客信息,只存储顾客ID。顾客的实际信息存储在Customers表中。

现在,假如需要列出订购物品RGAN01的所有顾客,应该怎样检索?下面列出具体的步骤。

  1. 检索包含物品RGAN01的所有订单的编号。
  2. 检索具有前一步骤列出的订单编号的所有顾客的ID。
  3. 检索前一步骤返回的所有顾客ID的顾客信息。

上述每个步骤都可以单独作为一个查询来执行。可以把一条SELECT语句返回的结果用于另一条SELECT语句的WHERE子句。也可以使用子查询来把3个查询组合成一条语句。使用子查询的语句如下所示:

输入▼


SELECT cust_name, cust_contact FROM Customers

WHERE cust_id IN (SELECT cust_id FROM Orders WHERE order_num IN

(SELECT order_num FROM OrderItems WHERE prod_id = 'RGAN01'));


分析▼


为了执行上述SELECT语句,DBMS实际上必须执行三条SELECT语句。最里边的子查询返回订单号列表,此列表用于其外面的子查询的WHERE子句。外面的子查询返回顾客ID列表,此顾客ID列表用于最外层查询的WHERE子句。最外层查询返回所需的数据。


第12课 联结表

这一课会介绍什么是联结,为什么使用联结,如何编写使用联结的SELECT语句。

 

12.1 联结

SQL最强大的功能之一就是能在数据查询的执行中联结(join)表。联结是利用SQL的SELECT能执行的最重要的操作,很好地理解联结及其语法是学习SQL的极为重要的部分。

 

12.1.1 关系表

有一个包含产品目录的数据库表,其中每类物品占一行。对于每一种物品,要存储的信息包括产品描述、价格,以及生产该产品的供应商。

现在有同一供应商生产的多种物品,那么在何处存储供应商名、地址、联系方法等供应商信息呢?将这些数据与产品信息分开存储的理由是:

  • 同一供应商生产的每个产品,其供应商信息都是相同的,对每个产品重复此信息既浪费时间又浪费存储空间;
  • 如果供应商信息发生变化,例如供应商迁址或电话号码变动,只需修改一次即可;
  • 如果有重复数据(即每种产品都存储供应商信息),则很难保证每次输入该数据的方式都相同。不一致的数据在报表中就很难利用。

关系表的设计就是要把信息分解成多个表,一类数据一个表。各表通过某些共同的值互相关联(所以才叫关系数据库)。

在这个例子中可建立两个表:一个存储供应商信息,另一个存储产品信息。Vendors表包含所有供应商信息,每个供应商占一行,具有唯一的标识。

Products表只存储产品信息,除了存储供应商ID (Vendors表的主键)外,它不存储其他有关供应商的信息。Vendors表的主键将Vendors表 与Products表关联,利用供应商ID能从Vendors表中找出相应供应商的详细信息。

 

12.1.2 为什么使用联结

如前所述,将数据分解为多个表能更有效地存储,更方便地处理,并且可伸缩性更好。但这些好处是有代价的。如果数据存储在多个表中,怎样用一条SELECT语句就检索出数据呢?

答案是使用联结。简单说,联结是一种机制,用来在一条SELECT语句中关联表,因此称为联结。使用特殊的语法,可以联结多个表返回一组输出,联结在运行时关联表中正确的行。

 

12.2 创建联结

创建联结非常简单,指定要联结的所有表以及关联它们的方式即可。请看下面的例子:

输入▼


SELECT vend_name, prod_name, prod_price FROM Vendors, Products

WHERE Vendors.vend_id = Products.vend_id;


分析▼


SELECT语句与前面所有语句一样指定要检索的列。这里最大的差别是所指定的两列(prod_name和prod_price)在一个表中,而第三列(vend_name)在另一个表中。

这条语句的FROM子句列出了两个表:Vendors和Products。它们就是这条SELECT语句联结的两个表的名字。这两个表用WHERE子句正确地联结,WHERE子句指示DBMS将Vendors表中的vend_id与Products表中的vend_id匹配起来。

可以看到,要匹配的两列指定为Vendors.vend_id和Products.vend_id。这里需要这种完全限定列名,如果只给出vend_id,DBMS就不知道指的是哪一个(每个表中有一个)。

 

12.3 联结多个表

SQL不限制一条SELECT语句中可以联结的表的数目。创建联结的基本规则也相同。首先列出所有表,然后定义表之间的关系。例如:

输入▼


SELECT prod_name,vend_name, prod_price, quantity FROM OrderItems, Products, Vendors

WHERE Products.vend_id = Vendors.vend_id AND OrderItems.prod_id = Products.prod_id

AND order_num = 20007;


分析▼


这个例子显示订单20007中的物品。订单物品存储在OrderItems表中。每个产品按其产品ID存储,它引用Products表中的产品。这些产品通过供应商ID联结到Vendors表中相应的供应商,供应商ID存储在每个产品的记录中。这里的FROM子句列出三个表,WHERE子句定义这两个联结条件, 而第三个联结条件用来过滤出订单20007中的物品。


第13课 创建高级联结

本课讲解另外一些联结(包括它们的含义和使用方法),介绍如何使用表别名,如何对被联结的表使用聚集函数。

 

13.1 使用表别名

请看下面的SELECT语句。它与前一课例子中所用的语句基本相同,但改成了使用表别名:

输入▼


SELECT cust_name, cust_contact FROM Customers AS C, Orders AS O, OrderItems AS OI

WHERE C.cust_id = O.cust_id AND OI.order_num = O.order_num AND prod_id = 'RGAN01';


分析▼


可以看到,FROM子句中的三个表全都有别名。Customers AS C使用C作为Customers的别名,如此等等。这样,就可以使用省略的C而不用全名Customers。在这个例子中,表别名只用于WHERE子句。其实它不仅能用于WHERE子句,还可以用于SELECT的列表、ORDER BY子句以及其他语句部分。

 

13.2 使用不同类型的联结

迄今为止,我们使用的只是内联结或等值联结的简单联结。现在来看三种其他联结:自联结(self-join)、自然联结(natura join)和外联结(outer join)。由于篇幅原因,这里不再过多介绍。


第14课 组合查询

本课讲述如何利用UNION操作符将多条SELECT语句组合成一个结果集。

 

14.1 组合查询

多数SQL查询只包含从一个或多个表中返回数据的单条SELECT语句。但是,SQL也允许执行多个查询(多条SELECT语句),并将结果作为一个查询结果集返回。这些组合查询通常称为并(union)或复合查询(compound query)。

主要有两种情况需要使用组合查询:

  • 在一个查询中从不同的表返回结构数据;
  • 对一个表执行多个查询,按一个查询返回数据。

 

14.2 创建组合查询

可用UNION操作符来组合数条SQL查询。利用UNION,可给出多条SELECT语句,将它们的结果组合成一个结果集。如下所示:

输入▼


SELECT cust_name, cust_contact, cust_email FROM Customers

WHERE cust_state IN ('IL', 'IN', 'MI')

UNION

SELECT cust_name,cust_contact,cust_email FROM Customers

WHERE cust_name = 'Fun4All';


分析▼


这条语句由前面的两条SELECT语句组成,之间用UNION关键字分隔。UNION指示DBMS执行这两条SELECT语句,并把输出组合成一个查询结果集。


第15课 插入数据

这一课介绍如何利用SQL的INSERT语句将数据插入表中。

 

15.1 数据插入

把数据插入表中的最简单方法是使用基本的INSERT语法,它要求指定表名和插入到新行中的值。下面举一个例子:

输入▼


INSERT INTO Customers VALUES('1000000006', 'Toy Land', '123 Any Street', 'New York',

'NY', '11111', 'USA', NULL, NULL);


分析▼


这个例子将一个新顾客插入到Customers表中。存储到表中每一列的数据在VALUES子句中给出,必须给每一列提供一个值。如果某列没有值,如上面的cust_contact和cust_email列,则应该使用NULL值(假定表允许对该列指定空值)。各列必须以它们在表定义中出现的次序填充。

虽然这种语法很简单,但并不安全,应该尽量避免使用。上面的SQL语句高度依赖于表中列的定义次序,还依赖于其容易获得的次序信息。即使可以得到这种次序信息,也不能保证各列在下一次表结构变动后保持完全相同的次序。因此,编写依赖于特定列次序的SQL语句是很不安全的,这样做迟早会出问题。

编写INSERT语句的更安全(不过更烦琐)的方法如下:

输入▼


INSERT INTO Customers(cust_id, cust_name, cust_address, cust_city, cust_state, cust_zip, cust_country,

cust_contact, cust_email)

VALUES('1000000006', 'Toy Land', '123 Any Street', 'New York', 'NY', '11111', 'USA', NULL, NULL);


分析▼


这个例子与前一个INSERT语句的工作完全相同,但在表名后的括号里明确给出了列名。在插入行时,DBMS将用VALUES列表中的相应值填入列表中的对应项。因为提供了列名,VALUES必须以其指定的次序匹配指定的列名,不一定按各列出现在表中的实际次序。其优点是,即使表的结构改变,这条INSERT语句仍然能正确工作。

 

15.2 从一个表复制到另一个表

要将一个表的内容复制到一个全新的表(运行中创建的表),可以使用SELECT INTO语句。MariaDB、MySQL、Oracle、PostgreSQL和SQLite使用的语法如下:

输入▼


CREATE TABLE CustCopy AS SELECT * FROM Customers;


分析▼


这条SELECT语句创建一个名为CustCopy的新表,并把Customers表的整个内容复制到新表中。因为这里使用的是SELECT *,所以将在CustCopy表中创建(并填充)与Customers表的每一列相同的列。要想只复制部分的列,可以明确给出列名,而不是使用*通配符。


第16课 更新和删除数据

这一课介绍如何利用UPDATE和DELETE语句进一步操作表数据。

 

16.1 更新数据

更新(修改)表中的数据,可以使用UPDATE语句。例子如下:

输入▼


UPDATE Customers SET cust_contact = 'Sam Roberts', cust_email = 'sam@toyland.cxx'

WHERE cust_id = '1000000006';


分析▼


UPDATE语句以WHERE子句结束,它告诉DBMS更新哪一行。没有WHERE子句,DBMS将会用这个电子邮件地址更新Customers表中的所有行,这不是我们希望的。

 

16.2 删除数据

从一个表中删除数据,使用DELETE语句。下面的语句从Customers表中删除一行:

输入▼


DELETE FROM Customers WHERE cust_id = '1000000006';


分析▼

这条语句很容易理解。DELETE FROM要求指定从中删除数据的表名,WHERE子句过滤要删除的行。在这个例子中,只删除顾客1000000006。如果省略WHERE子句,它将删除表中每个顾客。


第17课 创建和操纵表

这一课讲授创建、更改和删除表的基本知识。

 

17.1 创建表

用程序创建表,可以使用SQL的CREATE TABLE语句。下面的SQL语句创建本书中所用的Products表:

输入▼


CREATE TABLE Products(

prod id CHAR(10) NOT NULL,

vend id CHAR(10) NOT NULL,

prod name CHAR(254) NOT NULL,

prod price DECIMAL(8,2) NOT NULL DEFAULT 3,

proddesc VARCHAR(1000) NULL

);


分析▼

从上面的例子可以看到,表名紧跟CREATE TABLE关键字。实际的表定义(所有列)括在圆括号之中,各列之间用逗号分隔。有一列的描述增加了DEFAULT 3,指示DBMS,如果不给出价格则使用价格3。

 

17.2 更新表

更新表定义,可以使用ALTER TABLE语句。因为给已有表增加列可能是所有DBMS都支持的唯一操作,所以我们举个这样的例子:

输入▼


ALTER TABLE Vendors ADD vend_phone CHAR(20);


分析▼

这条语句给Vendors表增加一个名为vend_phone的列,其数据类型为CHAR。删除列,就使用DROP COLUMN col_name即可。

 

17.3 删除表

删除表(删除整个表而不是其内容)非常简单,使用DROP TABLE语句即可:

输入▼


DROP TABLE CustCopy;


分析▼

这条语句删除CustCopy表。删除表没有确认,也不能撤销,执行这条语句将永久删除该表。

使用关系规则防止意外删除 许多DBMS允许强制实施有关规则,防止删除与其他表相关联的表。在实施这些规则时,如果对某个表发布一条DROP TABLE语句,且该表是某个关系的组成部分,则DBMS将阻止这条语句执行,直到该关系被删除为止。

转载于:https://www.cnblogs.com/linuxAndMcu/p/10213378.html

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值