[疯狂Java]SQL-DDL:数据库约束

1. 约束的概念:

    1) 是定义在数据表上的强制执行的数据完整性校验规则;

    2) 它和数据表本身都属于数据库对象,因此它和表的地位是相同的,也会被当做元数据保存在系统表中记录;

!!MySQL使用information_schema元数据库中的TABLE_CONSTRAINTS表来保存该数据库对象中定义的所有约束;

!!可以到里面查看;

    3) 5种主要的完整性约束规则:

         i. not null:非空约束,指定列的值不能为空;

         ii. unique:唯一约束,指定列或者列的组合的值不能重复;

         iii. primary key:主键,指定列或者列的组合可以唯一的标识一条记录(本质就是非空、唯一);

         iv. foreign key:外键,指定该列参照(来自)另一列(可以在本表中也可以在其它表中),当然也可以是列的组合(但必须被参照的列组合列列对应);

         v. check:逻辑表达式约束,指定列的值必须满足指定的逻辑表达式(但MySQL并不支持check约束,虽然提供了check语句,但不起任何作用,MySQL要求用触发器来实现check约束的功能);

!!各个数据库都有自己的想法,侧重点不同,有些出于安全、成本等各种因素,但不要被这些差异所困扰;

    4) 约束的两种类型:

         i. 单列约束:约束只加在一列上;

         ii. 多列约束:也叫做组合约束,即将多列(≥2列)当成一列加以约束;

    5) 指定约束的两个时机:无非是创建表的时候指定和修改表结构的时候指定(alter table xxx modify/add/等);

    6) 定义约束的语法:

         i. 列级约束语法:在列的定义的最后指定约束(定义表时、修改表时(添加列、修改列));

         ii. 表级约束语法:在新的一行,和定义列一样平行地定义一个约束,并指定受约束的列或者列的组合;


!!接下来要讲的内容就是上述各种约束是:

        a. 如何定义的(建表时定义、修改表时添加、修改表时修改);

        b. 如何删除;

        c. 各个约束的上述两种操作的语法有一些细微的差别,还是值得强调的;


2. not null——非空约束:

    1) 定义一列的值不能为空(null),null是关键字,没有大小写之分,表示空值;

    2) null在SQL中不等于0、不等于空字符串"",,,等等,null也不等于null本身,就表示空,没有值而已,比较特殊;

    3) 非空约束的关键字就是not null;

    4) 非空约束只有列级约束语法,非空约束不能用表级定义;

    5) 建表时定义和修改表时定义:

craete table xxx
(
   col int not default 10 not null,
   ...
);

alter table xxx
modify col varchar(255) default 'xyz' not null;
!由于非空约束必须插入非空值,否则会拒绝插入(修改)数据,因此一般会给非空约束的列指定一个默认值避免插入空值而报错!

!!当然可以不指定默认值,但要小心插入空值会报错哦;

    6) 取消非空约束:只要修改表时将列指定为null即可,单个null表示可以为空值,例如alter table xxx modify col int null;

!!创建表时不指定not null就默认为是null的(即可以是空值),当然也可以显式写上null强制指定成可以为空值!

create table xxx ( col varchar(255) null ); // 这就强制指定col列可以为空值,null写不写都一样,默认不写就是null的;


3. 列级约束、表级约束的语法——以unique约束为例:

    1) 首先介绍unique约束,用于指定一列或者多列的组合不能出现重复值:

         i. 如果是一列,则该列的值不能重复,但可以有多个null,前面讲过null不等于null,所以符合unique约束(毕竟空的值不能比较,比较也没有意义,只能说明现在还没值,等有值了以后才能比较);

         ii. 如果是多列的组合,则组合值不能重复;

    2) 列级约束只能作用于一列,因此可以回想有哪些其它DDL语句也是只能作用于一列的,这些DML语句都可以用来定义列级约束(在语句末尾指定约束的关键字):

         i. 创建表时定义列的语句(一条语句只能定义一列):col1 int unique,   // 列级约束,最后的unique关键字指定了该列唯一

         ii. 修改表时用modify修改一列(modify一次只能修饰一列):alter table xxx modify col2 double unique;   // 列级约束,指定为唯一的

         iii. 修改表时用add增加一列(add一次只能增加一列):alter table xxx add col3 varchar(255) unique;   // 列级约束

    3) 表级约束:

         i. 表级约束的定义和列定义等属于平行的语句,列定义语句定义了一个列,而表级约束的定义则是显式定义了一个约束,只不过之前的列级约束是隐式定义的(夹杂在列定义中了);

         ii. 列级约束也可以用表级约束来定义,但是多列组合的约束就只能用表级约束来定义了;

         iii. 表级约束定义语法:[constraint 自定义约束名] 具体定义

!!可以看到表级约束定义允许自定义约束名,但是也可以不定义约束名(定义约束名必须用关键字constraint);

