SQL 入门篇

前言

读《sql必知必会 第四版》随手做的笔记,写的比较乱,可读性并不好,读的是中文版,翻译过来的感觉有点怪怪的。
想要pdf的话可以留邮箱给我。

1. 使用DISTINCT关键字,它指示数据库只返回不同的值。

SELECT DISTINCT vend_id FROM Products;

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

2. LIMIT

如果你只想返回第一行或者一定数量的行

-- LIMIT 5指示MySQL等DBMS返回不超过5行的数据
SELECT prod_name FROM Products LIMIT 5;
-- LIMIT 5 OFFSET 5指示MySQL等DBMS返回从第5行起的5行数据。
SELECT prod_name FROM Products LIMIT 5 OFFSET 5;

3. 排序检索数据

1. ORDER BY,根据需要排序检索出的数据。
 SELECT prod_name FROM Products
 ORDER BY prod_name;

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

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排序。

3. 按列位置排序
除了能用列名指出排序顺序外,ORDER BY还支持按相对列位置进行排序。

```sql
-- 只对 2, 3 列进行排序
SELECT prod_id, prod_price, prod_name FROM Products
ORDER BY 2, 3;
```
4. 指定排序方向

排序默认升序,降序的话用 DESC 关键字

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

如果打算用多个列排序

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

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

DESC是DESCENDING的缩写,这两个关键字都可以使用。与DESC相对的是ASC(或ASCENDING),在升序排序时可以指定它。但实际 上,ASC没有多大用处,因为升序是默认的(如果既不指定ASC也不指定DESC,则假定为ASC)

4. 过滤数据

1. 使用WHERE子句

数据根据WHERE子句中指定的搜索条件进行过滤。WHERE子句在表名(FROM子句)之后给出。

SELECT prod_name, prod_price FROM Products
WHERE prod_price = 3.49;

警告:WHERE子句的位置
在同时使用ORDER BY和WHERE子句时,应该让ORDER BY位于WHERE之后,否则将会产生错误

2. WHERE子句操作符
操作符 说明
= 等于
<> 不等于
!= 不等于
< 小于
<= 小于等于
! 不小于
> 大于
>= 大于等于
!> 不大于
BETWEEN 在指定的两个值之间
IS NULL 为NULL值
-- 列出所有不是供应商DLL01制造的产品
SELECT vend_id, prod_name FROM Products
WHERE vend_id <> 'DLL01';
--BETWEEN操作符可用来检索价格在5美元和10美元之间的所有产品
SELECT prod_name, prod_price
FROM Products
WHERE prod_price BETWEEN 5 AND 10;
-- 检索出 prod_price 是空的字段
SELECT prod_name
FROM Products
WHERE prod_price IS NULL;

5. 高级数据过滤

NOT和IN

1. 组合WHERE子句

1.1. AND操作符

SELECT prod_id, prod_price, prod_name
FROM Products
WHERE vend_id = 'DLL01' AND prod_price <= 4;

1.2. OR操作符
OR操作符与AND操作符正好相反,它指示DBMS检索匹配任一条件的行。事实上,许多DBMS在OR WHERE子句的第一个条件得到满足的情况下,
就不再计算第二个条件了(在第一个条件满足时,不管第二个条件是否满足,相应的行都将被检索出来)

SELECT prod_name, prod_price
FROM Products
WHERE vend_id = 'DLL01' OR vend_id = ‘BRS01’;

1.3. 求值顺序
SQL(像多数语言一样)在处理OR操作符前,优先处理AND操作符。

SELECT prod_name, prod_price FROM Products
WHERE vend_id = 'DLL01' OR vend_id = ‘BRS01’ AND prod_price >= 10;

提示:在WHERE子句中使用圆括号 任何时候使用具有AND和OR操作符的WHERE子句,都应该使用圆括号明确地分组操作符。不要过分依赖默认求值顺序,即使它确实如你希望的 那样。使用圆括号没有什么坏处,它能消除歧义。

2. IN操作符

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

SELECT prod_name, prod_price
FROM Products
WHERE vend_id IN ( 'DLL01', 'BRS01' ) ORDER BY prod_name;

--类似与

SELECT prod_name, prod_price
FROM Products
WHERE vend_id = 'DLL01' OR vend_id = 'BRS01' ORDER BY prod_name;
3. NOT操作符

WHERE子句中的NOT操作符有且只有一个功能,那就是否定其后所跟的任何条件。因为NOT从不单独使用(它总是与其他操作符一起使用),所以
它的语法与其他操作符有所不同。NOT关键字可以用在要过滤的列前,而不仅是在其后。

NOT WHERE子句中用来否定其后条件的关键字。

SELECT prod_name
FROM Products
WHERE NOT vend_id = 'DLL01'
ORDER BY prod_name;
- 等同于
SELECT prod_name
FROM Products
WHERE vend_id <> 'DLL01'
ORDER BY prod_name;

6. 用通配符进行过滤

为在搜索子句中使用通配符,必须使用LIKE操作符。LIKE指 示DBMS,后跟的搜索模式利用通配符匹配而不是简单的相等匹配进行比较。
通配符搜索只能用于文本字段(串),非文本数据类型字段不能使用通配符搜索。

1. 百分号(%)通配符

%表示任何字符出现任意次数

--找出所有以词Fish起头的产品
SELECT prod_id, prod_name
FROM Products
WHERE prod_name LIKE 'Fish%';

通配符可在搜索模式中的任意位置使用,并且可以使用多个通配符。下面的例子使用两个通配符,它们位于模式的两端:

--'%bean bag%'表示匹配任何位置上包含文本bean bag的值
--不论它之前或之后出现什么字符
SELECT prod_id, prod_name
FROM Products
WHERE prod_name LIKE '%bean bag%';
2. 下划线(_)通配符

下划线的用途与%一样,但它只匹配单个字符,而不是多个字符

