SQL必知必会(第4版)

Ben Forta

引言

  • 代码行前的箭头(➥)表示代码太长,上一行容纳不下。在➥符号后输入的所有字符都应该是前一行的内容。

第1课 了解SQL

  • 理解数据库概念是掌握SQL的重要前提,
  • 数据库是一个以某种有组织的方式存储的数据集合。
  • 描述表的这组信息就是所谓的模式(schema),模式可以用来描述数据库中特定的表,也可以用来描述整个数据库(和其中表的关系)。
  • 正确地将数据分解为多个列极为重要。
  • 数据类型(datatype)定义了列可以存储哪些数据种类。
  • 数据类型所允许的数据的类型。每个表列都有相应的数据类型,它限制(或允许)该列中存储的数据。
  • 表中的任何列都可以作为主键,只要它满足以下条件: □ 任意两行都不具有相同的主键值; □ 每一行都必须具有一个主键值(主键列不允许NULL值); □ 主键列中的值不允许修改或更新; □ 主键值不能重用(如果某行从表中删除,它的主键不能赋给以后的新行)。
  • SQL(发音为字母S-Q-L或sequel)是Structured Query Language(结构化查询语言)的缩写。SQL是一种专门用来与数据库沟通的语言。
  • 标准SQL由ANSI标准委员会管理,从而称为ANSI SQL。所有主要的DBMS,即使有自己的扩展,也都支持ANSI SQL。

第2课 检索数据

  • 为了使用SELECT检索表数据,必须至少给出两条信息——想选择什么,以及从什么地方选择。
  • SELECT prod_name
FROM Products;
  • 如果没有明确排序查询结果(下一课介绍),则返回的数据没有特定的顺序。
  • 在选择多个列时,一定要在列名之间加上逗号,但最后一个列名后不加。如果在最后一个列名后加了逗号,将出现错误。
  • 如果给定一个通配符(*),则返回表中所有列。
  • 一般而言,除非你确实需要表中的每一列,否则最好别使用*通配符。虽然使用通配符能让你自己省事,不用明确列出所需列,但检索不需要的列通常会降低检索和应用程序的性能。
  • 办法就是使用DISTINCT关键字,顾名思义,它指示数据库只返回不同的值。
  • DISTINCT关键字作用于所有的列,不仅仅是跟在其后的那一列。
  • 第一个被检索的行是第0行,而不是第1行。因此,LIMIT 1 OFFSET 1会检索第2行,而不是第1行。
  • 注释使用--(两个连字符)嵌在行内。-- 之后的文本就是注释

第3课 排序检索数据

  • ORDER BY子句取一个或多个列的名字,据此对输出进行排序。
  • 在指定一条ORDER BY子句时,应该保证它是SELECT语句中最后一条子句。如果它不是最后的子句,将会出现错误消息。
  • 除了能用列名指出排序顺序外,ORDER BY还支持按相对列位置进行排序。
  • 为了进行降序排序,必须指定DESC关键字。
  • SELECT prod_id, prod_price, prod_nameFROM ProductsORDER BY prod_price DESC, prod_name;
  • DESC关键字只应用到直接位于其前面的列名。
  • 这个子句必须是SELECT语句中的最后一条子句

第4课 过滤数据

  • 只检索所需数据需要指定搜索条件(search criteria),搜索条件也称为过滤条件(filter condition)。
  • 在同时使用ORDER BY和WHERE子句时,应该让ORDER BY位于WHERE之后,否则将会产生错误
  • 单引号用来限定字符串。如果将值与字符串类型的列进行比较,就需要限定引号。用来与数值列进行比较的值不用引号。
  • !=和<>通常可以互换。但是,并非所有DBMS都支持这两种不等于操作符。例如,Microsoft Access支持<>而不支持!=。如果有疑问,请参阅相应的DBMS文档。
  • 在使用BETWEEN时,必须指定两个值——所需范围的低端值和高端值。这两个值必须用AND关键字分隔。BETWEEN匹配范围中所有的值,包括指定的开始值和结束值。
  • 过滤数据时,一定要验证被过滤列中含NULL的行确实出现在返回的数据中。