!!如果自定义了约束名就可以在定义之后引用该约束名了!!

         iv. 隐式约束名:如果在表级约束定义时不指定自定义的约束名或者直接使用了列级约束,系统会自动为这些约束生成一个约束名,只不过你不知道而已,如果你硬要知道系统隐式自动定义的约束名则可以select以下information_schema数据库中的TALBE_CONSTRAINTS表,系统都会记录在内,由于里面存放着所有表的约束,因此内容非常多,因此select的时候需要指定你要查找的约束属于哪个用户(即哪个用户创建了该约束,或者表名),这样就可以大大缩小查找范围,例如:

select * from information_schema.TABLE_CONSTRAINTS where TABLE_NAME='xxx';
</pre><p>         v. 定义表级约束的地方:</p><p>            a. 创建表时;</p><p>            b. alter table add时,直接将约束作为一个单位add,一次只能add一个约束; </p><p>    4) unique的表级约束具体定义语法:unique(列1, 列2, 列3) // 即指定列或者列的组合</p><p>    5) 示例:</p><p></p><pre code_snippet_id="1636302" snippet_file_name="blog_20160406_4_8040859" name="code" class="sql">create table t1
(
	col1 int,
	col2 int,
	col3 int,
	col4 int,
	col5 int,
	unique(col5), // 代替列级定义
	unique(col1, col2), // 不命名的组合
	constraint uk unique(col3, col4); // 命名版的
);

alter table t1 add unique(col1, col2); // 修改时像添加列一样地添加约束
alter table t1 add constraint uk unique(col1, col2); // 命名版的


4. 主键约束——primary key:

    1) 主键约束用于唯一标识一条数据记录,本质就是非空+唯一约束的组合,只不过非空和唯一都可以同时指定多个,但一张表中最多只能指定一个主键(表可以没有主键);

!!因此这就体现出了唯一标识的特性;

    2) 可以将一列指定为主键,也可以将多列组合指定为一个主键,只不过多列组合中每一列都不能由空值,但是组合值不能重复;

    3) 主键约束的关键字是primary key,定义方式和unique完全一样(列级约束、表级约束等);

    4) 只不过主键特殊的地方在于MySQL的实现上,MySQL无论如何都只会给主键默认命名为PRIMARY,即使用户使用[constraint 约束名]的方式自行命名了,但是底层还是命名成PRIMARY,但不过MySQL还是为主键提供了命名的语法,仅仅是为了跟标准的SQL兼容而已,但不过底层并不起到任何作用罢了;

!!MySQL的想法很简单:仅仅就是为了减少维护数据表时的复杂度,因为毕竟一张表最多只能有一个主键,因此表内不存在主键命名重复的问题,因此可以通过表名唯一确定表中的主键,因此不如统一将主键命名成相同的名字来的方便;

    5) 自增长的主键:

         i. 一般作为主键最多的就是像“编号”这一类的字段的,这类字段往往可以唯一标识一条记录,同时又往往具有一个特性,就是递增的特性,即加一条记录“编号”就+1等;

         ii. SQL里刚好就有一个列属性auto_increment,可以设置该列为自增长的,默认是加一条记录该字段的值就+1,因此默认在插入记录时可以不指定该字段的值而自动根据自增长的规则添加该字段;

         iii. 只要是整数型的列都可以指定auto_increment,主键如果是整型的则也可以定义为自增长的,例如:col1 int auto_increment primary key;

!!一般指定了自增长就不再指定默认值了,因为自增长的列默认插入一条记录值就加上一个increment(默认是1);


5. 外键约束——foreign key:

    1) 顾名思义,就是该列的值参照了另外一列(值的范围处于另一列的值中),而被参照的列可以是其它表(外关联),也可以参照本表中的其它列(自关联);

    2) 被参照的表称为“主表”,参照的表称为“从表”;

    3) 外键最大的特点就是其值必须可以唯一对应到被参照列的一个值上,也就是说对于外键,只能是唯一键被参照(当然可以是主键,主键也是一种唯一键);

!!因此可以概括为从表上外键必须来自主表的被参照列,因此外键的值的范围就限定在了被参照列的值之内,或者为空(null属于任何范围,即表示还没参照任何值);

    4) 外键的3种关系:

         i. 1对空:外键的值为null,即还没有参照任何值;

         ii. 1对多:外键的值可以重复,那么相同的值必然对应到参照的同一个主表值上,因此主对从的记录之间是1对多的关系;

         iii. 1对1:外键同时被指定为unique,那么外键之间互不相同,那么就意味着他们参照的值也互不相同(参照值跟被参照值必然相等的),因此主队从的记录之间是1对1的关系;

    5) 一张表中当然可以指定多个外键!

    6) 设置外键:语法格式还是通用的,只不过关键字是references,即参照的动词,表示该键要参照哪一列;

    7) 而外键的名称(关键字)是foreign key:列级约束直接用动词references指定要参照的列,而表级约束需要使用“名词(指定谁是外键)+动词(参照了谁)”;

    8) 格式总结:

         i. 列级约束:列定义... references 被参照的表(被参照的列);

         ii. 表级约束:[constraint 自定义约束名] foreign key(哪几列是外键) references 被参照的表(被参照的那些列);