SELECT prod_id, prod_name
FROM Products
WHERE prod_name LIKE '__ inch teddy bear';

与%能匹配0个字符不同,_总是刚好匹配一个字符,不能多也不能少。

3. 方括号([ ])通配符

方括号([])通配符用来指定一个字符集,它必须匹配指定位置(通配符的位置)的一个字符

-- 找出所有名字以J或M起头的联系人
SELECT cust_contact
FROM Customers
WHERE cust_contact LIKE '[JM]%'
ORDER BY cust_contact;
--查询匹配不以J或M起头的任意联系人名
--如果使用的是Microsoft Access,需要用!而不是^来否定一个集合
--因此,使用的是[!JM]而不是[^JM]
SELECT cust_contact
FROM Customers
WHERE cust_contact LIKE '[^JM]%'
ORDER BY cust_contact;

--也可以使用NOT操作符得出相同的结果
SELECT cust_contact
FROM Customers
WHERE NOT cust_contact LIKE '[JM]%'
ORDER BY cust_contact;

7. 创建计算字段

1. 拼接字段

拼接(concatenate)
将值联结到一起(将一个值附加到另一个值)构成单个值。
Access和SQL Server使用+号。
DB2、Oracle、PostgreSQL、SQLite和Open Office Base使用||。

SELECT vend_name || ' (' || vend_country || ')'
FROM Vendors
ORDER BY vend_name;
/*
*再看看上述SELECT语句返回的输出。
*结合成一个计算字段的两个列用空格填充。
*许多数据库(不是所有)保存填充为列宽的文本值,而实际上
*你要的结果不需要这些空格。
*为正确返回格式化的数据,必须去掉这些空格。
*这可以使用SQL的RTRIM()函数来完成
*/
SELECT RTRIM(vend_name) + ' (' + RTRIM(vend_country) + ')'
FROM Vendors
ORDER BY vend_name;

说明:TRIM函数 大多数DBMS都支持RTRIM()(正如刚才所见,它去掉字符串右边的空格)、LTRIM()(去掉字符串左边的空格)以及TRIM()(去掉字符串左 右两边的空格)。

2. 使用别名

别名(alias)是一个字段或值的替换名。别名用AS关键字赋予

--它指示SQL创建一个包含指定计算结果的名 为vend_title的计算字段。
--任何客户端应用都可以按名称引用这个列,就像 它是一个实际的表列一样。
SELECT RTRIM(vend_name) + ' (' + RTRIM(vend_country) + ')'
AS vend_title
FROM Vendors
ORDER BY vend_name;
3. 执行算术计算
--检索订单号20008中的所有物品
SELECT prod_id, quantity, item_price
FROM OrderItems
WHERE order_num = 20008;

SELECT prod_id,
quantity,
item_price,
quantity*item_price AS expanded_price
FROM OrderItems
WHERE order_num = 20008;

SQL算术操作符 +、-、*、/

提示:如何测试计算
SELECT语句为测试、检验函数和计算提供了很好的方法。虽然SELECT通常用于从表中检索数据,但是省略了FROM子句后就是简单地访问和处理表达式,例如SELECT 3 * 2;将返回6,SELECT Trim(' abc ');将返回abc,SELECT Now();使用Now()函数返回当前日期和时间。现在你明 白了,可以根据需要使用SELECT语句进行检验。

8. 使用数据处理函数

函 数 语 法
提取字符串的组成部分 Access使用MID();DB2、Oracle、PostgreSQL和SQLite使用SUBSTR();MySQL和SQL Server使用SUBSTRING()
数据类型转换 Access和Oracle使用多个函数,每种类型的转换有一个函数;DB2和PostgreSQL使用CAST();MariaDB、MySQL和SQL Server使用 CONVERT()
取当前日期 Access使用NOW();DB2和PostgreSQL使用CURRENT_DATE;MariaDB和MySQL使用CURDATE();Oracle使用SYSDATE;SQL Server 使用GETDATE();SQLite使用DATE()

与SQL语句不一样,SQL函数不是可移植的。这表示为特定SQL实现编写的代码在其他实现中可能不正常.
例如:

函 数 语 法
提取字符串的组成部分 Access使用MID();DB2、Oracle、PostgreSQL和SQLite使用SUBSTR();MySQL和SQL Server使用SUBSTRING()
数据类型转换 Access和Oracle使用多个函数,每种类型的转换有一个函数;DB2和PostgreSQL使用CAST();MariaDB、MySQL和SQL Server使用 CONVERT()
取当前日期 Access使用NOW();DB2和PostgreSQL使用CURRENT_DATE;MariaDB和MySQL使用CURDATE();Oracle使用SYSDATE;SQL Server 使用GETDATE();SQLite使用DATE()
1. 文本处理函数
-- UPPER()将文本转换为大写
SELECT vend_name, UPPER(vend_name) AS vend_name_upcase
FROM Vendors
ORDER BY vend_name;

常用的文本处理函数

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

SOUNDEX是一个将任何文本串转换为描述其语音表示的字母数字模式的算法。SOUNDEX考虑了类似的发 音字符和音节,使得能对字符串进行发音比较而不是字母比较。

说明:SOUNDEX支持
Microsoft Access和PostgreSQL不支持SOUNDEX(),因此以下的例子不适用于这些DBMS。 另外,如果在创建SQLite时使用了SQLITE_SOUNDEX编译时选项,那么SOUNDEX()在SQLite中就可用。因为SQLITE_SOUNDEX不是默认的编译时
选项,所以多数SQLite实现不支持SOUNDEX()。

