zoukankan      html  css  js  c++  java
  • MySQL--约束

    约束

    1. 概述

    为了防止不符合规范的数据进入数据库,在用户对数据进行插入、修改、删除等操作时,DBMS自动按照一定的约束条件对数据进行监测,使不符合规范的数据不能进入数据库,以确保数据库中存储的数据正确、有效、相容。

    约束条件与数据类型的宽度一样,都是可选参数,主要分为以下几种:

    约束条件 解释
    NOT NULL 非空约束,指定某列不能为空;
    UNIQUE 唯一约束,指定某列或者几列组合不能重复
    PRIMARY KEY 主键,指定该列的值可以唯一地标识该列记录
    FOREIGN KEY 外键,指定该行记录从属于主表中的一条记录,主要用于参照完整性

    2. NOT NULL

    是否可空,null表示空,非字符串

    not null - 不可空

    null - 可空

    2.1 实例

    mysql> create table t12 (id int not null);
    Query OK, 0 rows affected (0.02 sec)
    
    mysql> select * from t12;
    Empty set (0.00 sec)
    
    mysql> desc t12;
    +-------+---------+------+-----+---------+-------+
    | Field | Type    | Null | Key | Default | Extra |
    +-------+---------+------+-----+---------+-------+
    | id    | int(11) | NO   |     | NULL    |       |
    +-------+---------+------+-----+---------+-------+
    row in set (0.00 sec)
    
    #不能向id列插入空元素。 
    mysql> insert into t12 values (null);
    ERROR 1048 (23000): Column 'id' cannot be null
    
    mysql> insert into t12 values (1);
    Query OK, 1 row affected (0.01 sec)
    

    2.2 DEFAULT

    我们约束某一列不为空,如果这一列中经常有重复的内容,就需要我们频繁的插入,这样会给我们的操作带来新的负担,于是就出现了默认值的概念。

    默认值,创建列时可以指定默认值,当插入数据时如果未主动设置,则自动添加默认值。

    not null + default

    mysql> create table t13 (id1 int not null,id2 int not null default 222);
    Query OK, 0 rows affected (0.01 sec)
    
    mysql> desc t13;
    +-------+---------+------+-----+---------+-------+
    | Field | Type    | Null | Key | Default | Extra |
    +-------+---------+------+-----+---------+-------+
    | id1   | int(11) | NO   |     | NULL    |       |
    | id2   | int(11) | NO   |     | 222     |       |
    +-------+---------+------+-----+---------+-------+
    rows in set (0.01 sec)
    
    # 只向id1字段添加值,会发现id2字段会使用默认值填充
    mysql> insert into t13 (id1) values (111);
    Query OK, 1 row affected (0.00 sec)
    
    mysql> select * from t13;
    +-----+-----+
    | id1 | id2 |
    +-----+-----+
    | 111 | 222 |
    +-----+-----+
    row in set (0.00 sec)
    
    # id1字段不能为空,所以不能单独向id2字段填充值;
    mysql> insert into t13 (id2) values (223);
    ERROR 1364 (HY000): Field 'id1' doesn't have a default value
    
    # 向id1,id2中分别填充数据,id2的填充数据会覆盖默认值
    mysql> insert into t13 (id1,id2) values (112,223);
    Query OK, 1 row affected (0.00 sec)
    
    mysql> select * from t13;
    +-----+-----+
    | id1 | id2 |
    +-----+-----+
    | 111 | 222 |
    | 112 | 223 |
    +-----+-----+
    rows in set (0.00 sec)
    

    3. UNIQUE

    唯一约束,指定某列或者几列组合不能重复。

    3.1 示例:

    方法一:
    create table department1(
    id int,
    name varchar(20) unique,
    comment varchar(100)
    );
    
    
    方法二:
    create table department2(
    id int,
    name varchar(20),
    comment varchar(100),
    unique(name)
    );
    
    
    mysql> insert into department1 values(1,'IT','技术');
    Query OK, 1 row affected (0.00 sec)
    mysql> insert into department1 values(1,'IT','技术');
    ERROR 1062 (23000): Duplicate entry 'IT' for key 'name'
    

    3.2 not null + unique

    mysql> create table t1(id int not null unique);
    Query OK, 0 rows affected (0.02 sec)
    
    mysql> desc t1;
    +-------+---------+------+-----+---------+-------+
    | Field | Type    | Null | Key | Default | Extra |
    +-------+---------+------+-----+---------+-------+
    | id    | int(11) | NO   | PRI | NULL    |       |
    +-------+---------+------+-----+---------+-------+
    row in set (0.00 sec)
    

    3.3 联合唯一

    create table service(
    id int primary key auto_increment,
    name varchar(20),
    host varchar(15) not null,
    port int not null,
    unique(host,port) #联合唯一
    );
    
    mysql> insert into service values
        -> (1,'nginx','192.168.0.10',80),
        -> (2,'haproxy','192.168.0.20',80),
        -> (3,'mysql','192.168.0.30',3306)
        -> ;
    Query OK, 3 rows affected (0.01 sec)
    Records: 3  Duplicates: 0  Warnings: 0
    
    mysql> insert into service(name,host,port) values('nginx','192.168.0.10',80);
    ERROR 1062 (23000): Duplicate entry '192.168.0.10-80' for key 'host'
    

    4. PRIMARY KEY

    主键为了保证表中的每一条数据的该字段都是表格中的唯一值。换言之,它是用来独一无二地确认一个表格中的每一行数据。
    主键可以包含一个字段或多个字段。当主键包含多个栏位时,称为组合键 (Composite Key),也可以叫联合主键。
    主键可以在建置新表格时设定 (运用 CREATE TABLE 语句),或是以改变现有的表格架构方式设定 (运用 ALTER TABLE)。
    主键必须唯一,主键值非空;可以是单一字段,也可以是多字段组合。

    4.1 单字段主键

    ============单列做主键===============
    #方法一:not null+unique
    create table department1(
    id int not null unique, #主键
    name varchar(20) not null unique,
    comment varchar(100)
    );
    
    mysql> desc department1;
    +---------+--------------+------+-----+---------+-------+
    | Field   | Type         | Null | Key | Default | Extra |
    +---------+--------------+------+-----+---------+-------+
    | id      | int(11)      | NO   | PRI | NULL    |       |
    | name    | varchar(20)  | NO   | UNI | NULL    |       |
    | comment | varchar(100) | YES  |     | NULL    |       |
    +---------+--------------+------+-----+---------+-------+
    rows in set (0.01 sec)
    
    #方法二:在某一个字段后用primary key
    create table department2(
    id int primary key, #主键
    name varchar(20),
    comment varchar(100)
    );
    
    mysql> desc department2;
    +---------+--------------+------+-----+---------+-------+
    | Field   | Type         | Null | Key | Default | Extra |
    +---------+--------------+------+-----+---------+-------+
    | id      | int(11)      | NO   | PRI | NULL    |       |
    | name    | varchar(20)  | YES  |     | NULL    |       |
    | comment | varchar(100) | YES  |     | NULL    |       |
    +---------+--------------+------+-----+---------+-------+
    rows in set (0.00 sec)
    
    #方法三:在所有字段后单独定义primary key
    create table department3(
    id int,
    name varchar(20),
    comment varchar(100),
    primary key(id); #创建主键并为其命名pk_name
    
    mysql> desc department3;
    +---------+--------------+------+-----+---------+-------+
    | Field   | Type         | Null | Key | Default | Extra |
    +---------+--------------+------+-----+---------+-------+
    | id      | int(11)      | NO   | PRI | NULL    |       |
    | name    | varchar(20)  | YES  |     | NULL    |       |
    | comment | varchar(100) | YES  |     | NULL    |       |
    +---------+--------------+------+-----+---------+-------+
    rows in set (0.01 sec)
    
    # 方法四:给已经建成的表添加主键约束
    mysql> create table department4(
        -> id int,
        -> name varchar(20),
        -> comment varchar(100));
    Query OK, 0 rows affected (0.01 sec)
    
    mysql> desc department4;
    +---------+--------------+------+-----+---------+-------+
    | Field   | Type         | Null | Key | Default | Extra |
    +---------+--------------+------+-----+---------+-------+
    | id      | int(11)      | YES  |     | NULL    |       |
    | name    | varchar(20)  | YES  |     | NULL    |       |
    | comment | varchar(100) | YES  |     | NULL    |       |
    +---------+--------------+------+-----+---------+-------+
    rows in set (0.01 sec)
    
    mysql> alter table department4 modify id int primary key;
    Query OK, 0 rows affected (0.02 sec)
    Records: 0  Duplicates: 0  Warnings: 0
    
    mysql> desc department4;
    +---------+--------------+------+-----+---------+-------+
    | Field   | Type         | Null | Key | Default | Extra |
    +---------+--------------+------+-----+---------+-------+
    | id      | int(11)      | NO   | PRI | NULL    |       |
    | name    | varchar(20)  | YES  |     | NULL    |       |
    | comment | varchar(100) | YES  |     | NULL    |       |
    +---------+--------------+------+-----+---------+-------+
    rows in set (0.01 sec)
    

    4.2 多字段主键

    ==================多列做主键================
    create table service(
    ip varchar(15),
    port char(5),
    service_name varchar(10) not null,
    primary key(ip,port)
    );
    
    
    mysql> desc service;
    +--------------+-------------+------+-----+---------+-------+
    | Field        | Type        | Null | Key | Default | Extra |
    +--------------+-------------+------+-----+---------+-------+
    | ip           | varchar(15) | NO   | PRI | NULL    |       |
    | port         | char(5)     | NO   | PRI | NULL    |       |
    | service_name | varchar(10) | NO   |     | NULL    |       |
    +--------------+-------------+------+-----+---------+-------+
    rows in set (0.00 sec)
    
    mysql> insert into service values
        -> ('172.16.45.10','3306','mysqld'),
        -> ('172.16.45.11','3306','mariadb')
        -> ;
    Query OK, 2 rows affected (0.00 sec)
    Records: 2  Duplicates: 0  Warnings: 0
    
    mysql> insert into service values ('172.16.45.10','3306','nginx');
    ERROR 1062 (23000): Duplicate entry '172.16.45.10-3306' for key 'PRIMARY'
    

    4.3 自动增长 auto_increment

    约束字段为自动增长,被约束的字段必须同时被key约束

    4.3.1 设置自动增长

    #不指定id,则自动增长
    create table student(
    id int primary key auto_increment,
    name varchar(20),
    sex enum('male','female') default 'male'
    );
    
    mysql> desc student;
    +-------+-----------------------+------+-----+---------+----------------+
    | Field | Type                  | Null | Key | Default | Extra          |
    +-------+-----------------------+------+-----+---------+----------------+
    | id    | int(11)               | NO   | PRI | NULL    | auto_increment |
    | name  | varchar(20)           | YES  |     | NULL    |                |
    | sex   | enum('male','female') | YES  |     | male    |                |
    +-------+-----------------------+------+-----+---------+----------------+
    mysql> insert into student(name) values
        -> ('nick'),
        -> ('tank')
        -> ;
    
    mysql> select * from student;
    +----+------+------+
    | id | name | sex  |
    +----+------+------+
    |  1 | nick | male |
    |  2 | tank | male |
    +----+------+------+
    
    
    #也可以指定id
    mysql> insert into student values(4,'asb','female');
    Query OK, 1 row affected (0.00 sec)
    
    mysql> insert into student values(7,'wsb','female');
    Query OK, 1 row affected (0.00 sec)
    
    mysql> select * from student;
    +----+------+--------+
    | id | name | sex    |
    +----+------+--------+
    |  1 | nick | male   |
    |  2 | tank | male   |
    |  4 | asb  | female |
    |  7 | wsb  | female |
    +----+------+--------+
    
    
    #对于自增的字段,在用delete删除后,再插入值,该字段仍按照删除前的位置继续增长
    mysql> delete from student;
    Query OK, 4 rows affected (0.00 sec)
    
    mysql> select * from student;
    Empty set (0.00 sec)
    
    mysql> insert into student(name) values('ysb');
    mysql> select * from student;
    +----+------+------+
    | id | name | sex  |
    +----+------+------+
    |  8 | ysb  | male |
    +----+------+------+
    
    #应该用truncate清空表,比起delete一条一条地删除记录,truncate是直接清空表,在删除大表时用它
    mysql> truncate student;
    Query OK, 0 rows affected (0.01 sec)
    
    mysql> insert into student(name) values('nick');
    Query OK, 1 row affected (0.01 sec)
    
    mysql> select * from student;
    +----+------+------+
    | id | name | sex  |
    +----+------+------+
    |  1 | nick | male |
    +----+------+------+
    row in set (0.00 sec)
    

    5. FOREIGN KEY

    多表 :

    假设我们要描述所有公司的员工,需要描述的属性有这些 : 工号 姓名 部门

    公司有3个部门,但是有1个亿的员工,那意味着部门这个字段需要重复存储,部门名字越长,越浪费

    解决方法: 我们完全可以定义一个部门表 然后让员工信息表关联该表,如何关联,即foreign key

    5.1 foreign key 带来的约束作用

    1. 必须先创建主表, 再创建从表
    2. 在从表中插入一条记录,关联了一个主表中不存在的id , 导致插入失败, 必须保证外键的值必须是在主表中存在的
    3. 插入数据的顺序, 先插入主表记录, 在插入从表记录
    4. 从表更新外键时也必须保证, 外键的值在主表中是存在的
    5. 删除主表记录前 要保证从表中没有外键关联被删除的id
    6. 更新删除主表记录的主键时, 要保证从表中没有外键关联主表主键

    5.2 示例

    create table class(
            id int primary key auto_increment,
            name char(20)
    	);
    	
    create table student(
            id int primary key auto_increment,
            name char(20),
            gender char(1),
            c_id int,
        	foreign key(c_id) references class(id)
    	);
    

    5.3 级联操作

    当我们需要删除部门(主表)信息 时,必须先删除从表中关联的数据,很麻烦

    级联操作指的就是,当你操作主表时,自动的操作从表

    5.3.1 两种级联操作

    1. 级联的删除

      当删除主表时自动删除从表中相关数据

    2. 级联更新

      当主表的主键更新时自动的更新关联的从表数据

    案例, 以上面的班级个学员为例:

    drop table if exists class;
    # 如果这表存在 才执行删除  可以避免报错  if exists
    # if not exists  如果不存在才执行
    create table class(
            id int primary key auto_increment,
            name char(20)
    	);
    insert into class values(null,"py9");
    insert into class values(null,"py10");
    
    #创建表的时候指定级联操作 
    drop table if exists student;
    create table student(
            id int primary key auto_increment,
            name char(20),
            gender char(1),
            c_id int,
        	foreign key(c_id) references class(id)
             on update cascade
       	 	on delete cascade
    	);
    # 级联操作可以单独使用 也可以一起使用   空格隔开即可 
    insert into student values(null,"jack","m",1);
    insert into student values(null,"rose","m",1);
    insert into student values(null,"tom","m",2);
    

    多表关系

    1. 为什么要分表操作

    有人说, 一张表不是很好吗? 分成多个表进行操作又乱又麻烦

    其实不然, 就以下表为例:

    看起来是不是很乱, 而且如果生产部的主管发生变更, 那所有生产部人员的Manager都需要进行相应的变更, 这样极大地增加了工作的难度和复杂度

    所以我们就需要进行分表操作, 如果部门主管发生变更, 只需要改右边的表就可以了, 左边的表不需要变动

    多表操作的优点:

    1. 节省内存空间
    2. 表与表之间分开, 除了外键约束之外, 没有任何关系, 修改一个表不会影响另外的表

    2. 多对一

    指的是从表的多条数据指向主表的同一个记录,比如员工和部门的关系, 一个部门可以有好多员工, 这就是多对一

    处理方式

    在多的一方保存相应的一的一方的编号

    #部门:
    	create table dept(
            id int primary key auto_increment,
            name char(20),
            job char(50),
            manager char(10)
    	);
    	#老师表:
    	create table teacher(
            id int primary key auto_increment,
            name char(20),
            gender char(1),
            dept_id int,
            foreign key(t_id) references teacher(id),
    	);
    

    3. 多对多

    例如老师和学生, 一个学生有不同的老师来教不同的课程, 二一个老师又有很多学生, 这就是多对多关系

    处理方式

    建立一个中间表, 专门存放关系, 至少具备两个字段分别指向老师和学生的主键,两个字段都是外键 如下:

    create table t_s_r(
        id int primary key auto_increment,
        t_id int,
        s_id int,
        foreign key(t_id) references teacher(id),
        foreign key(s_id) references student(id),
        unique key(t_id,s_id)
        );
        
     上表中id是可选的,问题是如何保证没有重复的关系 ?
    
     方式1:
     给两个字段设置为联合唯一  +  非空
     # 假设表已经存在了
     alter table t_s_r add unique key(t_id,s_id);
     # 创建表时指定 多字段联合唯一 
     create table t_s_r2(t_id int,s_id int,unique key(t_id,s_id));
     方式2:
     # 推荐 将中间的关系表 两个id 作为联合主键 同时具备了 唯一且非空约束 
     create table t_s_r3(t_id int,s_id int,primary key(t_id,s_id));
     
    

    案例

    create table student(id int primary key auto_increment,name char(10));
    create table teacher(id int primary key auto_increment,name char(10)); 
    create table t_s_r(
        t_id int,
        s_id int,
        foreign key(t_id) references teacher(id),
        foreign key(s_id) references student(id),
        primary key(t_id,s_id)
        );
     # 先插入 学生或是老师都可以 但是关系表一定是最后添加的 
     insert into teacher values(null,"bgon"),(null,"nike");
     
      insert into student values(null,"老王"),(null,"老李");
      
      # 老王被bgon教过 
      insert into t_s_r values(1,1);
       # nike教过老李 
      insert into t_s_r values(2,2);
       # nike教过老王 
      insert into t_s_r values(2,1);
    
    
    
     已知老师名称为bgon 请找出他教过那些学生			
    1.通过名字获取 bgon的id 			
    2.拿着id取关系表中拿到一堆学生的id			
    3.通过学生的id取出学生的信息			
     select id from teacher where name = "bgon";
     select s_id from t_s_r where t_id = 1;
     select  * from student where id = 1;
     
     # 子查询方式   把一条语句的结果作为另一条语句的条件!
     select  * from student where id = (select s_id from t_s_r where t_id = (select id from teacher where name = "bgon"));
      
      
      
     已知学生名为老李 请查询出 哪些老师教过他  				
    1.通过名字获取老李的id
    select id from student where name = "老李";
    2.拿着id去关系表 找出老师的id				
    select t_id from t_s_r where s_id = 2;
    3.通过老师的id取出老师的信息 			
    select name from teacher where id = x;
    
    
    # 子查询方式:
    select name from teacher where id = (
        select t_id from t_s_r where s_id = (
            select id from student where name = "老李"
        )
    );
    

    4. 一对一

    如一个客户对应一个学生, 站在两边看都是一对一的关系,

    处理方式,

    ​ 确定先后顺序, 将先存在的数据作为主表 ,后存在的作为从表

    ​ 使两个表的id保持一一对应

    ​ 方法1: 从表的id 即是主键又是外键

    ​ 方法2:从表的id设置为外键 并保证唯一

    案例:

    # 人员表
    create table person(
    	id int primary key auto_increment,
        name char(10),
        age int
    );
    # 详情表 
    create table person_info(
    	id int primary key,
        height float,
        weight float,
        foreign key(id) references person(id)
    );
    #再这样的关系中 必须先插入主表即person 拿到一个id 在添加详情表的数据  
    
    #将一条完整数拆分到不同表中,可以提高查询的效率,上述方式称之为垂直分表!
    
    
  • 相关阅读:
    fatal: unable to auto-detect email address (got 'tim@newton.(none)')的解决方法
    使用git命令提示“不是内部或外部命令
    Windows系统下安装 CMake
    php与mysql的连接
    array_merge和加号的区别
    可重入锁的实现
    redis scan
    redis主丛,哨兵和集群的区别
    高并发架构系列:Redis并发竞争key的解决方案详解
    laravel上传图片
  • 原文地址:https://www.cnblogs.com/Hades123/p/11181038.html
Copyright © 2011-2022 走看看