!!可以看到表级约束可以定义组和列的外键,因为有多列作为一个组合参照其它表,因此被参照的列也必须是组合,类型啥的一定要一一对应;

!!MySQL并不支持外键的列级约束!MySQL要求定义外键只能用表级约束,但MySQL还是保留了列级约束的语法,只不过不生效而已,仅仅就是为了兼容标准SQL的语法而已;

    9) 设定外键不能用modify,只能在建表或者是add的表级约束方式添加!!!MySQL不支持add的列级约束设置外键!

    10) 示例:

create table t1
(
	col1 int,
	col2 int,
	primary key(col1, col2)
);

create table t2
(
	col1 int,
	col2 int,
        // col3 int references t1(col1), // 列级约束的外键在MySQL中不生效!!
	constraint fk foreign key(col1, col2) references t1(col1, col2) // 建表时的表级约束
);

alter table t2 add constraint fk foreign key(col1, col2) references t1(col1, col2); // 增加是的表级约束,MySQL不支持modify的列级约束
!!注意:多列组合是要保证主和从的列一一对应;

!!如果references参照的是本表中的另外一列那么就是自关联了!!比如一张员工表,里面有一个字段为是否是经理,而经理可以管理多个员工,那么就在一张表的记录中就存在从属关系了,如果有这样的字段(比如所属的经理),那么就是自关联了;


6. 删除约束:

    1) 由于约束也是数据库对象,也会被记录在系统表中作为元数据保存,因此当然可以删除这些约束;

    2) not null的删除:由于not null只有列级约束,只要modify时将那一列指定为null就相当于把nut null删去了;

    3) 标准SQL中删除约束的语法是:alter table 目标约束所属的表 dorp constraint 约束名;

!但是MySQL中删除unique、primary key、foreign key时和标准SQL并不完全一样;

!!删除任何约束都要先alter table一下指定删除目标所属的表,然后再写具体删除的语句;

    4) 标准SQL要求约束在系统表中都以索引的方式保存(唯一键、主键、外键),因此创建这些键的语法底层实际上是create index!而这在unique键的删除中表现的淋漓尽致,删除unique的语法是:drop index 唯一键的键名;

!!因此,如果创建unique时指定了键名就可以直接通过"drop index 键名"的方式删去,但如果没有指定键名,就必须到information_schema的TABLE_CONSTRAINTS中查找到系统默认的给出的键名后再使用drop index删去!

    5) 删除主键:MySQL主键名默认就是PRIMARY,因此可以使用"drop index PRIMARY"的方式删去,但是MySQL提供了drop primary key语句之间删除主键,毕竟一张表中最多只有一个主键,因此删它就没谁了!

    6) 和外键相关的删除:比较麻烦,因为存在主从对照的关系,特别是在主表上删除会影响到从表

         i. 首先考虑删除主表中的记录:由于主表被从表参照,那么删除主表中的记录后从表中参照它的记录就没有东西可以参照了,因此如果直接删除主表中的记录会被拒绝;

         ii. 因此正常情况下必须先删除从表中参照的记录,然后才可以删除主表中被参照的那几个记录;

         iii. 当然标准SQL语句提供了级联删除语法,可以在删除主表记录时自动将相应的被参照的从表记录也删除,这必须在从表定义外键时指定:

              a. 关键字是on delete cascade(级联删除参照记录)和on delete set null(并不删除参照记录,只是级联地将参照记录中的外键设为null(但前提是该外键不能是not null约束的!);

              b. 这两个属性作为属性语法写在外键定义的末尾处;

              c. 示例:

create table t2
(
	col1 int,
	col2 int,
	constraint fk foreign key(col1, col2) references t1(col1, col2) on delete cascade
);

alter table t2 add foreign key(col1) references t1(col2) on delete set null;
!!外键定义在MySQL必须使用标记约束,这里前者是级联删除,后者是级联置空;

         iv. 接下来考虑删除表中的外键约束了:外键约束很好删除,因为外键是从表中的,从表的改变并不影响主表,因此可以直接放心删除

              a. MySQL提供的删除外键的语句是:drop foreign key 外键名;

              b. 如果自定义了外建名那么可以直接指定,如果没有定义外键名(没有用[constraint 键名]的方式定义外键),就只能使用系统隐式指定的默认外键名;

              c. MySQL默认的隐式外键命名规则是:"表名_ibfk_n",其中n代表该外键是表中定义的第几个外键,按照定义的先后顺序编号;

              d. 例如:

	foreign key(col1) references t1(col1) on delete cascade,
	foreign key(col2) references t1(col2) on delete set null,





你可能感兴趣的:(sql,DDL,约束,疯狂Java)