-- WHERE子句使用SOUNDEX()函数把cust_contact列值和搜索字符串转换为它们的SOUNDEX值。
--因为Michael Green和Michelle Green发音相似,所以它们的SOUNDEX值匹配
--因此WHERE子句正确地过滤出了所需的数据。
SELECT cust_name, cust_contact
FROM Customers
WHERE SOUNDEX(cust_contact) = SOUNDEX('Michael Green');
2. 日期和时间处理函数
--SQL Server中检索2012年的所有订单
SELECT order_num
FROM Orders
WHERE DATEPART(yy, order_date) = 2012;
-- SQLite
SELECT order_num
FROM Orders
WHERE strftime('%Y', order_date) = 2012;
3. 数值处理函数
函 数 说 明
ABS() 返回一个数的绝对值
COS() 返回一个角度的余弦
EXP() 返回一个数的指数值
PI() 返回圆周率
SIN() 返回一个角度的正弦
SQRT() 返回一个数的平方根
TAN() 返回一个角度的正切

常用数值处理函数

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

9. 汇总数据

1. 聚集函数

聚集函数(aggregate function) 对某些行运行的函数,计算并返回一个值。

SQL聚集函数

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

AVG()通过对表中行数计数并计算其列值之和,求得该列的平均值。AVG()可用来返回所有列的平均值,也可以用来返回特定列或行的平均值。

--返回值avg_price,它包含Products表中所有产品的平均价格
SELECT AVG(prod_price) AS avg_price
FROM Products;
SELECT AVG(prod_price) AS avg_price
FROM Products
WHERE vend_id = 'DLL01';

警告:只用于单个列
AVG()只能用来确定特定数值列的平均值,而且列名必须作为函数参数给出。为了获得多个列的平均值,必须使用多个AVG()函数。

说明:NULL值
AVG()函数忽略列值为NULL的行。

1.2. COUNT()函数

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

  • 使用COUNT(*)对表中行的数目进行计数,不管表列中包含的是空值(NULL)还是非空值。
  • 使用COUNT(column)对特定列中具有值的行进行计数,忽略NULL值。
SELECT COUNT(*) AS num_cust
FROM Customers;
--只对具有电子邮件地址的客户计数:
SELECT COUNT(cust_email) AS num_cust
FROM Customers;
1.3 MAX()函数

MAX()返回指定列中的最大值。MAX()要求指定列名

SELECT MAX(prod_price) AS max_price
FROM Products;
1.4 MIN()函数

返回指定列的最小值,要求指定列名

SELECT MIN(prod_price) AS min_price
FROM Products;
1.5 SUM()函数

SUM()用来返回指定列值的和(总计)

SELECT SUM(quantity) AS items_ordered
FROM OrderItems
WHERE order_num = 20005;
--  SUM()也可以用来合计计算值
SELECT SUM(item_price*quantity) AS total_price
FROM OrderItems
WHERE order_num = 20005;
2. 聚集不同值

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

  • 对所有行执行计算,指定ALL参数或不指定参数(因为ALL是默认行为)。
  • 只包含不同的值,指定DISTINCT参数。
--在使用了DISTINCT后,此例子中的avg_price比较高
--因为有多个物品具有相同的较低价格。
--排除它们提升了平均价格
SELECT AVG(DISTINCT prod_price) AS avg_price
FROM Products
WHERE vend_id = 'DLL01';

警告:DISTINCT不能用于COUNT()
如果指定列名,则DISTINCT只能用于COUNT()。DISTINCT不能用于COUNT(
)。类似地,DISTINCT必须使用列名,不能用于计算或表达式。

提示:将DISTINCT用于MIN()和MAX()
虽然DISTINCT从技术上可用于MIN()和MAX(),但这样做实际上没有价值。一个列中的最小值和最大值不管是否只考虑不同值,结果都是相同
的。

说明:其他聚集参数
除了这里介绍的DISTINCT和ALL参数,有的DBMS还支持其他参数,如支持对查询结果的子集进行计算的TOP和TOP PERCENT。为了解具体的 DBMS支持哪些参数,请参阅相应的文档。

3. 组合聚集函数
-- 这里用单条SELECT语句执行了4个聚集计算
--返回4个值(Products表中物品的数目
--产品价格的最高值、最低值以及平均值)
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;

10. 分组数据

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

1. 创建分组
-- 分组是使用SELECT语句的GROUP BY子句建立的。
SELECT vend_id, COUNT(*) AS num_prods
FROM Products
GROUP BY vend_id;

在使用GROUP BY子句前,需要知道一些重要的规定。

  • GROUP BY子句可以包含任意数目的列,因而可以对分组进行嵌套,更细致地进行数据分组。
  • 如果在GROUP BY子句中嵌套了分组,数据将在最后指定的分组上进行汇总。换句话说,在建立分组时,指定的所有列都一起计算(所以不 能从个别的列取回数据)。
  • GROUP BY子句中列出的每一列都必须是检索列或有效的表达式(但不能是聚集函数)。如果在SELECT中使用表达式,则必须在GROUP BY子 句中指定相同的表达式。不能使用别名。
  • 大多数SQL实现不允许GROUP BY列带有长度可变的数据类型(如文本或备注型字段)。
  • 除聚集计算语句外,SELECT语句中的每一列都必须在GROUP BY子句中给出。
  • 如果分组列中包含具有NULL值的行,则NULL将作为一个分组返回。如果列中有多行NULL值,它们将分为一组。
  • GROUP BY子句必须出现在WHERE子句之后,ORDER BY子句之前。
2. 过滤分组

除了能用GROUP BY分组数据外,SQL还允许过滤分组,规定包括哪些分组,排除哪些分组。
WHERE过滤行,而HAVING过滤分组。

SELECT cust_id, COUNT(*) AS orders
FROM Orders
GROUP BY cust_id
HAVING COUNT(*) >= 2;
SELECT vend_id, COUNT(*) AS num_prods
FROM Products
WHERE prod_price >= 4
GROUP BY vend_id
HAVING COUNT(*) >= 2;