第5课 高级数据过滤

  • 操作符(operator)用来联结或改变WHERE子句中的子句的关键字,也称为逻辑操作符(logical operator)。
  • AND用在WHERE子句中的关键字,用来指示检索满足所有给定条件的行。
  • 许多DBMS在OR WHERE子句的第一个条件得到满足的情况下,就不再计算第二个条件了(在第一个条件满足时,不管第二个条件是否满足,相应的行都将被检索出来)。
  • SQL(像多数语言一样)在处理OR操作符前,优先处理AND操作符。
  • IN操作符用来指定条件范围,范围中的每个条件都可以进行匹配。IN取一组由逗号分隔、括在圆括号中的合法值。
  • IN操作符完成了与OR相同的功能
  • IN的最大优点是可以包含其他SELECT语句,能够更动态地建立WHERE子句。
  • 但在更复杂的子句中,NOT是非常有用的。例如,在与IN操作符联合使用时,NOT可以非常简单地找出与条件列表不匹配的行。

第6课 用通配符进行过滤

  • 利用通配符,可以创建比较特定数据的搜索模式。
  • 通配符搜索只能用于文本字段(字符串),非文本数据类型字段不能使用通配符搜索。
  • 如果使用的是Microsoft Access,需要使用*而不是%。
  • 另一个有用的通配符是下划线(_)。下划线的用途与%一样,但它只匹配单个字符,而不是多个字符。
  • 8 inch teddy bear产品没有匹配,因为搜索模式要求匹配两个通配符而不是一个。
  • 方括号([])通配符用来指定一个字符集,它必须匹配指定位置(通配符的位置)的一个字符。
  • SELECT cust_contact
FROM Customers
WHERE cust_contact LIKE '[JM]%'
ORDER BY cust_contact;
  • 此通配符可以用前缀字符^(脱字号)来否定。

第7课 创建计算字段

  • 存储在数据库表中的数据一般不是应用程序所需要的格式
  • 计算字段并不实际存在于数据库表中。计算字段是运行时在SELECT语句内创建的。
  • 在SQL中的SELECT语句中,可使用一个特殊的操作符来拼接两个列。根据你所使用的DBMS,此操作符可用加号(+)或两个竖杠(||)表示。在MySQL和MariaDB中,必须使用特殊的函数。
  • SELECT Concat(vend_name, ' (', vend_country, ')')
FROM Vendors
ORDER BY vend_name;
  • 为正确返回格式化的数据,必须去掉这些空格。这可以使用SQL的RTRIM()函数来完成
  • 大多数DBMS都支持RTRIM()(正如刚才所见,它去掉字符串右边的空格)、LTRIM()(去掉字符串左边的空格)以及TRIM()(去掉字符串左右两边的空格)。
  • 在很多DBMS中,AS关键字是可选的,不过最好使用它,这被视为一条最佳实践。
  • SELECT prod_id, quantity, item_price, quantity*item_price AS expanded_priceFROM OrderItemsWHERE order_num = 20008;
  • 输出中显示的expanded_price列是一个计算字段,此计算为quantity*item_price。客户端应用现在可以使用这个新计算列,就像使用其他列一样
  • SELECT语句为测试、检验函数和计算提供了很好的方法。虽然SELECT通常用于从表中检索数据,但是省略了FROM子句后就是简单地访问和处理表达式,例如SELECT 3 * 2;将返回6, SELECT Trim(' abc ');将返回abc, SELECT Now();使用Now()函数返回当前日期和时间。现在你明白了,可以根据需要使用SELECT语句进行检验。

第8课 使用函数处理数据

  • 与SQL语句不一样,SQL函数不是可移植的。
  • 如果你决定使用函数,应该保证做好代码注释,以便以后你(或其他人)能确切地知道所编写的SQL代码的含义。
  • 大多数SQL实现支持以下类型的函数。□ 用于处理文本字符串(如删除或填充值,转换值为大写或小写)的文本函数。□ 用于在数值数据上进行算术操作(如返回绝对值,进行代数运算)的数值函数。□ 用于处理日期和时间值并从这些值中提取特定成分(如返回两个日期之差,检查日期有效性)的日期和时间函数。
  • UPPER()将文本转换为大写
  • SOUNDEX是一个将任何文本串转换为描述其语音表示的字母数字模式的算法。SOUNDEX考虑了类似的发音字符和音节,使得能对字符串进行发音比较而不是字母比较。
  • 日期和时间值以特殊的格式存储,以便能快速和有效地排序或过滤,并且节省物理存储空间。
  • DATEPART()函数,顾名思义,此函数返回日期的某一部分。DATEPART()函数有两个参数,它们分别是返回的成分和从中返回成分的日期。
  • MySQL和MariaDB用户可使用名为YEAR()的函数从日期中提取年份:
  • 表8-3 常用数值处理函数

