目录
- 表与表之间只有三种关系
- 一对一
- 多对一
- 一对多
- 在MySQL的关系中没有多对一的说法
- 一对多、多对多,都叫一对多
外键是关系数据库中的一个概念,用于建立两个关系表之间的关联关系
它是一个列或一组列,用来指向另一个表的主键
外键在建立数据表与数据表之间的关系时起到了重要的作用
- 一对多关系,外键建在字段多的地方
- 在创建表的时候一定要先创建被关联表
- 在录入数据的时候必须先录入被关联表
以员工部门表为例:
- 在员工表
- 要考虑到员工表里面的一个员工是否能对应部门表里的多个部门
- 在部门表
- 要考虑到一个部门是否能对应员工表里多个员工
- 总结
- 员工表与部门表只是单项的一对多成立,那么员工表与部门表就是一对多关系
- create table dep(
- id int primary key auto_increment,
- dep_name char(16),
- dep_desc char(32)
- );
-
- #Query OK, 0 rows affected (0.82 sec)
- desc dep;
-
-
- +----------+----------+------+-----+---------+----------------+
- | Field | Type | Null | Key | Default | Extra |
- +----------+----------+------+-----+---------+----------------+
- | id | int(11) | NO | PRI | NULL | auto_increment |
- | dep_name | char(16) | YES | | NULL | |
- | dep_desc | char(32) | YES | | NULL | |
- +----------+----------+------+-----+---------+----------------+
- 3 rows in set (0.01 sec)
- create table emp(
- id int primary key auto_increment,
- emp_name char(16),
- emp_gender enum("male","female","others") default "male",
- dep_id int,
- foreign key (dep_id) references dep(id)
- );
-
- # Query OK, 0 rows affected (0.92 sec)
- desc emp;
-
-
- +------------+--------------------------------+------+-----+---------+----------------+
- | Field | Type | Null | Key | Default | Extra |
- +------------+--------------------------------+------+-----+---------+----------------+
- | id | int(11) | NO | PRI | NULL | auto_increment |
- | emp_name | char(16) | YES | | NULL | |
- | emp_gender | enum('male','female','others') | YES | | male | |
- | dep_id | int(11) | YES | MUL | NULL | |
- +------------+--------------------------------+------+-----+---------+----------------+
- 4 rows in set (0.00 sec)
- insert into dep(dep_name,dep_desc) values("sm运动社","日常活动"),("游戏社","休闲娱乐"),("技术部","能力提升"),("cp外交部","社交沟通");
-
- # Query OK, 4 rows affected (0.14 sec)
- # Records: 4 Duplicates: 0 Warnings: 0
- insert into emp(emp_name,emp_gender,dep_id) values("dream","male",1),("chimeng","female",4),("mengmeng","female",2),("drunkmeng","male",3);
-
- # Query OK, 4 rows affected (0.13 sec)
- # Records: 4 Duplicates: 0 Warnings: 0
- select * from dep;
-
-
- +----+-------------+--------------+
- | id | dep_name | dep_desc |
- +----+-------------+--------------+
- | 1 | sm运动社 | 日常活动 |
- | 2 | 游戏社 | 休闲娱乐 |
- | 3 | 技术部 | 能力提升 |
- | 4 | cp外交部 | 社交沟通 |
- +----+-------------+--------------+
- 4 rows in set (0.00 sec)
- select * from emp;
-
-
- +----+-----------+------------+--------+
- | id | emp_name | emp_gender | dep_id |
- +----+-----------+------------+--------+
- | 1 | dream | male | 1 |
- | 2 | chimeng | female | 4 |
- | 3 | mengmeng | female | 2 |
- | 4 | drunkmeng | male | 3 |
- +----+-----------+------------+--------+
- 4 rows in set (0.00 sec)
以图书表和作者为例
- 站在图书表角度
- 一本书可以有多个作者
- 站在作者表的角度
- 一个作者可以写多本书
- 总结
- 如果两个都可以那么就是多对多关系
***** 针对多对多的表关系,外键字段在第三张表中 *****
- id title price author_id
-
-
- create table book(
- id int primary key auto_increment,
- title varchar(32),
- price int,
- author_id int,
- foreign key(author_id) references author(id)
- on update cascade
- on delete cascade
-
- )
- id name age book_id
-
-
- create table author (
- id int primary key auto_increment,
- name varchar(32),
- age int,
- book_id int,
- foreign key(book_id) references book(id)
- on update cascade
- on delete cascade
- )
这种方式建表,由于外键具有”在创建表的时候一定要先创建被关联表“的特性,从而导致都无法创建彼此的关联表,针对多对多字段关系,不能在原有两张表的基础上创建外键,需要创建一张新表来建立两表的关系
- book
- id title price
-
- author
- id name age
-
- book_connect
- id boo_id author_id
- # 图书表
- create table book(
- id int primary key auto_increment,
- title varchar(32),
- price int
- );
-
- # 作者表
- create table author(
- id int primary key auto_increment,
- name varchar(32),
- age int
- );
-
- # 中转联系表
- create table book_connect(
- id int primary key auto_increment,
- author_id int,
- book_id int,
- foreign key(author_id) references author(id)
- on update cascade
- on delete cascade,
- foreign key(book_id) references book(id)
- on update cascade
- on delete cascade
- );
- desc book;
-
-
- +-------+-------------+------+-----+---------+----------------+
- | Field | Type | Null | Key | Default | Extra |
- +-------+-------------+------+-----+---------+----------------+
- | id | int(11) | NO | PRI | NULL | auto_increment |
- | title | varchar(32) | YES | | NULL | |
- | price | int(11) | YES | | NULL | |
- +-------+-------------+------+-----+---------+----------------+
- 3 rows in set (0.00 sec)
-
-
-
-
-
-
- desc author;
-
-
- +-------+-------------+------+-----+---------+----------------+
- | Field | Type | Null | Key | Default | Extra |
- +-------+-------------+------+-----+---------+----------------+
- | id | int(11) | NO | PRI | NULL | auto_increment |
- | name | varchar(32) | YES | | NULL | |
- | age | int(11) | YES | | NULL | |
- +-------+-------------+------+-----+---------+----------------+
- 3 rows in set (0.00 sec)
-
-
-
-
-
-
- desc book_connect;
-
-
- +-----------+---------+------+-----+---------+----------------+
- | Field | Type | Null | Key | Default | Extra |
- +-----------+---------+------+-----+---------+----------------+
- | id | int(11) | NO | PRI | NULL | auto_increment |
- | author_id | int(11) | YES | MUL | NULL | |
- | book_id | int(11) | YES | MUL | NULL | |
- +-----------+---------+------+-----+---------+----------------+
- 3 rows in set (0.00 sec)
- insert into book(title,price) values("西游记",18),("水浒传",29),("三国演义",99),("如何让富婆爱上你",999);
-
- insert into author(name,age) values("dream",18),("chimeng",28),("mengmeng",38);
-
- insert into book_connect(author_id,book_id) values(1,3),(2,1),(2,3),(1,1);
- select * from book;
-
-
- +----+--------------------------+-------+
- | id | title | price |
- +----+--------------------------+-------+
- | 1 | 西游记 | 18 |
- | 2 | 水浒传 | 29 |
- | 3 | 三国演义 | 99 |
- | 4 | 如何让富婆爱上你 | 999 |
- +----+--------------------------+-------+
- 4 rows in set (0.00 sec)
-
-
-
-
-
-
- select * from author;
-
-
- +----+----------+------+
- | id | name | age |
- +----+----------+------+
- | 1 | dream | 18 |
- | 2 | chimeng | 28 |
- | 3 | mengmeng | 38 |
- +----+----------+------+
- 3 rows in set (0.00 sec)
-
-
-
-
-
-
- select * from book_connect;
-
-
- +----+-----------+---------+
- | id | author_id | book_id |
- +----+-----------+---------+
- | 1 | 1 | 3 |
- | 2 | 2 | 1 |
- | 3 | 2 | 3 |
- | 4 | 1 | 1 |
- +----+-----------+---------+
- 4 rows in set (0.00 sec)
在MySQL的关系中没有多对一的说法
一对多、多对多 都叫做 一对多
如果一个表的字段特别多,每次查询又不是所有字段的数据都需要,那么可以将表一分为二
- authors
- id name age author_detail_id
-
- author_detail
- id phone addr
一对一,外键建在任意一方都可以,但是建议建立在查询频率较高的表内
- create table author_detail(
- id int primary key auto_increment,
- phone int,
- addr varchar(64)
- );
-
- create table authors(
- id int primary key auto_increment,
- name varchar(16),
- age int,
- author_detail_id int unique,
- foreign key(author_detail_id) references author_detail(id)
- on update cascade
- on delete cascade
- );
- desc author_detail;
-
-
- +-------+-------------+------+-----+---------+----------------+
- | Field | Type | Null | Key | Default | Extra |
- +-------+-------------+------+-----+---------+----------------+
- | id | int(11) | NO | PRI | NULL | auto_increment |
- | phone | int(11) | YES | | NULL | |
- | addr | varchar(64) | YES | | NULL | |
- +-------+-------------+------+-----+---------+----------------+
- 3 rows in set (0.01 sec)
-
-
-
-
-
-
-
- desc authors;
-
-
- +------------------+-------------+------+-----+---------+----------------+
- | Field | Type | Null | Key | Default | Extra |
- +------------------+-------------+------+-----+---------+----------------+
- | id | int(11) | NO | PRI | NULL | auto_increment |
- | name | varchar(16) | YES | | NULL | |
- | age | int(11) | YES | | NULL | |
- | author_detail_id | int(11) | YES | UNI | NULL | |
- +------------------+-------------+------+-----+---------+----------------+
- 4 rows in set (0.01 sec)
create database day04;
- create table dep(
- id int,
- name varchar(20)
- );
-
-
- CREATE TABLE emp (
- id INT PRIMARY KEY AUTO_INCREMENT,
- name VARCHAR(20),
- sex ENUM("male","female") NOT NULL DEFAULT "male",
- age INT,
- dep_id INT
- );
-
- insert into dep(id,name) values
- ("200","技术部"),
- ("201","人力资源"),
- ("202","销售部"),
- ("203","运营部"),
- ("204","售后部"),
- ("206","外交部");
-
- insert into emp(name,sex,age,dep_id) values
- ("dream","male",18,200),
- ("chimeng","female",18,201),
- ("menmgneg","male",38,202),
- ("hope","male",18,203),
- ("own","male",28,204),
- ("thdream","male",18,205);
先拿到部门和员工表拼接之后的结果
对拼接后的结果进行部门分组
- select * from emp inner join dep on emp.dep_id = dep.id;
-
-
- +----+----------+--------+------+--------+------+--------------+
- | id | name | sex | age | dep_id | id | name |
- +----+----------+--------+------+--------+------+--------------+
- | 1 | dream | male | 18 | 200 | 200 | 技术部 |
- | 2 | chimeng | female | 18 | 201 | 201 | 人力资源 |
- | 3 | menmgneg | male | 38 | 202 | 202 | 销售部 |
- | 4 | hope | male | 18 | 203 | 203 | 运营部 |
- | 5 | own | male | 28 | 204 | 204 | 售后部 |
- +----+----------+--------+------+--------+------+--------------+
- 5 rows in set (0.00 sec)
- select dep.name from emp inner join dep
- on emp.dep_id = dep.id
- group by dep.name
- having avg(age) > 25
- ;
-
-
-
- select dep.name from emp inner join dep
- on emp.dep_id = dep.id
- group by dep.name
- having avg(age) > 25
- ;
分步操作
- select name from dep where id in
- (select dep_id from emp group by dep_id
- having avg(age) > 25);
-
-
-
- +-----------+
- | name |
- +-----------+
- | 销售部 |
- | 售后部 |
- +-----------+
- 2 rows in set (0.00 sec)
Navicat可以充当多个数据库的客户端
返回的是查询到的数据的条数
- # -*-coding: Utf-8 -*-
- # @File : 01 简介 .py
- # author: Chimengmeng
- # blog_url : https://www.cnblogs.com/dream-ze/
- # Time:2023/7/2
- import pymysql
-
- # (1)链接数据库
- conn = pymysql.connect(
- # 指定 ip端口
- host='127.0.0.1',
- port=3306,
-
- # 指定用户名密码
- user='root',
- password='1314521',
-
- # 指定数据库
- database='day04',
-
- # 指定编码
- charset='utf8'
- )
-
- # (2)创建游标对象 - 执行命令 对象
- cursor = conn.cursor()
-
- # (3)创建SQL语句
- sql = 'select * from emp;'
-
- # (4)游标对象执行SQL语句
- # 【1】execute: 返回的是数据的条数
- res = cursor.execute(sql)
- print(res) # 6
返回查询到的第一条数据
- # -*-coding: Utf-8 -*-
- # @File : 01 简介 .py
- # author: Chimengmeng
- # blog_url : https://www.cnblogs.com/dream-ze/
- # Time:2023/7/2
- import pymysql
-
- # (1)链接数据库
- conn = pymysql.connect(
- # 指定 ip端口
- host='127.0.0.1',
- port=3306,
-
- # 指定用户名密码
- user='root',
- password='1314521',
-
- # 指定数据库
- database='day04',
-
- # 指定编码
- charset='utf8'
- )
-
- # (2)创建游标对象 - 执行命令 对象
- cursor = conn.cursor()
-
- # (3)创建SQL语句
- sql = 'select * from emp;'
-
- # (4)游标对象执行SQL语句
- # 【2】
- res = cursor.execute(sql) # 先执行这条语句
- res1 = cursor.fetchone() # 拿第一条数据
- print(res1) # (1, 'dream', 'male', 18, 200)
- # -*-coding: Utf-8 -*-
- # @File : 01 简介 .py
- # author: Chimengmeng
- # blog_url : https://www.cnblogs.com/dream-ze/
- # Time:2023/7/2
- import pymysql
-
- # (1)链接数据库
- conn = pymysql.connect(
- # 指定 ip端口
- host='127.0.0.1',
- port=3306,
-
- # 指定用户名密码
- user='root',
- password='1314521',
-
- # 指定数据库
- database='day04',
-
- # 指定编码
- charset='utf8'
- )
-
- # (2)创建游标对象 - 执行命令 对象
- # cursor=pymysql.cursors.DictCursor:将查询的参数以字典的形式返回
- cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
-
- # (3)创建SQL语句
- sql = 'select * from emp;'
-
- # (4)游标对象执行SQL语句
- # 【2】
- res = cursor.execute(sql) # 先执行这条语句
- res1 = cursor.fetchone() # 拿一条数据 - 返回的是字典
- print(res1)
-
- # {'id': 1, 'name': 'dream', 'sex': 'male', 'age': 18, 'dep_id': 200}
- # -*-coding: Utf-8 -*-
- # @File : 01 简介 .py
- # author: Chimengmeng
- # blog_url : https://www.cnblogs.com/dream-ze/
- # Time:2023/7/2
- import pymysql
-
- # (1)链接数据库
- conn = pymysql.connect(
- # 指定 ip端口
- host='127.0.0.1',
- port=3306,
-
- # 指定用户名密码
- user='root',
- password='1314521',
-
- # 指定数据库
- database='day04',
-
- # 指定编码
- charset='utf8'
- )
-
- # (2)创建游标对象 - 执行命令 对象
- # cursor=pymysql.cursors.DictCursor:将查询的参数以字典的形式返回
- cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
-
- # (3)创建SQL语句
- sql = 'select * from emp;'
-
- # (4)游标对象执行SQL语句
- # 【2】
- res = cursor.execute(sql) # 先执行这条语句
- res2 = cursor.fetchall() # 拿一条数据 - 返回的是列表
- print(res2)
-
- # [{'id': 2, 'name': 'chimeng', 'sex': 'female', 'age': 18, 'dep_id': 201}, {'id': 3, 'name': 'menmgneg', 'sex': 'male', 'age': 38, 'dep_id': 202}, {'id': 4, 'name': 'hope', 'sex': 'male', 'age': 18, 'dep_id': 203}, {'id': 5, 'name': 'own', 'sex': 'male', 'age': 28, 'dep_id': 204}, {'id': 6, 'name': 'thdream', 'sex': 'male', 'age': 18, 'dep_id': 205}]
- # -*-coding: Utf-8 -*-
- # @File : 01 简介 .py
- # author: Chimengmeng
- # blog_url : https://www.cnblogs.com/dream-ze/
- # Time:2023/7/2
- import pymysql
-
- # (1)链接数据库
- conn = pymysql.connect(
- # 指定 ip端口
- host='127.0.0.1',
- port=3306,
-
- # 指定用户名密码
- user='root',
- password='1314521',
-
- # 指定数据库
- database='day04',
-
- # 指定编码
- charset='utf8'
- )
-
- # (2)创建游标对象 - 执行命令 对象
- # cursor=pymysql.cursors.DictCursor:将查询的参数以字典的形式返回
- cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
-
- # (3)创建SQL语句
- sql = 'select * from emp;'
-
- # (4)游标对象执行SQL语句
- # 【2】
- res = cursor.execute(sql) # 先执行这条语句
- res3 = cursor.fetchmany(2) # 拿指定条数据 - 返回的是列表
- print(res3)
-
- # [{'id': 1, 'name': 'dream', 'sex': 'male', 'age': 18, 'dep_id': 200}, {'id': 2, 'name': 'chimeng', 'sex': 'female', 'age': 18, 'dep_id': 201}]
- # -*-coding: Utf-8 -*-
- # @File : 02 scroll方法 .py
- # author: Chimengmeng
- # blog_url : https://www.cnblogs.com/dream-ze/
- # Time:2023/7/2
- import pymysql
-
-
- def connect_mysql():
- import pymysql
-
- # (1)链接数据库
- conn = pymysql.connect(
- # 指定 ip端口
- host='127.0.0.1',
- port=3306,
-
- # 指定用户名密码
- user='root',
- password='1314521',
-
- # 指定数据库
- database='day04',
-
- # 指定编码
- charset='utf8'
- )
-
- return conn
-
-
- def create_cursor():
- conn = connect_mysql()
- # (2)创建游标对象 - 执行命令 对象
- # cursor=pymysql.cursors.DictCursor:将查询的参数以字典的形式返回
- cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
-
- # (3)创建SQL语句
- sql = 'select * from emp;'
-
- cursor.execute(sql)
- return cursor
-
-
- def main_fetch():
- cursor = create_cursor()
- print('第一次fetchone:>>>>', cursor.fetchone())
- print('第二次fetchone:>>>>', cursor.fetchone())
- print('fetchall:>>>>', cursor.fetchall())
-
- # 第一次fetchone:>>>> {'id': 1, 'name': 'dream', 'sex': 'male', 'age': 18, 'dep_id': 200}
- # 第二次fetchone:>>>> {'id': 2, 'name': 'chimeng', 'sex': 'female', 'age': 18, 'dep_id': 201}
- # fetchall:>>>> [{'id': 3, 'name': 'menmgneg', 'sex': 'male', 'age': 38, 'dep_id': 202}, {'id': 4, 'name': 'hope', 'sex': 'male', 'age': 18, 'dep_id': 203}, {'id': 5, 'name': 'own', 'sex': 'male', 'age': 28, 'dep_id': 204}, {'id': 6, 'name': 'thdream', 'sex': 'male', 'age': 18, 'dep_id': 205}]
- # 当已经执行了 fetchone 时 光标就会向下移动一次 所以最后的查询是从索引 3 开始的
-
-
- def main_scroll():
- cursor = create_cursor()
-
- # 【1】 cursor.scroll(1, 'absolute'))
- # 控制 光标的移动
- # print('第一次fetchone:>>>>', cursor.fetchone())
- # print('第二次fetchone:>>>>', cursor.fetchone())
- # print('第二次fetchone:>>>>', cursor.scroll(1, 'relative')) # 相当于光标所在的位置向后移动一次
- # print('fetchall:>>>>', cursor.fetchall())
-
- # 第一次fetchone:>>>> {'id': 1, 'name': 'dream', 'sex': 'male', 'age': 18, 'dep_id': 200}
- # 第二次fetchone:>>>> {'id': 2, 'name': 'chimeng', 'sex': 'female', 'age': 18, 'dep_id': 201}
- # 第二次fetchone:>>>> None
- # fetchall:>>>> [{'id': 4, 'name': 'hope', 'sex': 'male', 'age': 18, 'dep_id': 203}, {'id': 5, 'name': 'own', 'sex': 'male', 'age': 28, 'dep_id': 204}, {'id': 6, 'name': 'thdream', 'sex': 'male', 'age': 18, 'dep_id': 205}]
- # 当遇到 scroll 的时候 ,光标向下移动了一次,所以最后的索引是从4 开始的
-
- # 【2】 cursor.scroll(1, 'absolute'))
- print('第一次fetchone:>>>>', cursor.fetchone())
- print('第二次fetchone:>>>>', cursor.fetchone())
- print('第二次fetchone:>>>>', cursor.scroll(1, 'absolute')) # 相当于光标所在的位置向后移动一次
- print('fetchall:>>>>', cursor.fetchall())
-
- # 第一次fetchone:>>>> {'id': 1, 'name': 'dream', 'sex': 'male', 'age': 18, 'dep_id': 200}
- # 第二次fetchone:>>>> {'id': 2, 'name': 'chimeng', 'sex': 'female', 'age': 18, 'dep_id': 201}
- # 第二次fetchone:>>>> None
- # fetchall:>>>> [{'id': 2, 'name': 'chimeng', 'sex': 'female', 'age': 18, 'dep_id': 201}, {'id': 3, 'name': 'menmgneg', 'sex': 'male', 'age': 38, 'dep_id': 202}, {'id': 4, 'name': 'hope', 'sex': 'male', 'age': 18, 'dep_id': 203}, {'id': 5, 'name': 'own', 'sex': 'male', 'age': 28, 'dep_id': 204}, {'id': 6, 'name': 'thdream', 'sex': 'male', 'age': 18, 'dep_id': 205}]
- # 相对于数据的起始位置向后移动一位
-
-
- if __name__ == '__main__':
- main_scroll()