说明:使用HAVING和WHERE
HAVING与WHERE非常类似,如果不指定GROUP BY,则大多数DBMS会同等对待它们。不过,你自己要能区分这一点。使用HAVING时应该结
合GROUP BY子句,而WHERE子句用于标准的行级过滤。

3. 分组和排序
ORDER BY GROUP BY
对产生的输出排序 对行分组,但输出可能不是分组的顺序
任意列都可以使用(甚至非选择的列也可以使用) 只可能使用选择列或表达式列,而且必须使用每个选择列表达式
不一定需要 如果与聚集函数一起使用列(或表达式),则必须使用

GROUP BY和ORDER BY经常完成相同的工作,但它们非常不同
ORDER BY与GROUP BY

ORDER BY GROUP BY
对产生的输出排序 对行分组,但输出可能不是分组的顺序
任意列都可以使用(甚至非选择的列也可以使用) 只可能使用选择列或表达式列,而且必须使用每个选择列表达式
不一定需要 如果与聚集函数一起使用列(或表达式),则必须使用

提示:不要忘记ORDER BY
一般在使用GROUP BY子句时,应该也给出ORDER BY子句。这是保证数据正确排序的唯一方法。千万不要仅依赖GROUP BY排序数据。

SELECT order_num, COUNT(*) AS items
FROM OrderItems
GROUP BY order_num
HAVING COUNT(*) >= 3
ORDER BY items, order_num;
4. SELECT子句顺序
子 句 说 明 是否必须使用
SELECT 要返回的列或表达式
FROM 从中检索数据的表 仅在从表选择数据时使用
WHERE 行级过滤
GROUP BY 分组说明 仅在按组计算聚集时使用
HAVING 组级过滤
ORDER BY 输出排序顺序

11. 使用子查询

1. 子查询
-- 语句1
SELECT order_num
FROM OrderItems
WHERE prod_id = 'RGAN01';

-- 语句2
SELECT cust_id
FROM Orders
WHERE order_num IN (20007,20008);

-- 结合 变为子查询
-- 在SELECT语句中,子查询总是从内向外处理。
-- DBMS实际上执行了两个操作。
SELECT cust_id
FROM Orders
WHERE order_num IN (SELECT order_num
FROM OrderItems
WHERE prod_id = 'RGAN01');

警告:只能是单列
作为子查询的SELECT语句只能查询单个列。企图检索多个列将返回错误

警告:子查询和性能
这里给出的代码有效,并且获得了所需的结果。但是,使用子查询并不总是执行这类数据检索的最有效方法

2. 作为计算字段使用子查询
SELECT cust_name,
cust_state,
(SELECT COUNT(*)
FROM Orders
WHERE Orders.cust_id = Customers.cust_id) AS orders
FROM Customers
ORDER BY cust_name;

12. 联结表

1. 联结
1.1. 关系表
1.2. 建立连接
SELECT vend_name, prod_name, prod_price
FROM Vendors, Products
WHERE Vendors.vend_id = Products.vend_id;
内联接
SELECT vend_name, prod_name, prod_price
FROM Vendors INNER JOIN Products
ON Vendors.vend_id = Products.vend_id;
-- 连接多个表
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;

警告:性能考虑
DBMS在运行时关联指定的每个表,以处理联结。这种处理可能非常耗费资源,因此应该注意,不要联结不必要的表。联结的表越多,性能下降越厉害。

13. 创建高级联结

使用表别名
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';
内联结和外联结

INNER JOIN、 OUTTER JOIN

警告:SQLite外联结
SQLite支持LEFT OUTER JOIN,但不支持RIGHT OUTER JOIN。幸好,如果你确实需要在SQLite中使用RIGHT OUTER JOIN,有一种更简单的办 法,这将在下面的提示中介绍。

提示:外联结的类型
要记住,总是有两种基本的外联结形式:左外联结和右外联结。它们之间的唯一差别是所关联的表的顺序。换句话说,调整FROM或WHERE子
句中表的顺序,左外联结可以转换为右外联结。因此,这两种外联结可以互换使用,哪个方便就用哪个。

3. 使用带聚集函数的联结
--检索所有顾客及每个顾客所下的订单数
SELECT Customers.cust_id,
COUNT(Orders.order_num) AS num_ord
FROM Customers INNER JOIN Orders
ON Customers.cust_id = Orders.cust_id
GROUP BY Customers.cust_id;

14. 组合查询

利用UNION操作符将多条SELECT语句组合成一个结果集。
使用UNION很简单,所要做的只是给出每条SELECT语句,在各条语句之间放上关键字UNION。

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';
UNION规则

可以看到,UNION非常容易使用,但在进行组合时需要注意几条规则。

  • UNION必须由两条或两条以上的SELECT语句组成,语句之间用关键字UNION分隔(因此,如果组合四条SELECT语句,将要使用三个UNION关键字)。
  • UNION中的每个查询必须包含相同的列、表达式或聚集函数(不过,各个列不需要以相同的次序列出)。
  • 列数据类型必须兼容:类型不必完全相同,但必须是DBMS可以隐含转换的类型(例如,不同的数值类型或不同的日期类型)。
1. 包含或取消重复的行

使用UNION时,重复的行会被自动取消。
这是UNION的默认行为,如果愿意也可以改变它。事实上,如果想返回所有的匹配行,可使用UNION ALL而不是UNION。

SELECT cust_name, cust_contact, cust_email
FROM Customers
WHERE cust_state IN ('IL','IN','MI')
UNION ALL
SELECT cust_name, cust_contact, cust_email
FROM Customers
WHERE cust_name = 'Fun4All';
对组合查询结果排序

SELECT语句的输出用ORDER BY子句排序。在用UNION组合查询时,只能使用一条ORDER BY子句,它必须位于最后一条SELECT语句之后。对于结果
集,不存在用一种方式排序一部分,而又用另一种方式排序另一部分的情况,因此不允许使用多条ORDER BY子句。

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'
ORDER BY cust_name, cust_contact;
15. 插入数据