第9课 汇总数据

  • □ 确定表中行数(或者满足某个条件或包含某个特定值的行数);□ 获得表中某些行的和;□ 找出表列(或所有行或某些特定的行)的最大值、最小值、平均值。
  • 表9-1 SQL聚集函数[插图]
  • AVG()可用来返回所有列的平均值,也可以用来返回特定列或行的平均值。
  • 使用COUNT(*)对表中行的数目进行计数,不管表列中包含的是空值(NULL)还是非空值。 □ 使用COUNT(column)对特定列中具有值的行进行计数,忽略NULL值。
  • 如果指定列名,则COUNT()函数会忽略指定列的值为空的行,但如果COUNT()函数中用的是星号(*),则不忽略。
  • 在用于文本数据时,MAX()返回按该列排序后的最后一行。
  • 在用于文本数据时,MIN()返回该列排序后最前面的行。
  • SUM()函数忽略列值为NULL的行。
  • 只包含不同的值,指定DISTINCT参数。
  • 如果指定列名,则DISTINCT只能用于COUNT()。DISTINCT不能用于COUNT(*)。类似地,DISTINCT必须使用列名,不能用于计算或表达式。
  • 在指定别名以包含某个聚集函数的结果时,不应该使用表中实际的列名。虽然这样做也算合法,但许多SQL实现不支持,可能会产生模糊的错误消息。
  • 这些函数很高效,它们返回结果一般比你在自己的客户端应用程序中计算要快得多。

第10课 分组数据

  • GROUPBY子句和HAVING子句。
  • GROUP BY子句必须出现在WHERE子句之后,ORDER BY子句之前。
  • 唯一的差别是,WHERE过滤行,而HAVING过滤分组。
  • 使用HAVING时应该结合GROUP BY子句,而WHERE子句用于标准的行级过滤。
  • 表10-1 ORDER BY与GROUP BY
  • 一般在使用GROUP BY子句时,应该也给出ORDER BY子句。这是保证数据正确排序的唯一方法。千万不要仅依赖GROUP BY排序数据。
  • Microsoft Access不允许按别名排序,因此这个例子在Access中将失败。解决方法是用实际的计算或字段位置替换items(在ORDER BY子句中),即ORDER BY COUNT(*), order_num或ORDER BY 2, order_num。
  • 表10-2 SELECT子句及其顺序

第11课 使用子查询

  • SQL还允许创建子查询(subquery),即嵌套在其他查询中的查询
  • 可以把一条SELECT语句返回的结果用于另一条SELECT语句的WHERE子句。
  • 在SELECT语句中,子查询总是从内向外处理。
  • 把子查询分解为多行并进行适当的缩进,能极大地简化子查询的使用。
  • 作为子查询的SELECT语句只能查询单个列。企图检索多个列将返回错误。
  • 使用子查询的另一方法是创建计算字段。
  • SELECT cust_name, cust_state, (SELECT COUNT(*) FROM Orders WHERE Orders.cust_id = Customers.cust_id) AS ordersFROM CustomersORDER BY cust_name;
  • 用一个句点分隔表名和列名,在有可能混淆列名时必须使用这种语法。
  • 如果在SELECT语句中操作多个表,就应使用完全限定列名来避免歧义。
  • 子查询常用于WHERE子句的IN操作符中,以及用来填充计算列。

第12课 联结表

  • SQL最强大的功能之一就是能在数据查询的执行中联结(join)表。
  • 关系表的设计就是要把信息分解成多个表,一类数据一个表。各表通过某些共同的值互相关联(所以才叫关系数据库)。
  • 可伸缩(scale)能够适应不断增加的工作量而不失败。设计良好的数据库或应用程序称为可伸缩性好(scale well)。
  • 联结是一种机制,用来在一条SELECT语句中关联表,因此称为联结。
  • SELECT vend_name, prod_name, prod_price
