百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程字典 > 正文

「SQL数据分析系列」13. 索引和约束

toyiye 2024-06-21 12:36 12 浏览 0 评论

数据与智能 本公众号关注大数据与人工智能技术。由一批具备多年实战经验的技术极客参与运营管理,持续输出大数据、数据分析、推荐系统、机器学习、人工智能等方向的原创文章,每周至少输出7篇精品原创。同时,我们会关注和分享大数据与人工智能行业动态。欢迎关注。

来源 | Learning SQL Generate, Manipulate, and Retrieve Data, Third Edition

作者 | Alan Beaulieu

译者 | Liangchu

校对 | gongyouliu

编辑 | auroral-L

全文共8341字,预计阅读时间50分钟。

第十三章 索引和约束

1. 索引

1.1 创建索引

1.1.1 唯一索引

1.1.2 多列索引

1.2 索引类型

1.2.1 B 树索引

1.2.2 位图索引

1.2.3 文本索引

1.3 如何使用索引

1.4 索引的缺点

2. 约束

2.1 创建约束


由于本书着重介绍编程技术,所以前十二章集中介绍了SQL语言的基础知识,你可以运用这些知识编写强大的select、insert、update和delete语句。但是,还有些数据库功能会间接影响你编写的代码。本章主要讨论其中两个功能:索引和约束。

1. 索引

在表中插入一行数据时,数据库服务器不会试图将数据放在表中任何一个特定的地方。例如,如果向customer表中添加一行,那么服务器不会按照customer_id列的数字顺序存放该行,也不会按照last_name列的字母顺序存放该行。相反,服务器只是简单地将数据存放在文件中的下一个可用位置(服务器为每个表维护一系列可用空间)。因此,在查询customer表时,服务器需要检查表的每一行来完成查询。例如,发出查询如下:


mysql> SELECT first_name, last_name
 -> FROM customer
 -> WHERE last_name LIKE 'Y%';
+------------+-----------+
| first_name | last_name |
+------------+-----------+
| LUIS | YANEZ |
| MARVIN | YEE |
| CYNTHIA | YOUNG |
+------------+-----------+
3 rows in set (0.09 sec)



要查找姓氏以Y开头的所有客户,服务器必须访问customer表中的每一行并检查相应last_name列的内容,如果姓氏以Y开头,则将该行添加到结果集中。这种类型的访问称为表扫描(table scan)。


虽然这种方法对于只有三行的表很有效,但是想象一下,如果表包含300万行数据,那么要多久才能完成一次查询。对于大于3而小于300万行规模的表,在没有其他帮助的情况下,服务器也不能在合适的时间内完成一次查询。所以我们需要额外帮助来提升查询效率,它就是customer表上的一个或多个索引(index)。


即使你可能从没听说过数据库索引,但你也肯定知道索引是什么(例如本书就有一个)。索引是在资源中查找特定项的一种机制。例如,每个科技出版物的末尾都有一个索引,以供定位指定的单词或短语。索引按字母顺序列出这些单词和短语,使得读者快速定位到索引中的特定字母,找到所需的条目,然后找到指定单词或短语所在的页面。


和人们在出版物中使用索引查找单词的方式相同,数据库服务器亦使用索引定位表中的行。索引与普通的数据表不同,它是一种以特定顺序保存的专用表。然而,索引并不包括相关实体的所有数据,而只包含用于在数据表中定位行的列,以及描述这些行的物理位置的信息。因此,索引的作用就是方便检索表中行和列的子集,而无需检查表中的每一行。


1.1 创建索引


再回到customer表,你可能打算在email列上添加索引,以加速所有为该列定位值的查询,或是与客户电子邮件地址有关的任何update或delete操作。下面介绍如何在MySQL数据库中添加该索引:


mysql> ALTER TABLE customer
 -> ADD INDEX idx_email (email);
Query OK, 0 rows affected (1.87 sec)
Records: 0 Duplicates: 0 Warnings: 0



此语句在customer.email列创建了索引(其实是一个B树,稍后再讨论)并命名为idx_email。有了索引,倘若索引有利于查询,那么查询优化器(我们在第三章中讨论过)可以选择索引。如果一个表上有多个索引,那么优化器必须决定对于特定的SQL语句而言哪个索引最有利。


注意