INSERT

  1. 数据插入
    INSERT用来将行插入(或添加)到数据库表。插入有几种方式:
  • 插入完整的行;
  • 插入行的一部分;
  • 插入某些查询的结果。
插入完整的行

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

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

提示:INTO关键字
在某些SQL实现中,跟在INSERT之后的INTO关键字是可选的。但是,即使不一定需要,最好还是提供这个关键字,这样做将保证SQL代码在 DBMS之间可移植。

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);

警告:小心使用VALUES
不管使用哪种INSERT语法,VALUES的数目都必须正确。如果不提供列名,则必须给每个表列提供一个值;如果提供列名,则必须给列出的每个列一个值。否则,就会产生一条错误消息,相应的行不能成功插入。

插入部分行
INSERT INTO Customers(cust_id,
cust_name,
cust_address,
cust_city,
cust_state,
cust_zip,
cust_country)
VALUES('1000000006',
'Toy Land',
'123 Any Street',
'New York',
'NY',
'11111',
'USA');

警告:省略列
如果表的定义允许,则可以在INSERT操作中省略某些列。省略的列必须满足以下某个条件。

  • 该列定义为允许NULL值(无值或空值)。
  • 在表定义中给出默认值。这表示如果不给出值,将使用默认值。
    如果对表中不允许NULL值且没有默认值的列不给出值,DBMS将产生错误消息,并且相应的行插入不成功。
插入检索出的数据
/**
 *使用INSERT SELECT从CustNew中将所有数据导入Customers。
 *SELECT语句从CustNew检索出要插入的值,而不是列出它们。
 *SELECT中列 出的每一列对应于Customers表名后所跟的每一列。
 *这条语句将插入多少行呢?这依赖于CustNew表有多少行。
 *如果这个表为空,则没有行被插 入(也不产生错误,因为操作仍然是合法的)。
 *如果这个表确实有数据,则所有数据将被插入到Customers。
**/
INSERT
INTO Customers(cust_id,cust_contact,
cust_email,
cust_name,
cust_address,
cust_city,
cust_state,
cust_zip,
cust_country)
SELECT
cust_id,
cust_contact,
cust_email,
cust_name,
cust_address,
cust_city,
cust_state,
cust_zip,
cust_country
FROM CustNew;

提示:INSERT SELECT中的列名
为简单起见,这个例子在INSERT和SELECT语句中使用了相同的列名。但是,不一定要求列名匹配。事实上,DBMS一点儿也不关心SELECT返 回的列名。它使用的是列的位置,因此SELECT中的第一列(不管其列名)将用来填充表列中指定的第一列,第二列将用来填充表列中指定的 第二列,如此等等。

INSERT SELECT中SELECT语句可以包含WHERE子句,以过滤插入的数据。

提示:插入多行
INSERT通常只插入一行。要插入多行,必须执行多个INSERT语句。INSERT SELECT是个例外,它可以用一条INSERT插入多行,不管SELECT语句返回多少行,都将被INSERT插入。

2. 从一个表复制到另一个表

有一种数据插入不使用INSERT语句。要将一个表的内容复制到一个全新的表(运行中创建的表),可以使用SELECT INTO语句。

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

-- MariaDB、MySQL、Oracle、PostgreSQL和SQLite使用的语法稍有不同
CREATE TABLE CustCopy AS
SELECT * FROM Customers;

在使用SELECT INTO时,需要知道一些事情:

  • 任何SELECT选项和子句都可以使用,包括WHERE和GROUP BY;
  • 可利用联结从多个表插入数据;
  • 不管从多少个表中检索数据,数据都只能插入到一个表中。

16. 更新和删除数据

UPDATE和DELETE

有两种使用UPDATE的方式:

  • 更新表中的特定行;
  • 更新表中的所有行。

基本的UPDATE语句由三部分组成,分别是:

  • 要更新的表;
  • 列名和它们的新值;
  • 确定要更新哪些行的过滤条件。
UPDATE Customers
SET cust_email = 'kim@thetoystore.com'
WHERE cust_id = '1000000005';

-- 多个列 
UPDATE Customers
SET cust_contact = 'Sam Roberts',
cust_email = 'sam@toyland.com'
WHERE cust_id = '1000000006';

提示:FROM关键字
有的SQL实现支持在UPDATE语句中使用FROM子句,用一个表的数据更新另一个表的行。如想知道你的DBMS是否支持这个特性,请参阅它的 文档。

-- 要删除某个列的值,可设置它为NULL(假如表定义允许NULL值)
UPDATE Customers
SET cust_email = NULL
WHERE cust_id = '1000000005';
2. 删除数据

使用DELETE语句。有两种使用DELETE的方式:

  • 从表中删除特定的行;
  • 从表中删除所有行。
DELETE FROM Customers
WHERE cust_id = '1000000006';

DELETE不需要列名或通配符。DELETE删除整行而不是删除列。要删除指定的列,请使用UPDATE语句。

说明:删除表的内容而不是表
DELETE语句从表中删除行,甚至是删除表中所有行。但是,DELETE不删除表本身

17. 创建和操纵表

利用CREATE TABLE创建表,必须给出下列信息:

  • 新表的名字,在关键字CREATE TABLE之后给出;
  • 表列的名字和定义,用逗号分隔;
  • 有的DBMS还要求指定表的位置。
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,
prod_desc VARCHAR(1000) NULL
);
指定默认值
-- SQL允许指定默认值,在插入行时如果不给出值,
-- DBMS将自动采用默认值。
-- 默认值在CREATE TABLE语句的列定义中用关键字DEFAULT指定。
CREATE TABLE OrderItems
(
order_num  INTEGER  NOT NULL,
order_item INTEGER  NOT NULL,
prod_id    CHAR(10) NOT NULL,
quantity   INTEGER  NOT NULL DEFAULT 1,
item_price DECIMAL(8,2) NOT NULL,
);
获得系统日期
DBMS 函数/变量
Access NOW()
DB2 CURRENT_DATE
MySQL CURRENT_DATE()
Oracle SYSDATE
PostgreSQL CURRENT_DATE
SQL Server GETDATE()
SQLite date('now')
2. 更新表