FROM Vendors, Products
WHERE Vendors.vend_id = Products.vend_id;
  • 笛卡儿积(cartesian product)由没有联结条件的表关系返回的结果为笛卡儿积。检索出的行的数目将是第一个表中的行数乘以第二个表中的行数。
  • 目前为止使用的联结称为等值联结(equijoin),它基于两个表之间的相等测试。这种联结也称为内联结(inner join)。
  • 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;
  • 不要联结不必要的表。联结的表越多,性能下降越厉害。
  • 联结是SQL中一个最重要、最强大的特性,有效地使用联结需要对关系数据库设计有基本的了解。

第13课 创建高级联结

  • SQL除了可以对列名和计算字段使用别名,还允许给表名起别名。这样做有两个主要理由:□ 缩短SQL语句;□ 允许在一条SELECT语句中多次使用相同的表。
  • 表别名只在查询执行中使用。与列别名不一样,表别名不返回到客户端。
  • 自联结(self-join)、自然联结(natural join)和外联结(outer join)。
  • 使用表别名的一个主要原因是能在一条SELECT语句中不止一次引用相同的表
  • 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';
  • 自然联结排除多次出现,使每一列只返回一次。
  • 联结包含了那些在相关表中没有关联行的行。这种联结称为外联结。
  • 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左边的表)。
  • SQLite支持LEFT OUTER JOIN,但不支持RIGHT OUTER JOIN。
  • 全外联结(full outer join),它检索两个表中的所有行并关联那些可以关联的行。与左外联结或右外联结包含一个表的不关联的行不同,
  • 应该总是提供联结条件,否则会得出笛卡儿积。

第14课 组合查询

  • 主要有两种情况需要使用组合查询: □ 在一个查询中从不同的表返回结构数据; □ 对一个表执行多个查询,按一个查询返回数据。
  • 可用UNION操作符来组合数条SQL查询。利用UNION,可给出多条SELECT语句,将它们的结果组合成一个结果集。
  • SELECT cust_name, cust_contact, cust_emailFROM CustomersWHERE cust_state IN ('IL', 'IN', 'MI')UNIONSELECT cust_name, cust_contact, cust_emailFROM CustomersWHERE cust_name = 'Fun4All';
  • UNION指示DBMS执行这两条SELECT语句,并把输出组合成一个查询结果集。
  • 在这个简单的例子中,使用UNION可能比使用WHERE子句更为复杂。但对于较复杂的过滤条件,或者从多个表(而不是一个表)中检索数据的情形,使用UNION可能会使处理更简单。
  • UNION中的每个查询必须包含相同的列、表达式或聚集函数(不过,各个列不需要以相同的次序列出)。
  • UNION从查询结果集中自动去除了重复的行
  • 使用UNION ALL, DBMS不取消重复的行。
  • 在用UNION组合查询时,只能使用一条ORDER BY子句,它必须位于最后一条SELECT语句之后。
  • 虽然ORDER BY子句似乎只是最后一条SELECT语句的组成部分,但实际上DBMS将用它来排序所有SELECT语句返回的所有结果。
  • 不管结果中有无重复。使用UNION可极大地简化复杂的WHERE子句,简化从多个表中检索数据的工作。

第15课 插入数据

  • 插入有几种方式:□ 插入完整的行;□ 插入行的一部分;□ 插入某些查询的结果。
  • 使用INSERT语句可能需要客户端/服务器DBMS中的特定安全权限。在你试图使用INSERT前,应该保证自己有足够的安全权限。
  • 各列必须以它们在表定义中出现的次序填充。
  • 因为提供了列名,VALUES必须以其指定的次序匹配指定的列名,不一定按各列出现在表中的实际次序。其优点是,即使表的结构改变,这条INSERT语句仍然能正确工作。
  • 不要使用没有明确给出列的INSERT语句。给出列能使SQL代码继续发挥作用,即使表结构发生了变化。
  • 如果表的定义允许,则可以在INSERT操作中省略某些列。省略的列必须满足以下某个条件。□ 该列定义为允许NULL值(无值或空值)。□ 在表定义中给出默认值。这表示如果不给出值,将使用默认值。
  • INSERT通常只插入一行。要插入多行,必须执行多个INSERT语句。INSERTSELECT是个例外,它可以用一条INSERT插入多行,不管SELECT语句返回多少行,都将被INSERT插入。
  • 说明:INSERT SELECT与SELECT INTO它们之间的一个重要差别是前者插入数据,而后者导出数据。

