数据库笔记(九)——对违反约束的处理 2017.10.15

写在前面:本篇博客大部分内容参考数据库系统概念(本科教学版)第四章的一些尾巴,然后开始讲第六章的关系代数
笔者接下来的代码示例会主要在SQL Server数据库中测试


在开始今天的摸鱼大业之前,让我们构造一些简单表

-- 执行下面的语句构造表
CREATE TABLE country(
  country_id INTEGER PRIMARY KEY ,
  country_name VARCHAR(20)
);

CREATE TABLE person(
  person_id INTEGER PRIMARY KEY ,
  name VARCHAR(20),
  country_id INTEGER FOREIGN KEY REFERENCES country(country_id)
    ON DELETE CASCADE
    ON UPDATE CASCADE
);

INSERT INTO country (country_id, country_name) VALUES (
    1, 'China'
);
INSERT INTO country (country_id, country_name) VALUES (
    2, 'English'
);
INSERT INTO country (country_id, country_name) VALUES (
    3, 'America'
);

INSERT INTO person (person_id, name, country_id) VALUES (
    1, 'Sunny', 1
);

INSERT INTO person (person_id, name, country_id) VALUES (
    2, 'Robbin', 2
);

INSERT INTO person (person_id, name, country_id) VALUES (
    3, 'Jane', 3
);

级联操作

在指定外键以后,由于存在完整性约束,所以在执行删除或更新的时候由于语句可能会破坏完整性约束而执行失败。因此可以在定义外键的时候声明为级联删除和级联更新(是一种对违反参照完整性约束时的处理方式)

  • 使用方式

    CREATE table 表名(
      ...
      FOREGIN KEY (字段序列) REFERENCES 表名(字段序列)
          ON DELETE CASCADE
          ON UPDATE CASCADE,
      ...
    )
    
  • 级联删除(ON DELETE CASCADE)

    • 级联删除是在定义外键时指定的,但是却会在执行删除语句时产生影响
    • 举个栗子
      • 我们先不指定级联
        DROP TABLE person;
        DROP TABLE country;
        CREATE TABLE country(
          country_id INTEGER PRIMARY KEY ,
          country_name VARCHAR(20)
        );
        
        CREATE TABLE person(
          person_id INTEGER PRIMARY KEY ,
          name VARCHAR(20),
          country_id INTEGER FOREIGN KEY REFERENCES country(country_id)
        );
        
        INSERT INTO country (country_id, country_name) VALUES (
            1, 'China'
        );
        INSERT INTO country (country_id, country_name) VALUES (
            2, 'English'
        );
        INSERT INTO country (country_id, country_name) VALUES (
            3, 'America'
        );
        
        INSERT INTO person (person_id, name, country_id) VALUES (
            1, 'Sunny', 1
        );
        
        INSERT INTO person (person_id, name, country_id) VALUES (
            2, 'Robbin', 2
        );
        
        INSERT INTO person (person_id, name, country_id) VALUES (
            3, 'Jane', 3
        );
        
        
      • 然后执行下面的删除操作
        -- 下面我们试图删除中国的信息,但是在person表里有一条数据引用了中国,所以因为参照完整性约束的存在,所以这条语句会执行失败
        DELETE country
        WHERE country_id = 1
        
      • 接下来我们重新构造一遍(当然直接用DDL语句更新也是可以的)--并在构造person表时指定了级联删除
        DROP TABLE person;
        DROP TABLE country;
        CREATE TABLE country(
          country_id INTEGER PRIMARY KEY ,
          country_name VARCHAR(20)
        );
        
        CREATE TABLE person(
          person_id INTEGER PRIMARY KEY ,
          name VARCHAR(20),
          country_id INTEGER FOREIGN KEY REFERENCES country(country_id)
              ON DELETE CASCADE
        );
        
        INSERT INTO country (country_id, country_name) VALUES (
            1, 'China'
        );
        INSERT INTO country (country_id, country_name) VALUES (
            2, 'English'
        );
        INSERT INTO country (country_id, country_name) VALUES (
            3, 'America'
        );
        
        INSERT INTO person (person_id, name, country_id) VALUES (
            1, 'Sunny', 1
        );
        
        INSERT INTO person (person_id, name, country_id) VALUES (
            2, 'Robbin', 2
        );
        
        INSERT INTO person (person_id, name, country_id) VALUES (
            3, 'Jane', 3
        );
        
        
      • 然后再次执行下面的删除操作
        -- 此时执行会发现语句成功执行了,不但删除了中国的信息,连带person表中引用了中国信息的所有数据都被删除了
        DELETE country
        WHERE country_id = 1
        
    • 上面的例子便很好的说明了级联删除的作用。如果我们视图删除外键参照键所在表(此处为country表)的某条数据A(此处是中国的信息),而这条数据又被外键所在表的一条或多条数据B所关联(此处person表中Sunny的country_id关联了country表中中国的id)。在指定了级联删除的情况下,删除A会连带着删除所有满足条件的B
    • 当然在实际使用的时候用的还是比较少的,因为参照完整性约束在一定程度上可以防止数据的误删除,对数据库的完整性起了一定的保护作用,如果指定了级联删除,这层保护就失效了。所以还是视情况而用
  • 级联更新(ON UPDATE CASCADE)

    • 类似的,级联更新和级联删除一样,如果我们更新时违反了完整性约束,同样更新操作不被拒绝,而是级联更新
    • 举个栗子(我们在上面操作的基础上执行,上面构造时指定了级联删除,但是没指定级联更新)
      -- 我们试图执行下面的更新操作,我们把修改English的country_id, 但是由于person表中还有数据的country_id=2,如果下面的更新成功执行,则会导致person表中存在country_id=2的数据,而country中却没有对应数了,违反参照完整性约束,故下面的语句执行失败
      UPDATE country
      SET country_id = 4
      WHERE country_id = 2
      
    • 同样的,我们重新构造一下,此时指定级联更新
      DROP TABLE person;
      DROP TABLE country;
      CREATE TABLE country(
        country_id INTEGER PRIMARY KEY ,
        country_name VARCHAR(20)
      );
      
      CREATE TABLE person(
        person_id INTEGER PRIMARY KEY ,
        name VARCHAR(20),
        country_id INTEGER FOREIGN KEY REFERENCES country(country_id)
          ON DELETE CASCADE
          ON UPDATE CASCADE
      );
      
      INSERT INTO country (country_id, country_name) VALUES (
          1, 'China'
      );
      INSERT INTO country (country_id, country_name) VALUES (
          2, 'English'
      );
      INSERT INTO country (country_id, country_name) VALUES (
          3, 'America'
      );
      
      INSERT INTO person (person_id, name, country_id) VALUES (
          1, 'Sunny', 1
      );
      
      INSERT INTO person (person_id, name, country_id) VALUES (
          2, 'Robbin', 2
      );
      
      INSERT INTO person (person_id, name, country_id) VALUES (
          3, 'Jane', 3
      );
      
    • 此时再执行一下上面的更新语句
      -- 由于指定了级联更新,所以会发现下面的语句执行成功了,不但更改了country表中的数据,连带着person表中的数据也一并更新了
      UPDATE country
      SET country_id = 4
      WHERE country_id = 2
      
    • 上面就是级联更新的效果
  • 另一类对违反完整性约束的处理

    • SET DEFAULT
      • 一旦违反完整性约束,就将参照域(此处为country_id)设置为默认值
    • SET NULL
      • 一旦违反完整性约束,就将参照域(此处为country_id)设置为NULL
    • 举个栗子
      • 执行下面的构造
        DROP TABLE person;
        DROP TABLE country;
        CREATE TABLE country(
          country_id INTEGER PRIMARY KEY ,
          country_name VARCHAR(20)
        );
        
        CREATE TABLE person(
          person_id INTEGER PRIMARY KEY ,
          name VARCHAR(20),
          country_id INTEGER FOREIGN KEY REFERENCES country(country_id)
            ON DELETE SET NULL
        );
        
        INSERT INTO country (country_id, country_name) VALUES (
            1, 'China'
        );
        INSERT INTO country (country_id, country_name) VALUES (
            2, 'English'
        );
        INSERT INTO country (country_id, country_name) VALUES (
            3, 'America'
        );
        
        INSERT INTO person (person_id, name, country_id) VALUES (
            1, 'Sunny', 1
        );
        
        INSERT INTO person (person_id, name, country_id) VALUES (
            2, 'Robbin', 2
        );
        
        INSERT INTO person (person_id, name, country_id) VALUES (
            3, 'Jane', 3
        );
        
      • 然后执行下面的删除操作
        -- 下面的删除操作成功执行,但不是级联删除,而是把person表中原来country_id=1的数据的country_id都设成了NULL
        DELETE country
        WHERE country_id = 1
        