更新表定义,可以使用ALTER TABLE语句
以下是使用ALTERTABLE时需要考虑的事情。

  • 理想情况下,不要在表中包含数据时对其进行更新。应该在表的设计过程中充分考虑未来可能的需求,避免今后对表的结构做大改动。
  • 所有的DBMS都允许给现有的表增加列,不过对所增加列的数据类型(以及NULL和DEFAULT的使用)有所限制。
  • 许多DBMS不允许删除或更改表中的列。
  • 多数DBMS允许重新命名表中的列。
  • 许多DBMS限制对已经填有数据的列进行更改,对未填有数据的列几乎没有限制。
-- 给Vendors表增加一个名为vend_phone的列,其数据类型为CHAR。
ALTER TABLE Vendors
ADD vend_phone CHAR(20);
ALTER TABLE Vendors
DROP COLUMN vend_phone;

说明:ALTER TABLE和SQLite
SQLite对使用ALTER TABLE执行的操作有所限制。最重要的一个限制是,它不支持使用ALTER TABLE定义主键和外键,这些必须在最初创建表 时指定。

3 删除表

DROP TABLE CustCopy;

4 重命名表

每个DBMS对表重命名的支持有所不同。对于这个操作,不存在严格的标准。DB2、MariaDB、MySQL、Oracle和PostgreSQL用户使
用RENAME语句,SQL Server用户使用sp_rename存储过程,SQLite用户使用ALTER TABLE语句。

18. 使用视图

SELECT cust_name, cust_contact
FROM Customers, Orders, OrderItems
WHERE Customers.cust_id = Orders.cust_id
AND OrderItems.order_num = Orders.order_num
AND prod_id = 'RGAN01';

-- 假如可以把整个查询包装成一个名为ProductCustomers的虚拟表,
-- 则可以如下轻松地检索出相同的数据:
SELECT cust_name, cust_contact
FROM ProductCustomers
WHERE prod_id = 'RGAN01';

视图创建和使用的一些最常见的规则和限制。

  • 与表一样,视图必须唯一命名(不能给视图取与别的视图或表相同的名字)。
  • 对于可以创建的视图数目没有限制。
    创建视图,必须具有足够的访问权限。这些权限通常由数据库管理人员授予。
  • 视图可以嵌套,即可以利用从其他视图中检索数据的查询来构造视图。所允许的嵌套层数在不同的DBMS中有所不同(嵌套视图可能会严 重降低查询的性能,因此在产品环境中使用之前,应该对其进行全面测试)。
  • 许多DBMS禁止在视图查询中使用ORDER BY子句。
  • 有些DBMS要求对返回的所有列进行命名,如果列是计算字段,则需要使用别名(关于列别名的更多信息,请参阅第7课)。
  • 视图不能索引,也不能有关联的触发器或默认值。
  • 有些DBMS把视图作为只读的查询,这表示可以从视图检索数据,但不能将数据写回底层表。详情请参阅具体的DBMS文档。
  • 有些DBMS允许创建这样的视图,它不能进行导致行不再属于视图的插入或更新。例如有一个视图,只检索带有电子邮件地址的顾客。如 果更新某个顾客,删除他的电子邮件地址,将使该顾客不再属于视图。这是默认行为,而且是允许的,但有的DBMS可能会防止这种情况 发生。
1. 创建视图

视图用CREATE VIEW语句来创建。与CREATE TABLE一样,CREATE VIEW只能用于创建不存在的视图。

说明:视图重命名
删除视图,可以使用DROP语句,其语法为DROP VIEW viewname;覆盖(或更新)视图,必须先删除它,然后再重新创建。

-- 这条语句创建一个名为ProductCustomers的视图,它联结三个表,返回已订购了任意产品的所有顾客的列表。
-- 如果执行SELECT * FROM ProductCustomers,将列出订购了任意产品的顾客。
CREATE VIEW ProductCustomers AS
SELECT cust_name, cust_contact, prod_id
FROM Customers, Orders, OrderItems
WHERE Customers.cust_id = Orders.cust_id
AND OrderItems.order_num = Orders.order_num;

-- 检索订购了产品RGAN01的顾客
SELECT cust_name, cust_contact
FROM ProductCustomers
WHERE prod_id = 'RGAN01';

19. 使用存储过程

什么要使用存储过程,如何使用存储过程,以及创建和使用存储过程的基本语法。
SqlLite 不支持,而接下来又主要用到 SqlLite 故不做笔记

20. 管理事务处理

介绍什么是事务处理,如何利用COMMIT和ROLLBACK语句管理事务处理。

  1. 事务处理
    使用事务处理(transaction processing),通过确保成批的SQL操作要么完全执行,要么完全不执行,来维护数据库的完整性。

关于事务处理需要知道的几个术语:

  • 事务(transaction)指一组SQL语句;
  • 回退(rollback)指撤销指定SQL语句的过程;
  • 提交(commit)指将未存储的SQL语句结果写入数据库表;
  • 保留点(savepoint)指事务处理中设置的临时占位符(placeholder),可以对它发布回退(与回退整个事务处理不同)。

提示:可以回退哪些语句?
事务处理用来管理INSERT、UPDATE和DELETE语句。不能回退SELECT语句(回退SELECT语句也没有必要),也不能回退CREATE或DROP操作。事 务处理中可以使用这些语句,但进行回退时,这些操作也不撤销。

2. 控制事务处理