第16课 更新和删除数据

  • □ 更新表中的特定行;□ 更新表中的所有行。
  • 在使用UPDATE时一定要细心。因为稍不注意,就会更新表中的所有行。
  • 基本的UPDATE语句由三部分组成,分别是:□ 要更新的表;□ 列名和它们的新值;□ 确定要更新哪些行的过滤条件。
  • 在更新多个列时,只需要使用一条SET命令,每个“列=值”对之间用逗号分隔(最后一列之后不用逗号)。
  • UPDATE语句中可以使用子查询,使得能用SELECT语句检索出的数据更新列数据。
  • 有的SQL实现支持在UPDATE语句中使用FROM子句,用一个表的数据更新另一个表的行。
  • 要删除某个列的值,可设置它为NULL(假如表定义允许NULL值)。
  • 有两种使用DELETE的方式:□ 从表中删除特定的行;□ 从表中删除所有行。
  • DELETE FROM Customers
WHERE cust_id = '1000000006';
  • 如果想从表中删除所有行,不要使用DELETE。可使用TRUNCATE TABLE语句,它完成相同的工作,而速度更快(因为不记录数据的变动)。
  • 在UPDATE或DELETE语句使用WHERE子句前,应该先用SELECT进行测试,保证它过滤的是正确的记录,以防编写的WHERE子句不正确。

第17课 创建和操纵表

  • 用程序创建表,可以使用SQL的CREATE TABLE语句。
  • 利用CREATE TABLE创建表,必须给出下列信息:□ 新表的名字,在关键字CREATE TABLE之后给出;□ 表列的名字和定义,用逗号分隔;□ 有的DBMS还要求指定表的位置。
  • 在创建新的表时,指定的表名必须不存在,否则会出错。防止意外覆盖已有的表,SQL要求首先手工删除该表(请参阅后面的内容),然后再重建它,而不是简单地用创建表语句覆盖它。
  • NULL值就是没有值或缺值。允许NULL值的列也允许在插入行时不给出该列的值。不允许NULL值的列不接受没有列值的行,换句话说,在插入或更新行时,该列必须有值。
  • NULL为默认设置,如果不指定NOT NULL,就认为指定的是NULL。
  • 主键是其值唯一标识表中每一行的列。只有不允许NULL值的列可作为主键,允许NULL值的列不能作为唯一标识。
  • 默认值在CREATE TABLE语句的列定义中用关键字DEFAULT指定。
  • 默认值经常用于日期或时间戳列
  • 更新表定义,可以使用ALTER TABLE语句。
  • 使用ALTER TABLE要极为小心,应该在进行改动前做完整的备份(表结构和数据的备份)。数据库表的更改不能撤销,如果增加了不需要的列,也许无法删除它们。类似地,如果删除了不应该删除的列,可能会丢失该列中的所有数据。
  • 删除表没有确认,也不能撤销,执行这条语句将永久删除该表。

第18课 使用视图

  • 视图是虚拟的表。与包含数据的表不一样,视图只包含使用时动态检索数据的查询。
  • ProductCustomers是一个视图,它不包含任何列或数据,包含的是一个查询(与上面用以正确联结表的查询相同)。
  • 重要的是,要知道视图仅仅是用来查看存储在别处数据的一种设施。视图本身不包含数据,因此返回的数据是从其他表中检索出来的。在添加或更改这些表中的数据时,视图将返回改变过的数据。
  • 许多DBMS禁止在视图查询中使用ORDER BY子句
  • 视图用CREATE VIEW语句来创建。与CREATE TABLE一样,CREATE VIEW只能用于创建不存在的视图。
  • CREATE VIEW ProductCustomers ASSELECT cust_name, cust_contact, prod_idFROM Customers, Orders, OrderItemsWHERE Customers.cust_id = Orders.cust_id AND OrderItems.order_num = Orders.order_num;
  • 视图极大地简化了复杂SQL语句的使用。利用视图,可一次性编写基础的SQL,然后根据需要多次使用。
  • 视图的另一常见用途是重新格式化检索出的数据。
  • 从视图检索数据时如果使用了一条WHERE子句,则两组子句(一组在视图中,另一组是传递给视图的)将自动组合。