延迟检查

这是由于数据库默认是在执行每一条SQL语句的时候都进行完整性约束的检查,导致有些操作无法进行。延迟操作就将完整性约束的检查延迟到了事务结束的时候检查(大多数数据库不支持,比如SQL Server, 但Oracle数据支持)

  • 由于不常用,SQL Server也不支持,这里就讲一下概念,不举实际的栗子了。

  • 假设上面的例子表中没有指定延迟检查

    • 执行下面的语句
    -- 执行下面两条语句是会出错的,因为插入第一条数据的时候,由于完整性约束的存在,要求country表中要有country_id=4的数据,但是这个数据目前还不存在(所以只要先执行第二条语句,这两个语句才能成功执行)
    INSERT INTO person (person_id, name, country_id) VALUES (
        4, 'Jerry', 4
    );
    INSERT INTO country (country_id, country_name) VALUES (
        4, 'France'
    );
    
  • 而如果指定了延迟检查呢

    • 执行下面语句(下面两个语句处于同一个事务中)
    -- 由于是延迟检查,所以两条数据都插入完,执行commit,事务结束时才进行完整性约束的检查,此时就不会出错,可以正常插入
    INSERT INTO person (person_id, name, country_id) VALUES (
        4, 'Jerry', 4
    );
    INSERT INTO country (country_id, country_name) VALUES (
        4, 'France'
    );
    COMMIT
    
  • 虽然SQL标准中有这个概念,但是大多数数据库没有提供支持,并且不常用

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

推荐阅读更多精彩内容