管理事务的关键在于将SQL语句组分解为逻辑块,并明确规定数据何时应该回退,何时不应该回退。

-- SQL Server
-- BEGIN TRANSACTION和COMMIT TRANSACTION语句之间的
-- SQL必须完全执行或者完全不执行。
BEGIN TRANSACTION
...
COMMIT TRANSACTION

ROLLBACK

-- SQL的ROLLBACK命令用来回退(撤销)SQL语句
-- 执行DELETE操作,然后用ROLLBACK语句撤销。
DELETE FROM Orders;
ROLLBACK;

COMMIT
一般的SQL语句都是针对数据库表直接执行和编写的。这就是所谓的隐式提交(implicit commit),即提交(写或保存)操作是自动进行的。
在事务处理块中,提交不会隐式进行。

-- SQL Server
-- 在这个SQL Server例子中,从系统中完全删除订单12345。
-- 因为涉及更新两个数据库表Orders和OrderItems,
-- 所以使用事务处理块来保证订单 不被部分删除。
-- 最后的COMMIT语句仅在不出错时写出更改。
-- 如果第一条DELETE起作用,
-- 但第二条失败,则DELETE不会提交。
BEGIN TRANSACTION
DELETE OrderItems WHERE order_num = 12345
DELETE Orders WHERE order_num = 12345
COMMIT TRANSACTION

使用保留点
使用简单的ROLLBACK和COMMIT语句,就可以写入或撤销整个事务。但是,只对简单的事务才能这样做,复杂的事务可能需要部分提交或回退。
在MariaDB、MySQL和Oracle中创建占位符,可使用SAVEPOINT语句:

-- 创建保留点
SAVEPOINT delete1;

-- SQL Server 创建保留点
-- SAVE TRANSACTION delete1;

--  SQL Server例子
BEGIN TRANSACTION
INSERT INTO Customers(cust_id, cust_name)
VALUES('1000000010', 'Toys Emporium');
SAVE TRANSACTION StartOrder;                      -- 创建保留点
INSERT INTO Orders(order_num, order_date, cust_id)
VALUES(20100,'2001/12/1','1000000010');
IF @@ERROR <> 0 ROLLBACK TRANSACTION StartOrder;
INSERT INTO OrderItems(order_num, order_item, prod_id, quantity, item_price)
VALUES(20100, 1, 'BR01', 100, 5.49);
IF @@ERROR <> 0 ROLLBACK TRANSACTION StartOrder;
INSERT INTO OrderItems(order_num, order_item, prod_id, quantity, item_price)
VALUES(20100, 2, 'BR03', 100, 10.99);
IF @@ERROR <> 0 ROLLBACK TRANSACTION StartOrder;
COMMIT TRANSACTION

21. 使用游标

什么是游标,如何使用游标

  1. 游标

结果集(result set)
SQL查询所检索出的结果。

有时,需要在检索出来的行中前进或后退一行或多行,这就是游标的用途所在。游标(cursor)是一个存储在DBMS服务器上的数据库查询,
它不是一条SELECT语句,而是被该语句检索出来的结果集。在存储了游标之后,应用程序可以根据需要滚动或浏览其中的数据。

说明:具体DBMS的支持
Microsoft Access不支持游标,所以本课的内容不适用于Microsoft Access。
MySQL 5已经支持存储过程。因此,本课的内容不适用MySQL较早的版本。
SQLite支持的游标称为步骤(step),下面讲述的基本概念适用于SQLite的步骤,但语法可能完全不同。

不同的DBMS支持不同的游标选项和特性。常见的一些选项和特性如下。

  • 能够标记游标为只读,使数据能读取,但不能更新和删除。
  • 能控制可以执行的定向操作(向前、向后、第一、最后、绝对位置、相对位置等)。
  • 能标记某些列为可编辑的,某些列为不可编辑的。
  • 规定范围,使游标对创建它的特定请求(如存储过程)或对所有请求可访问。
  • 指示DBMS对检索出的数据(而不是指出表中活动数据)进行复制,使数据在游标打开和访问期间不变化。
2. 使用游标

使用游标涉及几个明确的步骤:

  • 在使用游标前,必须声明(定义)它。这个过程实际上没有检索数据,它只是定义要使用的SELECT语句和游标选项。
  • 一旦声明,就必须打开游标以供使用。这个过程用前面定义的SELECT语句把数据实际检索出来。
  • 对于填有数据的游标,根据需要取出(检索)各行。
  • 在结束游标使用时,必须关闭游标,可能的话,释放游标(有赖于具体的DBMS)。

使用DECLARE语句创建游标,这条语句在不同的DBMS中有所不同。DECLARE命名游标,并定义相应的SELECT语句,根据需要带WHERE和其他子 句。

-- DECLARE语句用来定义和命名游标,
-- 这里为CustCursor。
-- SELECT语句定义一个包含没有电子邮件地址(NULL值)的所有顾客的游标。
DECLARE CustCursor CURSOR
FOR
SELECT * FROM Customers
WHERE cust_email IS NULL
-- 打开游标
OPEN CURSOR CustCursor

现在可以用FETCH语句访问游标数据了。FETCH指出要检索哪些行,从何处检索它们以及将它们放于何处(如变量名)。

DECLARE TYPE CustCursor IS REF CURSOR
RETURN Customers%ROWTYPE;
DECLARE CustRecord Customers%ROWTYPE
BEGIN
OPEN CustCursor;
FETCH CustCursor INTO CustRecord;
CLOSE CustCursor;
END;

22. 高级SQL特性

约束、索引和触发器。

约束(constraint)
管理如何插入或处理数据库数据的规则。

1. 主键

主键是一种特殊的约束,用来保证一列(或一组列)中的值是唯一的,而且永不改动。换句话说,表中的一列(或 多个列)的值唯一标识表中的每一行。这方便了直接或交互地处理表中的行。没有主键,要安全地UPDATE或DELETE特定行而不影响其他行会非 常困难。