第19课 使用存储过程

  • 存储过程就是为以后使用而保存的一条或多条SQL语句。可将其视为批文件,虽然它们的作用不仅限于批处理。
  • 使用存储过程有三个主要的好处,即简单、安全、高性能
  • 执行存储过程的SQL语句很简单,即EXECUTE。EXECUTE接受存储过程名和需要传递给它的任何参数。
  • SQL Server中所有局部变量名都以@起头

第20课 管理事务处理

  • 使用事务处理(transaction processing),通过确保成批的SQL操作要么完全执行,要么完全不执行,来维护数据库的完整性。
  • 事务处理是一种机制,用来管理必须成批执行的SQL操作,保证数据库不包含不完整的操作结果。利用事务处理,可以保证一组操作不会中途停止,它们要么完全执行,要么完全不执行(除非明确指示)。如果没有错误发生,整组语句提交给(写到)数据库表;如果发生错误,则进行回退(撤销),将数据库恢复到某个已知且安全的状态。
  • 事务处理用来管理INSERT、UPDATE和DELETE语句。不能回退SELECT语句(回退SELECT语句也没有必要),也不能回退CREATE或DROP操作。
  • 管理事务的关键在于将SQL语句组分解为逻辑块,并明确规定数据何时应该回退,何时不应该回退。
  • COMMIT用于保存更改,ROLLBACK用于撤销
  • SQL的ROLLBACK命令用来回退(撤销)SQL语句
  • 一般的SQL语句都是针对数据库表直接执行和编写的。这就是所谓的隐式提交(implicit commit),即提交(写或保存)操作是自动进行的。
  • 使用简单的ROLLBACK和COMMIT语句,就可以写入或撤销整个事务。但是,只对简单的事务才能这样做,复杂的事务可能需要部分提交或回退。
  • 要支持回退部分事务,必须在事务处理块中的合适位置放置占位符。这样,如果需要回退,可以回退到某个占位符。
  • 可以在SQL代码中设置任意多的保留点,越多越好。为什么呢?因为保留点越多,你就越能灵活地进行回退。

第21课 使用游标

  • 游标(cursor)是一个存储在DBMS服务器上的数据库查询,它不是一条SELECT语句,而是被该语句检索出来的结果集。
  • 游标主要用于交互式应用,其中用户需要滚动屏幕上的数据,并对数据进行浏览或做出更改。
  • 使用DECLARE语句创建游标
  • 使用OPEN CURSOR语句打开游标
  • CLOSE语句用来关闭游标。一旦游标关闭,如果不再次打开,将不能使用。第二次使用它时不需要再声明,只需用OPEN打开它即可。

第22课 高级SQL特性

  • 约束(constraint) 管理如何插入或处理数据库数据的规则。
  • 主键是一种特殊的约束,用来保证一列(或一组列)中的值是唯一的,而且永不改动。
  • 外键是表中的一列,其值必须列在另一表的主键中。
  • ADD CONSTRAINT CHECK (gender LIKE '[MF]')
  • 索引用来排序数据以加快搜索和排序操作的速度。
  • 索引改善检索操作的性能,但降低了数据插入、修改和删除的性能。在执行这些操作时,DBMS必须动态地更新索引。
  • 触发器是特殊的存储过程,它在特定的数据库活动发生时自动执行。触发器可以与特定表上的INSERT、UPDATE和DELETE操作(或组合)相关联。
  • 触发器内的代码具有以下数据的访问权:□ INSERT操作中的所有新数据;□ UPDATE操作中的所有新数据和旧数据;□ DELETE操作中删除的数据。
  • 一般来说,约束的处理比触发器快,因此在可能的时候,应该尽量使用约束。
  • 安全性使用SQL的GRANT和REVOKE语句来管理
  • 约束是实施引用完整性的重要部分,索引可改善数据检索的性能,触发器可以用来执行运行前后的处理,安全选项可用来管理数据访问。