MySQL将索引视为表的可选部件,这就是为什么在早期版本中你需要使用alter table命令添加或删除索引的原因。其他数据库服务器(包括SQL Server和Oracle Database)将索引视为独立的模式对象。因此,对于SQL Server和Oracle,你需要使用create index命令生成索引,如下所示:

CREATE INDEX idx_email
ON customer (email);

从5.0版本的MySQL开始,可以使用create index命令创建索引(尽管它被映射到了alter table命令)。但对于主键,仍要使用alter table命令来创建主键索引。



所有数据库服务器都允许查看可用索引。MySQL用户可以使用show命令查看某个表上的所有索引,如下所示:


mysql> SHOW INDEX FROM customer \G;
*************************** 1. row ***************************
 Table: customer
 Non_unique: 0
 Key_name: PRIMARY
 Seq_in_index: 1
 Column_name: customer_id
 Collation: A
 Cardinality: 599
 Sub_part: NULL
 Packed: NULL
 Null:
 Index_type: BTREE
...
*************************** 2. row ***************************
 Table: customer
 Non_unique: 1
 Key_name: idx_fk_store_id
 Seq_in_index: 1
 Column_name: store_id
 Collation: A
 Cardinality: 2
 Sub_part: NULL
 Packed: NULL
 Null:
 Index_type: BTREE
...
*************************** 3. row ***************************
 Table: customer
 Non_unique: 1
 Key_name: idx_fk_address_id
 Seq_in_index: 1
 Column_name: address_id
  Collation: A
 Cardinality: 599
 Sub_part: NULL
 Packed: NULL
 Null:
 Index_type: BTREE
...
*************************** 4. row ***************************
 Table: customer
 Non_unique: 1
 Key_name: idx_last_name
 Seq_in_index: 1
 Column_name: last_name
 Collation: A
 Cardinality: 599
 Sub_part: NULL
 Packed: NULL
 Null:
 Index_type: BTREE
...
*************************** 5. row ***************************
 Table: customer
 Non_unique: 1
 Key_name: idx_email
 Seq_in_index: 1
 Column_name: email
 Collation: A
 Cardinality: 599
 Sub_part: NULL
 Packed: NULL
 Null: YES
 Index_type: BTREE
...
5 rows in set (0.06 sec)



输出显示customer表上有五个索引:一个是customer_id列的主键索引PRIMARY,另外四个是store_id、address_id、last_name和email列的索引。这些索引是怎么来的呢?其实我先前就在email列上创建了索引,而其余的索引是作为示例Sakila数据库的一部分安装的。下面是用于创建表的语句:


CREATE TABLE customer (
 customer_id SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT,
 store_id TINYINT UNSIGNED NOT NULL,
 first_name VARCHAR(45) NOT NULL,
 last_name VARCHAR(45) NOT NULL,
 email VARCHAR(50) DEFAULT NULL,
 address_id SMALLINT UNSIGNED NOT NULL,
 active BOOLEAN NOT NULL DEFAULT TRUE,
 create_date DATETIME NOT NULL,
 last_update TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
 PRIMARY KEY (customer_id),
 KEY idx_fk_store_id (store_id),
 KEY idx_fk_address_id (address_id),
 KEY idx_last_name (last_name),
 ...




当创建表时,MySQL服务器会自动为主键列生成一个索引,在本例中是customer_id,并将索引命名为PRIMARY,这是一种与主键约束一起使用的一类特殊索引,我会在本章后面介绍它。


如果创建索引后你后悔了,那么可以通过下面的方法删除索引:


mysql> ALTER TABLE customer
 -> DROP INDEX idx_email;
Query OK, 0 rows affected (0.50 sec)
Records: 0 Duplicates: 0 Warnings: 0



注意:SQL Server和Oracle Database用户必须使用drop index命令删除索引,如下所示:


DROP INDEX idx_email; (Oracle)
DROP INDEX idx_email ON customer; (SQL Server)



MySQL现在也支持drop index命令,尽管它也映射到了alter table命令。


1.1.1 唯一索引


在设计数据库时,考虑哪些列允许、那些列不允许包含重复数据是很重要的事情。例如,在customer表中存在两个同名为John Smith的客户是允许的,因为每行都有一个不同的标识符(customer_id)、电子邮件和地址来帮助区分他们。但是,两个不同的客户是不能拥有相同的电子邮件地址的。可以通过在customer.email列上创建唯一索引(unique index)来限制出现相同的电子邮件地址。


唯一索引能起到多种作用,除了提供常规索引的所有优点外,它还充当一种用于禁止索引列中出现重复值的机制。每当插入一行或修改索引列时,数据库服务器都会检查唯一索引,以检查该值是否已存在于表中的另一行中。下面是为customer.email创建唯一索引的语句:


mysql> ALTER TABLE customer
 -> ADD UNIQUE idx_email (email);
Query OK, 0 rows affected (0.64 sec)
Records: 0 Duplicates: 0 Warnings: 0



注意

SQL Server和Oracle Database的用户在创建索引时只需增加unique关键字,如下所示:

CREATE UNIQUE INDEX idx_email
ON customer (email);


有了唯一索引之后,如果你尝试添加一行电子邮件地址已在表中存在的新客户信息,服务器将抛出一个错误:


mysql> INSERT INTO customer
 -> (store_id, first_name, last_name, email, address_id, active)
 -> VALUES
 -> (1,'ALAN','KAHN', 'ALAN.KAHN@sakilacustomer.org', 394, 1);
ERROR 1062 (23000): Duplicate entry 'ALAN.KAHN@sakilacustomer.org' 
 for key 'idx_email'


不需要为主键列构建唯一索引,因为服务器已经检查了主键的唯一性。但如果你觉得有必要的话,可以在同一个表上创建多个唯一索引。


1.1.2 多列索引


除了到目前为止演示过的单列索引外,还可以构建跨多列的索引。例如,如果要通过名字和姓氏来搜索客户,则可以一起为这两列创建索引,如下所示:


mysql> ALTER TABLE customer
 -> ADD INDEX idx_full_name (last_name, first_name);
Query OK, 0 rows affected (0.35 sec)
Records: 0 Duplicates: 0 Warnings: 0



此索引对于指定名字和姓氏的查询或是仅指定姓氏的查询非常有用,但是不太适合于仅指定客户名字的查询。想知道为什么,请考虑如何查找某人的电话号码的情况:倘若你可以知道此人的姓名,就可以通过电话簿快速查到号码,因为电话簿是先通过姓氏顺序排序,再通过名字顺序组织的。如果你只知道这个人的名字,那么你就必须浏览电话簿中的每个条目来查找具有指定名字的所有条目。


因此,在构建多列索引时,应该仔细考虑哪一列作为第一列,哪一列作为第二列,以此类推,以使索引尽可能发挥作用。但是请记住,如果你要保证足够的响应时间,那么也可以基于不同顺序为同一组列创建多列索引。


1.2 索引类型


索引是一种功能强大的工具,但由于存在许多不同类型的数据,单一的索引策略并不总是能满足需求。以下各节介绍各种服务器中使用的不同类型的索引。


1.2.1 B树索引


迄今为止演示的所有索引都是平衡树索引(balanced-tree index),通常称为B树索引(B-tree index)。MySQL、Oracle Database和SQL Server都默认使用B树索引,因此除非显式地指定使用其它类型的索引,否则你使用的就是B树索引。如你所料,B树索引以树结构组织,其中有一个或多个分支节点(branch node)用于指向单级叶节点(leaf node)。分支节点用于遍历树,而叶节点用于保存实际值和位置信息。例如,基于customer.last_name列的B树索引如下图(13-1)所示:




如果要查询检索姓氏以G开头的所有客户,那么服务器将查看顶部的分支节点(称为根节点(root node))并顺指针顺序前进到姓氏以A到M开头的分支节点。此分支节点会依次将服务器导向包含姓氏以G开头的叶节点,然后服务器开始读取叶节点中的值直到遇到一个不以G开头的值(在本例中是Hawthorne)。


当向customer表中插入、更新和删除行时,服务器将尽力保持树的平衡,避免出现根节点的一侧的分支节点/叶节点比另一侧多得多的情况。服务器可以添加或删除分支节点以重新分配值使得其分布更均匀,甚至还能添加或删除整个级别的分支节点来做到这一点。通过保持树的平衡,服务器可以在无需遍历多层分支节点的情况下快速遍历到叶节点以找到需要的值。


1.2.2 位图索引


尽管B-tree索引很适合处理包含许多不同值的列(例如客户的名字/姓氏),但在处理只有少量不同值的列时,它可能会变得非常笨拙。例如,你可以在customer.active创建索引列,以便可以快速检索到所有活跃或非活跃帐户。但是,由于只有两个不同的值(活跃存储为1,非活跃存储为0),而且活跃客户比非活跃客户要多得多,因此随着客户数量的增长,很难保持B树索引的平衡。


对于仅包含少量值却跨大量行(称为低基数(low-cardinality))的列,应该采用不同的索引策略。为了更有效地处理这种情况,Oracle Database使用了位图索引(bitmap index),它为列中存储的每个值生成一个位图(bitmap)。如果要为customer.active列建立位图索引,索引将维护两个位图:一个用于值0,另一个用于值1。当查询所有非活跃客户时,数据库服务器可以遍历0位图并快速检索所需的行。


对于低基数数据,位图索引是一个友好而紧凑的索引解决方案,但是如果列中存储的值的数量相对行数太高时(称为高基数(high-cardinality)),这种索引策略就会失败,因为服务器需要维护太多的位图。例如,你永远不会在主键列上创建一个位图索引,因为这可能代表最高的基数(每行的值都不同)。


Oracle用户只需将bitmap关键字添加到create index语句中就可以生成位图索引,如下所示:

CREATE BITMAP INDEX idx_active ON customer (active);


位图索引通常用于数据仓库环境,其中会有大量数据被索引,列的值也相对较少(例如,销售季度、地区、产品、销售员)。


1.2.3 文本索引


如果数据库中有存储文档,则可能需要允许用户在文档中查找单词或短语。你当然不希望服务器在每次请求查找时都打开所有文档并扫描所需的文本,但是传统的索引策略并不适用于这种情况。为了处理这种情况,MySQL、SQL Server和Oracle Database包括专门的文档索引和搜索机制,其中,SQL Server和MySQL包含所谓的全文索引,Oracle Database包含一组称为Oracle Text的强大工具集。文档查找的专业性很强,所以我就不举例子说明了,但我希望你至少了解一些处理这种情况的方法。


1.3 如何使用索引


服务器通常使用索引以快速定位特定表中的行,然后服务器会访问关联表以提取用户请求的附加信息。考虑以下查询:


mysql> SELECT customer_id, first_name, last_name
 -> FROM customer
 -> WHERE first_name LIKE 'S%' AND last_name LIKE 'P%';
+-------------+------------+-----------+
| customer_id | first_name | last_name |
+-------------+------------+-----------+
| 84 | SARA | PERRY |
| 197 | SUE | PETERS |
| 167 | SALLY | PIERCE |
+-------------+------------+-----------+
3 rows in set (0.00 sec)


对于此查询,服务器可以采用以下策略之一:


? 扫描customer表中的所有行;

? 使用last_name列上的索引查找姓氏以P开头的所有客户,然后访问customer表的每一行,只查找姓氏以S开头的行;

? 使用last_name和first_name列的索引查找姓以P开头、名以S开头的所有客户。


第三种选择似乎是最佳选择,因为索引将在无需重新访问表的情况下产生结果集所需的所有行。但你怎么知道数据库会使用这三种策略中的哪一个呢?为了了解MySQL的查询优化器是如何执行查询的,我使用explain语句请求服务器显示查询的执行计划而不执行查询:


mysql> EXPLAIN
 -> SELECT customer_id, first_name, last_name
 -> FROM customer
 -> WHERE first_name LIKE 'S%' AND last_name LIKE 'P%' \G;
*************************** 1. row ***************************
 id: 1
 select_type: SIMPLE
 table: customer
 partitions: NULL
  type: range
possible_keys: idx_last_name,idx_full_name
 key: idx_full_name
 key_len: 274
 ref: NULL
 rows: 28
 filtered: 11.11
 Extra: Using where; Using index
1 row in set, 1 warning (0.00 sec)


注意

每个数据库服务器都提供用于查看查询优化器是如何处理SQL语句的一些工具。SQL Server要在SQL语句之前加上show plan_text on来查看执行计划。Oracle Database包含explain plan语句,该语句把执行计划写入一个名为plan_ table的专用表中。


查看查询结果可以知道,possible_keys列告诉你服务器可以选择使用idx_last_name或idx_full_name索引,而key列则告诉你服务器使用的是idx_full_name索引。此外,type列告诉你将使用范围扫描,这意味着数据库服务器将在索引中查找一系列值,而不是检索一行。


注意

我刚才引导你完成了一个查询优化的示例。优化涉及查看SQL语句和确定服务器可用于执行该语句的资源。为了更高效地执行,你可以修改SQL语句,调整数据库资源,或者两种方法都采用。优化是一个很深入的话题,我强烈建议你阅读服务器的优化指南,或者选择一本好的调优书籍,这样你就能了解服务器所有可用的不同方法了。


1.4 索引的缺点


既然索引这么好,为什么不索引一切东西呢?好吧,要理解这一点的关键是要记住:每个索引都是一个表(一种特殊类型的表,但也是表)。因此,每次对表执行增加或删除操作时,都必须修改该表上的所有索引。当更新一行时,受影响的列的所有索引也需要修改。因此,索引越多,服务器为了保持所有模式对象处于最新状态所做的工作就越多,这往往会拖慢服务器的处理速度。


索引还需要磁盘空间,同时也需要管理员费心神去管理,因此最好的策略是:仅在有明确需求时添加索引。如果出于特殊目的(如每月维护例程)需要索引,则可以添加索引、运行例程,然后删除索引,直到下次需要再重复此过程。对数据仓库而言,在营业时间生成运行报表和特定查询时,索引至关重要,但当数据在一夜之间被加载到仓库中时就会出现问题,所以通常的做法是在加载数据之前删除索引,然后在仓库开放营业之前重新创建它们。


一般来说,你应该尽量避免索引太多和太少的情况。如果不确定应该有多少索引,可以默认使用以下策略:


? 确保所有主键列都已被索引(大多数服务器在创建主键约束时会自动创建唯一索引)。对于多列主键,请考虑为主键列的子集构建附加索引,或是以与主键约束定义不同的顺序为所有主键列构建索引;


? 为外键约束所引用的所有列构建索引。请记住,删除父级时,服务器会检查以确保没有子行存在,因此必须发出查询以搜索列中的特定值。如果列上没有索引,则服务器必须扫描整个表;


? 索引被频繁检索的列。大多数日期列以及短字符串列(2到50个字符)都是很好的候选列。


构建完一套初始索引后,请尽可能捕获表中的真实查询,查看服务器的执行计划,并修改索引策略以满足最常见的访问路径。



2. 约束


约束是一种简单地强加于表中一列或多列的限制。有几种不同类型的约束,包括:


? 主键约束(Primary key constraints)

标识表中保证唯一性的一列或多列。


? 外键约束(Foreign key constraints)

限制一列或多列中的值必须被包含在另一个表的主键列中(如果建立了update cascade或delete cascade规则,还可以限制其他表中的可用值,这两种规则叫级联更新或级联删除)。


? 唯一约束(Unique constraints)

限制一列或多列的值,保证其在表中的唯一性(主键约束是一类特殊的唯一约束)。


? 检查约束(Check constraints)

限制一列的可用值。


如果没有约束,那么难以保证数据库的一致性。例如,如果服务器允许更改customer表中的客户ID,而不更改rental表中相同的客户ID,那么就会得到不再指向有效客户记录的租赁数据(称为孤儿行(orphaned rows))。但如果有了主键约束和外键约束,任何视图修改或删除被其他表引用的数据时都会要么抛出一个错误,要么将这些改变传播到其他表(稍后将详细介绍这点)。



注意

如果你想在MySQL服务器上使用外键约束,那么表的存储引擎必须是InnoDB。


2.1 创建约束


约束通常与关联表通过create table语句同时创建。下面是Sakila示例数据库的模式生成脚本示例:


CREATE TABLE customer (
 customer_id SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT,
 store_id TINYINT UNSIGNED NOT NULL,
 first_name VARCHAR(45) NOT NULL,
 last_name VARCHAR(45) NOT NULL,
 email VARCHAR(50) DEFAULT NULL,
 address_id SMALLINT UNSIGNED NOT NULL,
 active BOOLEAN NOT NULL DEFAULT TRUE,
 create_date DATETIME NOT NULL,
 last_update TIMESTAMP DEFAULT CURRENT_TIMESTAMP 
 ON UPDATE CURRENT_TIMESTAMP,
 PRIMARY KEY (customer_id),
 KEY idx_fk_store_id (store_id),
 KEY idx_fk_address_id (address_id),
 KEY idx_last_name (last_name),
 CONSTRAINT fk_customer_address FOREIGN KEY (address_id) 
 REFERENCES address (address_id) ON DELETE RESTRICT ON UPDATE CASCADE,
 CONSTRAINT fk_customer_store FOREIGN KEY (store_id) 
 REFERENCES store (store_id) ON DELETE RESTRICT ON UPDATE CASCADE
)ENGINE=InnoDB DEFAULT CHARSET=utf8;



customer表包含三个约束:一个指定customer_id列作为表的主键,另外两个约束指定address_id和store_id列分别作为address和store表的外键。其实你也可以创建不带外键约束的customer表,然后通过alter table语句添加外键约束:


ALTER TABLE customer
ADD CONSTRAINT fk_customer_address FOREIGN KEY (address_id)
REFERENCES address (address_id) ON DELETE RESTRICT ON UPDATE CASCADE;
ALTER TABLE customer
ADD CONSTRAINT fk_customer_store FOREIGN KEY (store_id)
REFERENCES store (store_id) ON DELETE RESTRICT ON UPDATE CASCADE;



这两个语句都包含on子句:

? on delete restrict,如果在子表(customer)所引用的父表(address或store)中删除一行,服务器将抛出错误;


? on delete cascade,服务器会将父表(address或store)主键值的更改传播到子表(customer)。


当从父表中删除行时,on delete restrict子句可以防止生成孤儿行。为了说明这一点,下面在address表中选取一行,并显示address和customer表中共享此值的数据:


mysql> SELECT c.first_name, c.last_name, c.address_id, a.address
 -> FROM customer c
 -> INNER JOIN address a
 -> ON c.address_id = a.address_id
 -> WHERE a.address_id = 123;
+------------+-----------+------------+----------------------------------+
| first_name | last_name | address_id | address |
+------------+-----------+------------+----------------------------------+
| SHERRY | MARSHALL | 123 | 1987 Coacalco de Berriozbal Loop |
+------------+-----------+------------+----------------------------------+
1 row in set (0.00 sec)



结果显示,有一个客户行(对于Sherry Marshall)的address_id列包含值123。


如果尝试从父表(address)中删除此行,会发生以下情况:


mysql> DELETE FROM address WHERE address_id = 123;
ERROR 1451 (23000): Cannot delete or update a parent row: 
 a foreign key constraint fails (`sakila`.`customer`, 
 CONSTRAINT `fk_customer_address` FOREIGN KEY (`address_id`) 
 REFERENCES `address` (`address_id`) 
 ON DELETE RESTRICT ON UPDATE CASCADE)



因为子表中至少有一行包含address_id列中的值123,所以外键约束的on delete restrict子句导致语句失败。


当使用不同的策略更新父表中的主键值时,on update cascade子句也可以防止出现孤儿行。下面修改address.address_id列的某个值:


mysql> UPDATE address
 -> SET address_id = 9999
 -> WHERE address_id = 123;
Query OK, 1 row affected (0.37 sec)
Rows matched: 1 Changed: 1 Warnings: 0



语句成功执行,并且修改了一行。但是Sherry Marshall在customer表上的相关行发生了什么变化吗?它是否仍然指向不再存在的地址ID 123?想知道事实如何,请再次运行上一个查询,但是用新值9999替换旧值123:


mysql> SELECT c.first_name, c.last_name, c.address_id, a.address
 -> FROM customer c
 -> INNER JOIN address a
 -> ON c.address_id = a.address_id
 -> WHERE a.address_id = 9999;
+------------+-----------+------------+----------------------------------+
| first_name | last_name | address_id | address |
+------------+-----------+------------+----------------------------------+
| SHERRY | MARSHALL | 9999 | 1987 Coacalco de Berriozbal Loop |
+------------+-----------+------------+----------------------------------+
1 row in set (0.00 sec)



如你所见,返回的结果与以前相同(新的addressID值除外),这意味着customer表中的值9999已自动更新,这就是级联(cascade),它是用于防止产生孤儿行的第二种机制。


除了restrict和cascade之外,还可以使用set null,当父表中的行被删除或更新时,它会在子表中将外键值设置为null。在定义外键约束时,总共有六种不同的选策略可供选择:


? on delete restrict

? on delete cascade

? on delete set null

? on update restrict

? on update cascade

? on update set null


这些都是可选的,因此在定义外键约束时可以选择零个、一个或两个(一个用于删除,一个用于更新)策略。


最后,如果要删除主键或外键约束,可以再次使用alter table语句,只需用drop替换add。虽然删除主键约束并不是很常见,但有时外键约束会在某些维护操作期间被删除,然后重新建立。


相关推荐

为何越来越多的编程语言使用JSON(为什么编程)

JSON是JavascriptObjectNotation的缩写,意思是Javascript对象表示法,是一种易于人类阅读和对编程友好的文本数据传递方法,是JavaScript语言规范定义的一个子...

何时在数据库中使用 JSON(数据库用json格式存储)

在本文中,您将了解何时应考虑将JSON数据类型添加到表中以及何时应避免使用它们。每天?分享?最新?软件?开发?,Devops,敏捷?,测试?以及?项目?管理?最新?,最热门?的?文章?,每天?花?...

MySQL 从零开始:05 数据类型(mysql数据类型有哪些,并举例)

前面的讲解中已经接触到了表的创建,表的创建是对字段的声明,比如:上述语句声明了字段的名称、类型、所占空间、默认值和是否可以为空等信息。其中的int、varchar、char和decimal都...

JSON对象花样进阶(json格式对象)

一、引言在现代Web开发中,JSON(JavaScriptObjectNotation)已经成为数据交换的标准格式。无论是从前端向后端发送数据,还是从后端接收数据,JSON都是不可或缺的一部分。...

深入理解 JSON 和 Form-data(json和formdata提交区别)

在讨论现代网络开发与API设计的语境下,理解客户端和服务器间如何有效且可靠地交换数据变得尤为关键。这里,特别值得关注的是两种主流数据格式:...

JSON 语法(json 语法 priority)

JSON语法是JavaScript语法的子集。JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔花括号保存对象方括号保存数组JS...

JSON语法详解(json的语法规则)

JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔大括号保存对象中括号保存数组注意:json的key是字符串,且必须是双引号,不能是单引号...

MySQL JSON数据类型操作(mysql的json)

概述mysql自5.7.8版本开始,就支持了json结构的数据存储和查询,这表明了mysql也在不断的学习和增加nosql数据库的有点。但mysql毕竟是关系型数据库,在处理json这种非结构化的数据...

JSON的数据模式(json数据格式示例)

像XML模式一样,JSON数据格式也有Schema,这是一个基于JSON格式的规范。JSON模式也以JSON格式编写。它用于验证JSON数据。JSON模式示例以下代码显示了基本的JSON模式。{"...

前端学习——JSON格式详解(后端json格式)

JSON(JavaScriptObjectNotation)是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。它基于JavaScriptProgrammingLa...

什么是 JSON:详解 JSON 及其优势(什么叫json)

现在程序员还有谁不知道JSON吗?无论对于前端还是后端,JSON都是一种常见的数据格式。那么JSON到底是什么呢?JSON的定义...

PostgreSQL JSON 类型:处理结构化数据

PostgreSQL提供JSON类型,以存储结构化数据。JSON是一种开放的数据格式,可用于存储各种类型的值。什么是JSON类型?JSON类型表示JSON(JavaScriptO...

JavaScript:JSON、三种包装类(javascript 包)

JOSN:我们希望可以将一个对象在不同的语言中进行传递,以达到通信的目的,最佳方式就是将一个对象转换为字符串的形式JSON(JavaScriptObjectNotation)-JS的对象表示法...

Python数据分析 只要1分钟 教你玩转JSON 全程干货

Json简介:Json,全名JavaScriptObjectNotation,JSON(JavaScriptObjectNotation(记号、标记))是一种轻量级的数据交换格式。它基于J...

比较一下JSON与XML两种数据格式?(json和xml哪个好)

JSON(JavaScriptObjectNotation)和XML(eXtensibleMarkupLanguage)是在日常开发中比较常用的两种数据格式,它们主要的作用就是用来进行数据的传...

取消回复欢迎 发表评论:

请填写验证码