表中任意列只要满足以下条件,都可以用于主键:

  • 任意两行的主键值都不相同。
  • 每行都具有一个主键值(即列中不允许NULL值)。
  • 包含主键值的列从不修改或更新。(大多数DBMS不允许这么做,但如果你使用的DBMS允许这样做,好吧,千万别!)
  • 主键值不能重用。如果从表中删除某一行,其主键值不分配给新行。
CREATE TABLE Vendors
(
vend_id       CHAR(10)  NOT NULL PRIMARY KEY,   -- 主键
vend_name     CHAR(50)  NOT NULL,
vend_address  CHAR(50)  NULL,
vend_city     CHAR(50)  NULL,
vend_state    CHAR(5)   NULL,
vend_zip      CHAR(10)  NULL,
vend_country  CHAR(50)  NULL
);
给表的vend_id列定义添加关键字PRIMARY KEY,使其成为主键。
ALTER TABLE Vendors
ADD CONSTRAINT PRIMARY KEY (vend_id);

说明:SQLite中的键
SQLite不允许使用ALTER TABLE定义键,要求在初始的CREATE TABLE语句中定义它们。

外键

外键是表中的一列,其值必须列在另一表的主键中。外键是保证引用完整性的极其重要部分。

CREATE TABLE Orders
(
order_num   INTEGER NOT NULL PRIMARY KEY,   -- 主键
order_date DATETIME NOT NULL,
cust_id    CHAR(10) NOT NULL REFERENCES Customers(cust_id) -- 外键
);
--ALTER TABLE语句中用CONSTRAINT语法来定义外键
ALTER TABLE Orders
ADD CONSTRAINT
FOREIGN KEY (cust_id) REFERENCES Customers (cust_id)
唯一约束

唯一约束用来保证一列(或一组列)中的数据是唯一的。它们类似于主键,但存在以下重要区别。

  • 表可包含多个唯一约束,但每个表只允许一个主键。
  • 唯一约束列可包含NULL值。
  • 唯一约束列可修改或更新。
  • 唯一约束列的值可重复使用。
  • 与主键不一样,唯一约束不能用来定义外键。
检查约束

检查约束用来保证一列(或一组列)中的数据满足一组指定的条件。检查约束的常见用途有以下几点。

  • 检查最小或最大值。例如,防止0个物品的订单(即使0是合法的数)。
  • 指定范围。例如,保证发货日期大于等于今天的日期,但不超过今天起一年后的日期。
  • 只允许特定的值。例如,在性别字段中只允许M或F。
CREATE TABLE OrderItems
(
order_num   INTEGER  NOT NULL,
order_item  INTEGER  NOT NULL,
prod_id     CHAR(10) NOT NULL,
quantity    INTEGER  NOT NULL  CHECK (quantity > 0), -- 添加检查约束
item_price  MONEY    NOT NULL,
);

-- 检查名为gender的列只包含M或F,可编写如下的ALTER TABLE语句:
ADD CONSTRAINT CHECK (gender LIKE '[MF]')
2. 索引

索引用来排序数据以加快搜索和排序操作的速度。
数据库索引的作用也一样。主键数据总是排序的,这是DBMS的工作。因此,按主键检索特定行总是一种快速有效的操作。
解决方法是使用索引。可以在一个或多个列上定义索引,使DBMS保存其内容的一个排过序的列表。在定义了索引后,DBMS以使用书的索引 类似的方法使用它。DBMS搜索排过序的索引,找出匹配的位置,然后检索这些行

在开始创建索引前,应该记住以下内容:

  • 索引改善检索操作的性能,但降低了数据插入、修改和删除的性能。在执行这些操作时,DBMS必须动态地更新索引。
  • 索引数据可能要占用大量的存储空间。
  • 并非所有数据都适合做索引。取值不多的数据(如州)不如具有更多可能值的数据(如姓或名),能通过索引得到那么多的好处。
  • 索引用于数据过滤和数据排序。如果你经常以某种特定的顺序排序数据,则该数据可能适合做索引。
  • 可以在索引中定义多个列(例如,州加上城市)。这样的索引仅在以州加城市的顺序排序时有用。如果想按城市排序,则这种索引没有用处。

索引用CREATE INDEX语句创建(不同DBMS创建索引的语句变化很大)

--索引必须唯一命名。这里的索引名prod_name_ind在关键字CREATE INDEX之后定义。
-- ON用来指定被索引的表,而索引中包含的列(此例中仅有一 列)在表名后的圆括号中给出。
CREATE INDEX prod_name_ind
ON PRODUCTS (prod_name);
3. 触发器

触发器是特殊的存储过程,它在特定的数据库活动发生时自动执行。触发器可以与特定表上的INSERT、UPDATE和DELETE操作(或组合)相关联。

触发器内的代码具有以下数据的访问权:

  • INSERT操作中的所有新数据;
  • UPDATE操作中的所有新数据和旧数据;
  • DELETE操作中删除的数据。
-- SQL Server
-- 对所有INSERT和UPDATE操作,将Customers表中的cust_state列转换为大写
CREATE TRIGGER customer_state
ON Customers
FOR INSERT, UPDATE
AS
UPDATE Customers
SET cust_state = Upper(cust_state)
WHERE Customers.cust_id   inserted.cust_id;

提示:约束比触发器更快
一般来说,约束的处理比触发器快,因此在可能的时候,应该尽量使用约束。

-----------------------------------------大部分内容来自《sql必知必会》

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,198评论 4 359
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,663评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,985评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,673评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,994评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,399评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,717评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,407评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,112评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,371评论 2 241
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,891评论 1 256
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,255评论 2 250
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,881评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,010评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,764评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,412评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,299评论 2 260

推荐阅读更多精彩内容