第三课: 排序检索数据
- distinct关键字:
distinct 列名1,列名2,列名3
-
DISTINCT
关键字会作用于每一列,然后取最大的那一个返回;
-
- order by使用方法
-
order by
子句只能出现在select
语句中最后一条子句,否则报错; - 多个列排序时:
order by 列名1,列名2
,仅在多行具有相同的列名1
时,才会按照列名2
进行排序,如果每个列名1
都是唯一的,则不会按列名2
进行排序; -
select 列名1,列名2,列名3 from table order by 2,3
:先按照列名2
进行排序,再按照列名3
进行排序(列名2
的相对位置是2,列名3
的相对位置是3) -
desc
和asc
用法:desc/asc
只能作用于一列,如果想在多个列上进行排序,,需要对每一列指定desc/asc
关键字。
-
第四课:使用where子句
4.2 where子句操作符
操作符 | 说明 | 举例 |
---|---|---|
= | 等于 | |
<> | 不等于 | |
!= | 不等于 | |
< | 小于 | |
<= | 小于等于 | |
!< | 不小于 | |
> | 大于 | |
>= | 大于等于 | |
!> | 不大于 | |
BETWEEN…AND… | 在指定的两个值之间 | WHERE price BETWEEN 5 AND 10; |
IS NULL | 为null值 | WHERE name IS NULL;//name为空的记录 |
IS NOT NULL | 不为null值 | WHERE name IS NOT NULL //name不为空的记录 |
第五课 高级数据过滤
- and
- or:当一个
where
子句中有多个and
和or
时,一定要加括号,因为and
的优先级比or
高; - in:指定操作范围,相当于多个
or
;in
的优点:-
in
的语法更加清楚,直观; - 在与其他
and
和or
操作符组合使用in
时,求值顺序更容易管理; - in的效率高于多个
or
的组合; - 最大优点:可以包含其他
select
语句,可以动态建立where
子句。
-
- not :
- 功能:否定其后所跟的任何条件;
- 语法:not从不单独使用,总是和其他操作符一起使用;
not
关键字可以用在要过滤的列前,而不仅是在其后; - 如何使用:****先写出条件表达式,然后在条件表达式前面(where后面)加
not
****//这两条语句等价 SELECT * FROM t_project WHERE NOT Project_ID IN (1,2,3,4,5,6); SELECT * FROM t_project WHERE Project_ID NOT IN (1,2,3,4,5,6);
- not 比
!=
的功能更加强大,尤其是在复杂的子句中,如上述和in
的联合使用中。
第6课 用通配符进行过滤
通配符搜索只能作用于文本字段,非文本字段不能用通配符进行搜索;
6.1 “%”通配符
- %表示任何字符(0个,1个,多个字符)出现任意次数(0次,1次,多次);
- %可以在任何地方使用任何次;
- %不能匹配NULL;
6.2 “_”通配符
- _ 只匹配一个字符,因此有几个字符需要匹配就需要多个_;
6.3 “[]”通配符
-
[]
通配符用来指定一个字符集,它必须匹配指定位置(通配符的位置)的一个字符,但是并不是所有的DBMS都支持此通配符;
//找出所有名字以J或M起头的联系人
WHERE cust_contact LIKE '[JM]%' ORDER BY cust_contact;
//找出所有名字不以J或M起头的联系人
WHERE cust_contact LIKE '[^JM]%' ORDER BY cust_contact;
6.4 通配符的缺点及使用注意事项
- 缺点:通配符搜索比较耗时,性能低下;
- 注意事项:
- 不要过度使用通配符:如果其他操作符能达到相同的目的,应该使用其他操作符;
- 把通配符置于开始处,搜索起来是最慢的,在确实需要使用通配符时,也尽量不要把它们用在搜索模式的开始处;
- 仔细注意通配符的位置。如果放错地方,可能不会返回想要的数据;
第7课 创建计算字段
- 计算字段并不是数据库中真实存在的字段,而是运行时,在select语句内创建的,用来接收数据库中多个字段计算后的结果。
7.2 拼接字段
将多个字段拼接成一个字段
从数据库中查询返回的字段可以来源于多张表,也可以是自己创建的计算字段。
SELECT CONCAT(Project_Caption,'(',Project_ID,")")
AS project_title
FROM t_project
ORDER BY t_project.Project_ID;
- 不同的数据库采用不一样的方式来拼接多个字段
|数据库类型|拼接方式|举例|
|—|:—-:|—-|—|
|Access
、SQL Server
|+|SELECT vend_name + ' (' + vend_country + ')'+FROM VendorsORDER BY vend_name;
|
|DB2
、Oracle
、PostgreSQL
、SQLite
、Open Office Base
|||
|SELECT RTRIM(vend_name) || ' (' || RTRIM(vend_country) || ')'AS vend_title FROM Vendors ORDER BY vend_name;
|
|MySQL
、MariaDB
|concat(str...)
|SELECT Concat(vend_name, ' (', vend_country, ')') AS vend_title FROM Vendors ORDER BY vend_name;
|
- 去掉空格
名称 | 作用 | 举例 |
---|---|---|
RTRIM | 切割字符串右边的空格 | RTRIM(vend_country) |
LTRIM | 切割字符串左边的空格 | LTRIM(vend_country) |
TRIM | 切割字符串左右两边的空格 | TRIM(vend_country) |
- 使用别名(AS)
给计算字段取一个名字,方便以后的操作;
7.3 执行算术计算
对字段做加减乘除运算
SELECT prod_id,
quantity,
item_price,
quantity*item_price AS expanded_price
FROM OrderItems
WHERE order_num = 20008;
7.4 测试计算
select 去掉from子句后就可以用来测试
SELECT 3 * 2;将返回6,SELECT Trim(‘ abc ‘);将返回abc
第8课 使用数据处理函数
8.1 函数以及带来的问题
- 函数的作用:高效,方便。
- 问题:每一个DBMS都有特定的函数,而且名称和语法可能不同;
8.2 使用函数
- 大多数SQL实现支持以下类型的函数。
- 处理文本字符串(如删除或填充值,转换值为大写或小写)的文本函数。
2.数值数据上进行 算术操作(如返回绝对值,进行代数运算)的数值函数。 - 日期和时间值并从这些值中提取特定成分(如返回两个日期之差,检查日期有效性)的日期和时间函数。
- 返回DBMS正使用的特殊信息(如返回用户登录信息)的系统函数。
- 处理文本字符串(如删除或填充值,转换值为大写或小写)的文本函数。
- 常用的文本处理函数
函 数 说 明 LEFT()(或使用子字符串函数) 返回字符串左边的字符 LENGTH()(也使用DATALENGTH()或LEN()) 返回字符串的长度 LOWER()(Access使用LCASE()) 将字符串转换为小写 LTRIM() 去掉字符串左边的空格 RIGHT()(或使用子字符串函数) 返回字符串右边的字符 LTRIM() 去掉字符串右边的空格 SOUNDEX() 返回字符串的SOUNDEX值 UPPER()(Access使用UCASE()) 将字符串转换为大写 - 日期和时间处理函数
- 数值处理函数
函 数 说 明 ABS() 返回一个数的绝对值 COS() 返回一个角度的余弦 EXP() 返回一个数的指数值 PI() 返回圆周率 SIN() 返回一个角度的正弦 SQRT() 返回一个数的平方根 TAN() 返回一个角度的正切
第9课 汇总数据
9.1 聚集函数
函 数 | 作用 | 说明 | |
---|---|---|---|
AVG() | 返回某列的平均值 | ||
COUNT() | 返回某列的行数 | ||
MAX() | 返回某列的最大值 | 返回最大的日期,数值,排序后列的最后一行 | |
MIN() | 返回某列的最小值 | 返回最小的日期,数值,排序后列的第一行 | |
SUM() | 返回某列值之和 |
1. AVG()
特别说明
- 只能用于单个列;
- 而且必须给出列名;
- 多个列就必须使用多次avg();
- AVG()函数忽略列值为NULL的行;
举例说明
SELECT AVG( DISTINCT t_project.Project_code ) AS project_code FROM t_project;
SELECT AVG( DISTINCT t_project.Project_code ) AS project_code, AVG( DISTINCT t_project.Project_ID ) AS project_Id FROM t_project;
2. COUNT()
- 特别说明
- 使用
COUNT(*)
对表中行的数目进行计数,不管表列中包含的是空值(NULL)还是非空值,但是不能用DISTINCT
修饰,即COUNT(DISTINCT *)是违法的。 - 使用
COUNT(column)
对特定列中具有值的行进行计数,忽略NULL值,可以用DISTINCT修饰,即COUNT(DISTINCT column)是合法的。
- 使用
3. MAX()
- 特别说明
- 必须指定列名;
- 忽略列值为null的行
- 虽然MAX()一般用来找出最大的数值或日期值,但许多(并非所有)DBMS允许将它用来返回任意列中的最大值,包括返回文本列中的最值。在用于文本数据时,MAX()返回按该列排序后的最后一行。
SELECT MAX(prod_price) AS max_price
FROM Products;
4. MIN()
- 特别说明
- 必须指定列名;
- 忽略列值为null的行
- 虽然MIN()一般用来找出最小的数值或日期值,但许多(并非所有)DBMS允许将它用来返回任意列中的最小值,包括返回文本列中的最值。在用于文本数据时,MAX()返回按该列排序后的第一行。
5. SUM()
- SUM()函数忽略列值为NULL的行
SELECT
SUM(3 * quantity) AS total_price
FROM
OrderItems
WHERE
order_num = 20005;
9.2 组合聚集函数
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课 分组数据
10.1 创建分组(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
子句之前。
-- 先按照EPS_ID进行分组,然后在按照Status进行分组
SELECT
t_project.EPS_ID,
t_project.`Status`,
COUNT(*)
FROM
t_project
GROUP BY
t_project.EPS_ID,
t_project.`Status`;
10.2 过滤分组(HAVING)
-
GROUP BY
创建分组,HAVING
过滤分组,包括哪些分组,排除哪些分组。 -
GROUP BY
和WHERE
唯一的差别:- WHERE过滤行,而HAVING过滤分组。
- 学过的有关
WHERE
的所有技术和选项都适用于HAVING
。它们的句法是相同的,只是关键字有差别。
HAVING和WHERE的差别另一种理解方式:
WHERE在数据分组前进行过滤,HAVING在数据分组后进行过滤。这是一个重要的区别,WHERE排除的行不包括在分组中。这可能会改变计算值,从而影响HAVING子句中基于这些值过滤掉的分组。
SELECT
cust_id,
COUNT(*) AS orders
FROM
Orders
GROUP BY
cust_id
HAVING
COUNT(*) >= 2;
10.3 分组和排序
名称 | ORDERBY | GROUP BY |
---|---|---|
作用 | 对产生的输出排序 | 对行分组,但输出可能不是分组的顺序 |
使用范围 | 任意列都可以使用(甚至非选择的列也可以使用) | 只可能使用选择列或表达式列,而且必须使用每个选择列表达式 |
是否需要 | 不一定需要 | 如果与聚集函数一起使用列(或表达式),则必须使用 |
一般在使用GROUP BY子句时,应该也给出ORDER BY子句。这是保证数据正确排序的唯一方法。
SELECT
order_num,
COUNT(*) AS items
FROM
OrderItems
GROUP BY
order_num
HAVING
COUNT(*) >= 3
ORDER BY
items,
order_num;
10.4 SELECT子句顺序
子 句 | 说 明 | 是否必须使用 |
---|---|---|
SELECT | 要返回的列或表达式 | 是 |
FROM | 从中检索数据的表 | 仅在从表选择数据时使用 |
WHERE | 行级过滤 | 否 |
GROUP BY | 分组说明 | 仅在按组计算聚集时使用 |
HAVING | 组级过滤 | 否 |
ORDER BY | 输出排序顺序 | 否 |
第11课 使用子查询
- 就是在
select
里面嵌套select
,嵌套的select就是子查询; - 查询执行的顺序:由内而外;
- 使用子查询效率低下;
- 使用场景:
- 子查询常用于WHERE子句的IN操作符中;
- 用来填充计算列。
11.1 利用子查询进行过滤
SELECT
customers.cust_name,
customers.cust_contact
FROM
customers
WHERE
cust_id IN (
SELECT
orders.cust_id
FROM
orders
WHERE
order_num IN (
SELECT
orderitems.order_num
FROM
orderitems
WHERE
orderitems.prod_id = "RGAN01"
);
);
- 作为子查询的SELECT语句只能查询单个列。企图检索多个列将返回错误。
- 使用子查询并不总是执行这类数据检索的最有效方法,效率低下。
11.2 作为计算字段使用子查询
SELECT
customers.cust_name,
customers.cust_state,
(
SELECT
COUNT(*)
FROM
orders
WHERE
orders.cust_id = customers.cust_id
) AS orders
FROM
customers
ORDER BY
cust_name;
必须完全限定类名
第12课 联结表
12.1 联结
- 关系式数据库设计,关系表
- 方便维护;
- 防止冗余数据;
12.2 创建联结
- 如何创建链接(首先列出表,然后定义表之间的关系)
- 指定要链接的表;
- 指定这些表之间的关联方式;
SELECT
vend_name,
prod_name,
prod_price
FROM
vendors,
products
WHERE
vendors.vend_id = products.vend_id;
WHERE子句的重要性
- 在一条SELECT语句中联结几个表时,相应的关系是在运行中构造的;
- 在数据库表的定义中没有指示DBMS如何对表进行联结的内容,你必须自己做这件事情;
- 在联结两个表时,实际要做的是将第一个表中的每一行与第二个表中的每一行配对;
- WHERE子句作为过滤条件,只包含那些匹配给定条件(这里是联结条件)的行。没有WHERE子句,第一个表中的每一行将与第二个表中的每一行配对,而不管它们逻辑上是否能配在一起。
笛卡儿积(cartesian product)
由没有联结条件的表关系返回的结果为笛卡儿积。检索出的行的数目将是第一个表中的行数乘以第二个表中的行数。
提示:叉联结
有时,返回笛卡儿积的联结,也称叉联结(cross join)
12.2.2 内联结
--上述where的等价SQL语句
SELECT
vend_name,
prod_name,
prod_price
FROM
vendors
INNER JOIN products ON vendors.vend_id = products.vend_id;
12.2.3 联结多个表
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课 创建高级联结
13.1 使用表别名
- SQL可以给列名,计算字段,表名起别名;
- 使用别名的好处:
- 缩短SQL语句;
- 允许在一条SELECT语句中多次使用相同的表。
SELECT
cust_name,
cust_contact
FROM
customers AS C,
orders AS O,
orderitems AS OI
WHERE
O.cust_id = C.cust_id
AND O.order_num = OI.order_num AND prod_id = 'RGAN01';
- 表别名可以在任何子句中使用(如
WHERE子句
,SELECT的列表
、ORDER BY子句
等); - 表别名只在查询执行中使用。与列别名不一样,表别名不返回到客户端;
13.2 使用不同类型的联结
联接的类型
- 等值联接/内联接(inner join)
- 自联接(self-join)
- 自然联接(natural join)
- 外联接(outer join)
13.2.1 自联结
SELECT
cust_id,
cust_name,
cust_contact
FROM
customers
WHERE
cust_name = (
SELECT
cust_name
FROM
customers
WHERE
cust_contact = 'Jim Jones'
);
使用自联接实现
SELECT
C1.cust_id,
C1.cust_name,
C1.cust_contact
FROM
customers AS C1,
customers AS C2
WHERE
C1.cust_name = C2.cust_name
AND C2.cust_contact = 'Jim Jones';
- 分析
此查询中需要的两个表实际上是相同的表,因此Customers表在FROM子句中出现了两次。虽然这是完全合法的,但对Customers的引用具有歧义性,因为DBMS不知道你引用的是哪个Customers表。
解决此问题,需要使用表别名。Customers第一次出现用了别名C1,第二次出现用了别名C2。现在可以将这些别名用作表名。例如,SELECT语句使用C1前缀明确给出所需列的全名。如果不这样,DBMS将返回错误,因为名为cust_id、cust_name、cust_contact的列各有两个。DBMS不知道想要的是哪一列(即使它们其实是同一列)。WHERE首先联结两个表,然后按第二个表中的cust_contact过滤数据,返回所需的数据。
提示:用自联结而不用子查询
自联结通常作为外部语句,用来替代从相同表中检索数据的使用子查询语句。虽然最终的结果是相同的,但许多DBMS处理联结远比处理子查询快得多。应该试一下两种方法,以确定哪一种的性能更好。
13.2.2 自然联结
- 自然联接的作用:
- 标准的联结(前一课中介绍的内联结)返回所有数据,相同的列甚至多次出现。自然联结排除多次出现,使每一列只返回一次。
- 如何实现自然联接:
- 自然联结要求你只能选择那些唯一的列,一般通过对一个表使用通配符(
SELECT *
),而对其他表的列使用明确的子集来完成。
- 自然联结要求你只能选择那些唯一的列,一般通过对一个表使用通配符(
SELECT
C.*, O.order_num,
O.order_date,
OI.prod_id,
OI.quantity,
OI.item_price
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';
实际上,我们建立的大多数内联接都是自然联接,可能用不到不是自然联接的内联接。
13.2.3 外联结(使用OUTER JOIN
来指定联结类型)
- 定义:联结包含了那些在相关表中没有关联行的行。
- 分类
- 左外联接(
LEFT OUTER JOIN
) - 右外联接(
RIGHT OUTER JOIN
) - 全外联接(
FULL OUTER JOIN
)
- 左外联接(
-- 输出Customers表的所有行
SELECT
Customers.cust_id,
Orders.order_num
FROM
Customers
LEFT OUTER JOIN Orders ON Customers.cust_id = Orders.cust_id;
- 在使用
OUTER JOIN
语法时,必须使用RIGHT
或LEFT
关键字指定包括其所有行的表(RIGHT
指出的是OUTER JOIN
右边的表,而LEFT
指出的是OUTER JOIN
左边的表)。 - 左外联结和右外联结的区别:唯一的区别是所关联的表的顺序。
- 全外联接:
SELECT
Customers.cust_id,
Orders.order_num
FROM
Orders
FULL OUTER JOIN Customers ON Orders.cust_id = Customers.cust_id;
全外链接检索两个表中的所有行并关联那些可以关联的行;
13.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;
13.4 使用联结和联结条件的总结
- 注意所使用的联结类型
- 一般我们使用内联结,但使用外联结也有效
- 关于确切的联结语法,应该查看具体的文档,看相应的DBMS支持何种语法
- 保证使用正确的联结条件(不管采用哪种语法),否则会返回不正确的数据。
- 应该总是提供联结条件,否则会得出笛卡儿积。
- 在一个联结中可以包含多个表,甚至可以对每个联结采用不同的联结类型。虽然这样做是合法的,一般也很有用,但应该在一起测试它们前分别测试每个联结。这会使故障排除更为简单。
第14课 组合查询
14.1 组合查询/复合查询(compound query)/并(union)
- 定义:执行多条
SELECT
语句,并将结果作为一个查询结果返回; - 使用场景:
- 在一个查询中从不同的表返回结构数据;
- 对一个表执行多个查询,按一个查询返回数据;
- 组合查询和多个where条件
- 多数情况下,组合相同表的两个查询所完成的工作与具有多个WHERE子句条件的一个查询所完成的工作相同。换句话说,任何具有多个WHERE子句的SELECT语句都可以作为一个组合查询。
- 利用UNION,可以把多条查询的结果作为一条组合查询返回,不管结果中有无重复。使用UNION可极大地简化复杂的WHERE子句,简化从多个表中检索数据的工作。
14.2 创建组合查询
只需要使用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';
ORDER BY cust_name, cust_contact;
等价的where
语句
SELECT
cust_name,
cust_contact,
cust_email
FROM
customers
WHERE
cust_state IN ('IL', 'IN', 'MI')
OR cust_name = 'Fun4All';
ORDER BY cust_name, cust_contact;
14.2.2 UNION使用规则
- 性能和多个where没有多大差别,具体需要测试;
-
UNION
从查询结果集中自动去除了重复的行,如果想返回所有匹配的行,使用UNION ALL
; - 在用UNION组合查询时,只能使用一条
ORDER BY
子句,它必须位于最后一条SELECT
语句之后;- 对于结果集,不存在用一种方式排序一部分,而又用另一种方式排序另一部分的情况,因此不允许使用多条ORDER BY子句;
- 使用规则
- UNION必须由两条或两条以上的SELECT语句组成,语句之间用关键字UNION分隔;
- UNION中的每个查询必须包含相同的列、表达式或聚集函数(各个列次序可不同);
- 列数据类型必须兼容:类型不必完全相同,但必须是DBMS可以隐含转换的类型;
第15课 插入数据
15.1.1插入部分行
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将产生错误消息,并且相应的行插入不成功;
15.1.3 插入检索出的数据
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
WHERE XXX;
说明:
INSERT INTO() SELECT() FROM table WHERE
- INSERT SELECT中的列名:
- 为简单起见,这个例子在INSERT和SELECT语句中使用了相同的列名。但是,不一定要求列名匹配。事实上,DBMS一点儿也不关
心SELECT返回的列名。它使用的是列的位置,因此SELECT中的第一列(不管其列名)将用来填充表列中指定的第一列,第二列将用来填充表列中指定的第二列;
- 为简单起见,这个例子在INSERT和SELECT语句中使用了相同的列名。但是,不一定要求列名匹配。事实上,DBMS一点儿也不关
- INSERT通常只插入一行。要插入多行,必须执行多个INSERT语句。INSERT SELECT是个例外,它可以用一条INSERT插入多行,不管SELECT语句返回多少行,都将被INSERT插入。
15.2 从一个表复制到另一个表(SELECT INTO
)
SELECT
* INTO CustCopy
FROM
Customers;
说明:
-
INSERT SELECT
与SELECT INTO
的区别-
INSERT SELECT
导出数据; -
SELECT INTO
导入数据;
-
- 要想只复制部分的列,可以明确给出列名,而不是使用*通配符;
- 任何SELECT选项和子句都可以使用,包括WHERE和GROUP BY;
- 可利用联结从多个表插入数据;
- 不管从多少个表中检索数据,数据都只能插入到一个表中。
-- 这个不懂
CREATE TABLE CustCopy AS SELECT * FROM Customers;
第16课 更新和删除数据
16.1 更新数据
- 更新的语法
UPDATE table
SET columnName1 ='xxxx',
columnName2 ='XXX',
columnName3 ='XXX'
WHERE columnName4 ='' AND columnName5 ='';
- 举例
UPDATE Customers
SET cust_contact = 'Sam Roberts',
cust_email = 'sam@toyland.com'
WHERE cust_id = '1000000006';
- 在
UPDATE
语句中使用子查询-
UPDATE
语句中可以使用子查询,使得能用SELECT
语句检索出的数据更新列数据。
-
16.2 删除数据
-
delete
是删除一整行数据,而不是一行的某几列数据,即删除的是表的内容,而不是表本身,删除表本身使用drop
; - 删除的语法
DELETE FROM table
WHERE columnName1 = '' AND columnName2 ='';
- 举例
DELETE FROM Customers
WHERE cust_id = '1000000006';
- 更快的删除行
如果想从表中删除所有行,不要使用DELETE
。可使用TRUNCATE TABLE
语句,它完成相同的工作,而速度更快(因为不记录数据的变
动) - 友好的外键(不懂)
第12课介绍了联结,简单联结两个表只需要这两个表中的常用字段。也可以让DBMS通过使用外键来严格实施关系。存在外键时,DBMS使用它们实施引用完整性。例如要向Products表中插入一个新产品,DBMS不允许通过未知的供应商id插入它,因为vend_id列是作为外键连接到Vendors表的。那么,这与DELETE有什么关系呢?使用外键确保引用完整性的一个好处是,DBMS通常可以防止删除某个关系需要用到的行。例如,要从Products表中删除一个产品,而这个产品用在OrderItems的已有订单中,那么DELETE语句将抛出错误并中止。这是总要定义外键的另一个理由。
16.3 更新和删除的指导原则
- 除非确实打算更新和删除每一行,否则绝对不要使用不带WHERE子句的UPDATE或DELETE语句。
- 保证每个表都有主键,尽可能像WHERE子句那样使用它(可以指定各主键、多个值或值的范围)。
- 在UPDATE或DELETE语句使用WHERE子句前,应该先用SELECT进行测试,保证它过滤的是正确的记录,以防编写的WHERE子句不正确。
- 使用强制实施引用完整性的数据库,这样DBMS将不允许删除其数据与其他表相关联的行。
- 有的DBMS允许数据库管理员施加约束,防止执行不带WHERE子句的UPDATE或DELETE语句。如果所采用的DBMS支持这个特性,应该使用它。
第17课 创建和操纵表
17.1 创建表
-- 使用default 给某一列指定默认值
-- primary key 指定主键
-- autoincrement 自增长
CREATE TABLE OrderItems IF NOT EXISTS
(
order_num INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
order_item INTEGER NOT NULL,
prod_id CHAR(10) NOT NULL,
quantity INTEGER NOT NULL DEFAULT 1,
item_price DECIMAL(8,2) NULL
);
说明:
- 不允许NULL值的列不接受没有列值的行,否则会报错。
- 在不指定NOT NULL时,多数DBMS认为指定的是NULL,但是最好加上NULL;
- 不要把
NULL
值与空字符串相混淆。NULL
值是没有值,不是空字符串。如果指定”(两个单引号,其间没有字符),这在NOT NULL
列中是允许的。空字符串是一个有效的值,它不是无值。NULL
值用关键字NULL
而不是空字符串指定;
17.2 更新表(ALERT TABLE)
- 使用
alert table
时要考虑的问题- 理想情况下,不要在表中包含数据时对其进行更新。应该在表的设计过程中充分考虑未来可能的需求,避免今后对表的结构做大改动。
- 所有的DBMS都允许给现有的表增加列,不过对所增加列的数据类型(以及NULL和DEFAULT的使用)有所限制。
- 许多DBMS不允许删除或更改表中的列。
- 多数DBMS允许重新命名表中的列。
- 许多DBMS限制对已经填有数据的列进行更改,对未填有数据的列几乎没有限制。
- 使用ALTER TABLE更改表结构,必须给出下面的信息:
- 在
ALTER TABLE
之后给出要更改的表名(该表必须存在,否则将出错); - 列出要做哪些更改。
- 在
- 增加、更改、删除列、增加约束、增加键的做法都是类似的
ALTER TABLE Vendors
ADD vend_phone CHAR(20);
ALTER TABLE Vendors
DROP COLUMN vend_phone;
- 复杂的表结构更改一般需要手动删除过程,它涉及以下步骤(不懂)
- 用新的列布局创建一个新表;
- 使用INSERT SELECT语句,从旧表复制数据到新表。有必要的话,可以使用转换函数和计算字段;
- 检验包含所需数据的新表;
- 重命名旧表(如果确定,可以删除它);
- 用旧表原来的名字重命名新表;
- 根据需要,重新创建触发器、存储过程、索引和外键。
- 使用
ALTER TABLE
要极为小心,应该在进行改动前做完整的备份(模式和数据的备份),数据库的操作大多数都不可逆。
17.3 删除表
-- 就是这么简单
DROP TABLE CustCopy;
提示:使用关系规则防止意外删除
许多DBMS允许强制实施有关规则,防止删除与其他表相关联的表。在实施这些规则时,如果对某个表发布一条DROP TABLE语句,且该表
是某个关系的组成部分,则DBMS将阻止这条语句执行,直到该关系被删除为止。如果允许,应该启用这些选项,它能防止意外删除有用的
表。
17.4 重命名表
不同的DBMS对重命名完全不一样。参照具体的文档。
第18课 使用视图
18.1 视图
- 定义:视图是虚拟的表,包含的不是数据而是根据需要检索数据的查询,即将select语句封装起来。
- 作用:
- 简化数据处理,使用表的一部分而不是整个表;
- 提高sql语句的复用性,简化复杂的sql操作;
- 重新格式化数据,视图可返回与底层表的表示和格式不同的数据;
- 保护基础数据,可以授予用户访问表的特定部分的权限,而不是整个表的访问权限;
- 试图创建好之后,可以像正常的表一样操作他们,可以对视图执行
SELECT操作
,过滤
和排序
数据,将视图联结
到其他视图
或表
,甚至添加
和更新
数据。 - 试图的规则和限制
- 视图的名字必须唯一(与其他视图和表名都不能重复);
- 可以创建无数多个视图;
- 创建视图,必须具有足够的访问权限。这些权限通常由数据库管理人员授予。
- 视图可以嵌套,即视图里面含有视图,但是性能下降会很厉害,使用时需要测试;
- 许多DBMS禁止在视图查询中使用ORDER BY子句;
- 有些DBMS要求对返回的所有列进行命名,如果列是计算字段,则需要使用别名;
- 视图不能索引,也不能有关联的触发器或默认值;
- 有些DBMS把视图作为只读的查询,这表示可以从视图检索数据,但不能将数据写回底层表;
- 有些DBMS允许创建这样的视图,它不能进行导致行不再属于视图的插入或更新;(例如有一个视图,只检索带有电子邮件地址的顾客。如果更新某个顾客,删除他的电子邮件地址,将使该顾客不再属于视图。这是默认行为,而且是允许的,但有的DBMS可能会防止这种情况发生。)
18.2 创建视图
-- 创建视图,只能用来创建不存在的视图
CREATE VIEW view_name AS SELECT
-- 删除视图
DROP VIEW view_name;
- 更新视图,必须先删除它,然后重新创建;
- 视图的创建与使用举例
-- 创建视图 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; --使用视图,当DBMS处理此查询时, -- 它将指定的WHERE子句添加到视图查询中 -- 已有的WHERE子句中,以便正确过滤数据。 SELECT cust_name, cust_contact FROM ProductCustomers WHERE prod_id = 'RGAN01';
- 如何创建视图
- 先把select语句创建写出来;
- 然后把创建视图的语句加上;
- select语句有什么功能,视图就有什么功能,比如在视图中过滤不想要的数据,在试图中创建计算字段;
-- 用视图过滤不想要的数据 CREATE VIEW CustomerEMailList AS SELECT cust_id, cust_name, cust_email FROM Customers WHERE cust_email IS NOT NULL; -- 使用视图与计算字段 CREATE VIEW OrderItemsExpanded AS SELECT order_num, prod_id, quantity, item_price, quantity*item_price AS expanded_price FROM OrderItems;
第19课 使用存储过程
存储过程就是为以后使用而保存的一条或多条SQL语句。可将其视为批文件,但它们的作用不仅限于批处理。
19.2 为什么要使用存储过程
1. 存储过程的好处:简单
、安全
、高性能
。
- 通过把处理封装在一个易用的单元中,可以简化复杂的操作;
- 由于不要求反复建立一系列处理步骤,因而保证了数据的一致性(防止错误);
- 简化对变动的管理。如果表名、列名或业务逻辑(或别的内容)有变化,那么只需要更改存储过程的代码(保证安全性);
- 因为存储过程通常以编译过的形式存储,所以DBMS处理命令的工作较少,提高了性能;
- 存在一些只能用在单个请求中的SQL元素和特性,存储过程可以使用它们来编写功能更强更灵活的代码;
2. 存储过程的不足
- 不同DBMS中的存储过程语法有所不同,代码不具有可可移植性;
- 编写存储过程比编写基本SQL语句复杂,需要更高的技能,更丰富的经验;
19.3 执行存储过程
-- orcale版本的存储过程
-- 语法:EXECUTE <存储过程名称> <参数>
-- 利用存储过程插入一条数据
EXECUTE AddNewProduct( 'JTS01',
'Stuffed Eiffel Tower',
6.49,
'Plush stuffed toy with the text La
tour Eiffel in red white and blue' );
19.4 创建存储过程
-- orcale 版本
-- 功能:对邮件发送清单中具有邮件地址的顾客进行计数
CREATE PROCEDURE MailingListCount (
ListCount OUT INTEGER
)
IS v_rows INTEGER;
BEGIN
SELECT COUNT(*) INTO v_rows
FROM Customers
WHERE NOT cust_email IS NULL;
ListCount := v_rows;
END;
分析:
- 这个存储过程有一个名为
ListCount
的参数。此参数从存储过程返回一个值而不是传递一个值给存储过程。关键字OUT用来指示这种行为。- 存储过程的代码括在
BEGIN
和END
语句中,这里执行一条简单的SELECT
语句,它检索具有邮件地址的顾客。然后用检索出的行数设置ListCount(要传递的输出参数)
Oracle支持
- IN(传递值给存储过程);
- OUT(从存储过程返回值);
- INOUT(既传递值给存储过程也从存储过程传回值)类型的参数;
调用存储过程
-- 这段代码声明了一个变量来保存存储过程返回的任何
-- 值,然后执行存储过程,再使用SELECT语句显示返回的值
var ReturnValue NUMBER
EXEC MailingListCount(:ReturnValue);
SELECT ReturnValue;
第20课 管理事务处理
20.1 事务处理
1. 使用事务的目的
使用事务处理
(transaction processing),通过确保成批的SQL操作要么完全执行,要么完全不执行,来维护数据库的完整性。
2. 什么是事务处理
事务处理
是一种机制
,用来管理必须成批执行的SQL操作,保证数据库不包含不完整的操作结果。利用事务处理,可以保证一组操作不会中途停止,它们要么完全执行,要么完全不执行(除非明确指示)。如果没有错误发生,整组语句提交给(写到)数据库表;如果发生错误,则进行回退(撤销),将数据库恢复到某个已知且安全的状态。
3. 几个概念
- 事务(transaction):指一组SQL语句;
- 回退(rollback):指撤销指定SQL语句的过程;
- 提交(commit):指将未存储的SQL语句结果写入数据库表;
- 保留点(savepoint):指事务处理中设置的临时占位符(placeholder),可以对它发布回退(与回退整个事务处理不同)
4. 不能回退的语句
- 事务处理用来管理
INSERT
、UPDATE
和DELETE
语句; - 不能回退
SELECT
,DROP
,CREATE
语句;
20.2 控制事务处理
管理事务的关键在于将SQL语句组分解为逻辑块,并明确规定数据何时应该回退,何时不应该回退
-- BEGIN TRANSACTION和COMMIT TRANSACTION语句之间的SQL
-- 必须完全执行或者完全不执行
-- 不同的DBMS不一样
BEGIN TRANSACTION
...
COMMIT TRANSACTION
1.使用ROLLBACK
--执行DELETE操作,然后用ROLLBACK语句撤销
DELETE FROM Orders;
ROLLBACK;
2.使用COMMIT
一般的SQL语句都是针对数据库表直接执行和编写的。这就是所谓的隐式提交(implicit commit),即提交(写或保存、删除)操作是自动进行的。在事务处理块中,提交不会隐式进行。不过,不同DBMS的做法有所不同。有的DBMS按隐式提交处理事务端,有的则不这样。
-- SQL Server使用commit进行(明确)提交
BEGIN TRANSACTION
DELETE OrderItems WHERE order_num = 12345
DELETE Orders WHERE order_num = 12345
COMMIT TRANSACTION
-- Oracle使用commit
SET TRANSACTION
DELETE OrderItems WHERE order_num = 12345;
DELETE Orders WHERE order_num = 12345;
COMMIT;
3.使用保留点
1.使用保留点的原因
- 使用简单的
ROLLBACK
和COMMIT
语句,就可以写入或撤销整个事务。但是,只对简单的事务才能这样做,复杂的事务可能需要部分提交或回退; - 要支持回退部分事务,必须在事务处理块中的合适位置放置占位符。这样,如果需要回退,可以回退到某个占位符;在SQL中,这些占位符称为保留点。
- 每个保留点都要取能够标识它的唯一名字,以便在回退时,DBMS知道回退到何处
-- MariaDB、MySQL和Oracle
SAVEPOINT delete1;
ROLLBACK TO delete1;
-- SQL Server
SAVE TRANSACTION delete1;
ROLLBACK TRANSACTION delete1;
-- 一个完整的SQL Server例子
BEGIN TRANSACTION
INSERT INTO Customers(cust_id, cust_name)
VALUES('1000000010', 'Toys Emporium');
-- 这里设置了一个保留点,如果后面的任何一个INSERT操作失败,
-- 事务处理最近回退到这里
SAVE TRANSACTION StartOrder;
INSERT INTO Orders(order_num, order_date, cust_id)
VALUES(20100,'2001/12/1','1000000010');
-- 在SQL Server中,可检查一个名为@@ERROR的变量,看操作是否成功
-- 如果@@ERROR返回一个非0的值,表示有错误发生,
-- 事务处理回退到保留点
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课 使用游标
21.1 游标
- 结果集:SQL查询所检索出的结果
- SELECT操作返回一组称为结果集的行,这组返回的行都是与SQL语句相匹配的行(零行或多行)。
- 游标
游标(cursor)是一个存储在DBMS服务器上的数据库查询,它不是一条SELECT语句,而是被该语句检索出来的结果集;可以在检索出来的行中前进或后退一行或多行。
21.2 使用游标
使用步骤
- 在使用游标前,必须声明(定义)它。这个过程实际上没有检索数据,它只是定义要使用的
SELECT
语句和游标选项。 - 一旦声明,就必须打开游标以供使用。这个过程用前面定义的
SELECT
语句把数据实际检索出来。 - 对于填有数据的游标,根据需要取出(检索)各行。
- 在结束游标使用时,必须关闭游标,可能的话,释放游标(有赖于具体的DBMS)。
1、创建游标
使用DECLARE
语句创建游标,DECLARE
命名游标,并定义相应的SELECT
语句,根据需要带WHERE
和其他子句。
-- DB2、MariaDB、MySQL和SQL Server
DECLARE CustCursor CURSOR
FOR
SELECT * FROM Customers
WHERE cust_email IS NULL
-- Oracle和PostgreSQL
DECLARE CURSOR CustCursor
IS
SELECT * FROM Customers
WHERE cust_email IS NULL
2、使用游标
-- 打开游标
OPEN CURSOR CustCursor
-- oracle
-- FETCH用来检索当前行(自动从第一行开始),
-- 放到声明的变量CustRecord中。
-- 对于检索出来的数据不做任何处理
DECLARE TYPE CustCursor IS REF CURSOR
RETURN Customers%ROWTYPE;
DECLARE CustRecord Customers%ROWTYPE
BEGIN
OPEN CustCursor;
FETCH CustCursor INTO CustRecord;
CLOSE CustCursor;
END;
-- 使用FETCH检索当前行,放到一个名为CustRecord的变量中
DECLARE TYPE CustCursor IS REF CURSOR
RETURN Customers%ROWTYPE;
DECLARE CustRecord Customers%ROWTYPE
BEGIN
OPEN CustCursor;
LOOP
-- 这里的FETCH位于LOOP内,因此它反复执行。
-- 代码EXIT WHEN CustCursor%NOTFOUND
-- 使在取不出更多的行
-- 时终止处理(退出循环)
FETCH CustCursor INTO CustRecord;
EXIT WHEN CustCursor%NOTFOUND;
...
END LOOP;
-- 关闭游标
CLOSE CustCursor;
END;
第22课 高级SQL特性
22.1 约束
约束(constraint): 管理如何插入或处理数据库数据的规则。
1.1 主键
- 主键是一种特殊的约束,用来保证一列(或一组列)中的值是唯一的,而且永不改动。
- 表中任意列只要满足以下条件,都可以用于主键:
- 任意两行的主键值都不相同。
- 每行都具有一个主键值(即列中不允许NULL值)。
- 包含主键值的列从不修改或更新。(大多数DBMS不允许这么做,但如果你使用的DBMS允许这样做,好吧,千万别!)
- 主键值不能重用。如果从表中删除某一行,其主键值不分配给新行
CREATE TABLE Vendors
(
--通过primary key来指定主键
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
);
-- 这里定义相同的列为主键,但使用的是CONSTRAINT语法
ALTER TABLE Vendors
ADD CONSTRAINT PRIMARY KEY (vend_id);
1.2 外键
CREATE TABLE Orders
(
order_num INTEGER NOT NULL PRIMARY KEY,
order_date DATETIME NOT NULL,
-- 添加外键
-- 使用了REFERENCES关键字,它表示cust_id中的
-- 任何值都必须是Customers表的cust_id中的值
cust_id CHAR(10) NOT NULL REFERENCES Customers(cust_id)
);
ALTER TABLE Orders
ADD CONSTRAINT
FOREIGN KEY (cust_id) REFERENCES Customers (cust_id)
提示:外键有助防止意外删除
外键的作用
- 保证引用完整性;
- 在定义外键后,DBMS不允许删除在另一个表中具有关联行的行;
例如,不能删除关联订单的顾客。删除该顾客的唯一方法是首先删除相关的订单(这表示还要删除相关的订单项)。由于需要一系列的删除,
因而利用外键可以防止意外删除数据。有的DBMS支持称为级联删除(cascading delete)的特性。如果启用,该特性在从一个表中删除行时删除所有相关的数据。例如,如果启用级联删除并且从Customers表中删除某个顾客,则任何关联的订单行也会被自动删除。
1.3 唯一约束
唯一约束用来保证一列(或一组列)中的数据是唯一的。它们类似于主键,但存在以下重要区别。
- 表可包含多个唯一约束,但每个表只允许一个主键。
- 唯一约束列可包含NULL值。
- 唯一约束列可修改或更新。
- 唯一约束列的值可重复使用。
- 与主键不一样,唯一约束不能用来定义外键
语法:唯一约束既可以用
UNIQUE
关键字在表定义中定义,也可以用单独的CONSTRAINT
定义
employees表中每个雇员都有唯一的社会安全号,但我们并不想用它作主键,因为它太长(而且我们也不想使该信息容易利用)。因此,每个雇员除了其社会安全号外还有唯一的雇员ID(主键)。
雇员ID是主键,可以确定它是唯一的。你可能还想使DBMS保证每个社会安全号也是唯一的(保证输入错误不会导致使用他人号码)。可以通过在社会安全号列上定义
UNIQUE
约束做到。
1.4 检查约束
- 检查约束用来保证一列(或一组列)中的数据满足一组指定的条件。检查约束的常见用途有以下几点。
- 检查最小或最大值。例如,防止0个物品的订单(即使0是合法的数)。
- 指定范围。例如,保证发货日期大于等于今天的日期,但不超过今天起一年后的日期。
- 只允许特定的值。例如,在性别字段中只允许M或F。
CREATE TABLE OrderItems
(
order_num INTEGER NOT NULL,
order_item INTEGER NOT NULL,
prod_id CHAR(10) NOT NULL,
-- 利用这个约束,任何插入(或更新)的行都会被检查,保证quantity大于0。
quantity INTEGER NOT NULL CHECK (quantity > 0),
item_price MONEY NOT NULL
);
-- 检查名为gender的列只包含M或F,可编写如下的ALTER TABLE语句
ADD CONSTRAINT CHECK (gender LIKE '[MF]');
22.2 索引
- 索引用来排序数据以加快搜索和排序操作的速度,类似于书的目录;
- 可以在一个或多个列上定义索引,使DBMS保存其内容的一个排过序的列表。
- 关于索引的几点认识
- 索引改善检索操作的性能,但降低了数据插入、修改和删除的性能。在执行这些操作时,DBMS必须动态地更新索引。
- 索引数据可能要占用大量的存储空间。
- 并非所有数据都适合做索引。取值不多的数据(如州)不如具有更多可能值的数据(如姓或名),能通过索引得到那么多的好处。
- 索引用于数据过滤和数据排序。如果你经常以某种特定的顺序排序数据,则该数据可能适合做索引。
- 可以在索引中定义多个列(例如,州加上城市)。这样的索引仅在以州加城市的顺序排序时有用。如果想按城市排序,则这种索引没有用处。
-- 在Products表的产品名列上创建一个简单的索引
-- 索引必须唯一命名
CREATE INDEX prod_name_ind
ON PRODUCTS (prod_name);
- 索引的效率随表数据的增加或改变而变化,定期检查索引,并根据需要对索引进行调整;
22.3 触发器
- 触发器是特殊的存储过程,它在特定的数据库活动发生时自动执行;
- 触发器可以与特定表上的INSERT、UPDATE和DELETE操作(或组合)相关联。
- 触发器与单个的表相关联(存储过程可以关联多张表,存储过程只是简单的存储SQL语句)。与Orders表上的INSERT操作相关联的触发器只在Orders表中插入行时执行;
- 触发器内的代码具有的权限
-
INSERT
操作中的所有新数据; -
UPDATE
操作中的所有新数据和旧数据; -
DELETE
操作中删除的数据。
-
- 触发器的一些常见用途
- 保证数据一致。例如,在INSERT或UPDATE操作中将所有州名转换为大写。
- 基于某个表的变动在其他表上执行活动。例如,每当更新或删除一行时将审计跟踪记录写入某个日志表。
- 进行额外的验证并根据需要回退数据。例如,保证某个顾客的可用资金不超限定,如果已经超出,则阻塞插入。
- 计算计算列的值或更新时间戳。
-- 对所有INSERT和UPDATE操作,将Customers表中的cust_state列转换为大写
-- SQL Server
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;
-- Oracle和PostgreSQL
CREATE TRIGGER customer_state
AFTER INSERT OR UPDATE
FOR EACH ROW
BEGIN
UPDATE Customers
SET cust_state = Upper(cust_state)
WHERE Customers.cust_id = :OLD.cust_id
END;
- 约束的处理比触发器快,因此在可能的时候,应该尽量使用约束。
22.4 数据库安全
- 使用用户名用户密码来保证系统安全。
- 一般说来,需要保护的操作有:
- 对数据库管理功能(创建表、更改或删除已存在的表等)的访问;
- 对特定数据库或表的访问;
- 访问的类型(只读、对特定列的访问等);
- 仅通过视图或存储过程对表进行访问;
- 创建多层次的安全措施,从而允许多种基于登录的访问和控制;
- 限制管理用户账号的能力。
- 安全性使用SQL的
GRANT
和REVOKE
语句来管理,交互式管理程序在内部使用GRANT
和REVOKE
语句;