Bootstrap

SQL实现一对多、多对多建表与查询

1 一对多、多对多、自关联多对多、自关联一对多场景描述

之前在做网页开发的时候一直用Sqlalchemy来操作数据库,当我用到自关联多对多和自关联一对多的时候,sqlalchemy的配置会有一些辅助的参数,配置起来很麻烦,灵机一动我就想了一下,为什么不能直接写sql呢!!!虽然sql语句写起来不是很方便,但是sql才是各种ORM框架的基本,话不多说,开搞

1.1 概念

本节内容是自己对于数据库表直接关系的一些个人理解,非官方语言描述,都是大白话
  • 一对多关系:
生活中有很多一对多关系的实例,比如一个教师内有n个上课的学生,教师是一方,学生是多方
工作中每个职位的多名员工,职位是一方,同属这个职位的员工是多方
项目开发中可能会遇到一些权限的划分,权限规则role 和 同一个role下面的user也是一个一对多的关系
  • 多对多关系:
学生选课系统,所选的课程与选课的学生就是一个多对多关系,多对多关系不能通过主键与外键的关联实现,
必须建一个三方表,三方表中存放两个关系表的主键,将一个多对多的关系转换成两个一对多的关系。
在第三张表中通过外键关联另外两张表的主键
  • 自关联一对多关系:
新闻网站、社交网站的评论系统就是一个自关联的一对多关系,一条评论可以有n条子评论,
但是一条子评论只能有一条父评论
  • 自关联多对多关系:
典型的自关联多对多是用户关注表,比如微博、等社交软件,用户表之间会存在自关联多对多关系,
一个用户可以有n个粉丝,同样一个用户也可以被N个用户关注

1.2 创建表格& 插入数据

  • 一对多关系:
# 1:role表 M:user表
# 新建roles表 
create table roles(
	id int primary key auto_increment,
	name varchar(10) unique not null
)charset=utf8; 
# 插入一些测试数据
insert into roles (`name`) values ("admin"),("other");
# 新建users表
CREATE TABLE `test`.`users`  (
  `id` int(0) NOT NULL AUTO_INCREMENT,
  `uname` varchar(255) NULL,
  `role_id` int(0) NULL,
  PRIMARY KEY (`id`),
  CONSTRAINT `role_id` FOREIGN KEY (`role_id`) REFERENCES `test`.`roles` (`id`)
);
# 插入一些测试数据
insert into users (`uname`,`r_id`) values ("张三",1),("里斯",2),("王刚",2),
("金鹏展翅",2),("大鸟飞天",2),("花花公子",2),("犯上作乱",2),("天兵天将",2),("非你莫属",2)

  • 多对多关系:
# 用户表、兴趣爱好表、user_habbit_table
# 新建兴趣爱好表
create table habbits (
	id int primary key auto_increment,
	hname varchar(30)
)charset=utf8;
# 插入一些测试数据
insert into habbits (hname) 
values ("打篮球"),("打台球"),("踢足球"),("乒乓球"),("橄榄球"),("羽毛球"),("网球")

# 新建用户与爱好关联表
CREATE TABLE `test`.`union_table`  (
  `id` int(0) NOT NULL AUTO_INCREMENT,
  `user_id` int(0) NULL,
  `h_id` int(0) NULL,
  PRIMARY KEY (`id`),
  CONSTRAINT `user_id` FOREIGN KEY (`user_id`) REFERENCES `test`.`users` (`id`),
  CONSTRAINT `habbit_id` FOREIGN KEY (`h_id`) REFERENCES `test`.`habbits` (`id`)
);
# 插入测试数据
insert into union_table (user_id,h_id) values (1,1),(1,2),(1,5),(2,4),(2,3),(2,7),(3,1),(3,2),(3,3),(3,6)
  • 自关联一对多关系:
# 创建评论表
create table comments (
	id int primary key auto_increment,
	content varchar(200),
	parent_id int,
	ctime TIMESTAMP default now()
) charset =utf8;
# 插入测试数据
insert into comments (content,parent_id) values ("今天天气真好",NULL),
("我觉得文章写的不错",NULL),
("为什么今天库里投篮不准了",NULL),
("明天上学的路上我要买一张报纸",NULL),
("你是一个乖宝宝",NULL)

# 添加子评论
insert into comments (content,parent_id) values ("天龙八部好看",1),
("乔丹的篮球打的很棒",2),
("明天是周末,可以出去玩",3),
("想想明天的生活也会很好",4),
("今天吃了三个包子",5)


insert into comments (content,parent_id) values ("潘金莲怒打西门庆",1),
("吕布死后,关羽见到谁都是匹夫",2),
("当你慢慢学会设计数据路的时候",3),
("天气虽然很好但是我也不想出去打球",4),
("明明就是一个大骗子非要说自己是好人",5)
  • 自关联多对多关系:
# 自关联一对多可以在一张表中表现出关系,但是自关联多对多需要借助一张follows表才能表现结构
# 新建一张follows表
CREATE TABLE `test`.`Untitled`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `follower_id` int(11) NULL DEFAULT NULL,
  `followed_id` int(11) NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE,
  INDEX `follower_id`(`follower_id`) USING BTREE,
  INDEX `followed_id`(`followed_id`) USING BTREE,
  CONSTRAINT `followed_id` FOREIGN KEY (`followed_id`) REFERENCES `test`.`users` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT,
  CONSTRAINT `follower_id` FOREIGN KEY (`follower_id`) REFERENCES `test`.`users` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;
# 插入一些测试数据
insert into follows (follower_id,followed_id) 
values (1,2),(1,3),(1,4),
(2,5),(2,7),(2,1),
(3,6),(3,1),(3,2),
(4,1),(4,2),(4,6)

1.3 查询数据

  • 一对多查询
#  查询某一种角色的所有用户
select * from users 
where r_id = (
select id from roles where name = "other"
)
  • 多对多查询
# 查询某个人的所有爱好
select * from habbits
where id in (
select h_id from union_table where user_id = (
select id from users where uname like "张三"))

# 查询某一种爱好有哪些用户喜欢
select * from users 
where id in (
select user_id from union_table where h_id = (
select id from habbits where hname like "打篮球"
)
)

  • 自关联一对多查询
# 查询一级评论
select * from comments 
where parent_id is null

# 查询一个父级评论的所有子评论
select * from comments
where parent_id = 1
  • 自关联多对多查询
# 查询一个用户的所有粉丝
select u.id,u.uname from follows as f 
inner join users as u on u.id = f.follower_id
where f.followed_id = (
select id from users where uname = "张三"
)

1.4 总结

本文用的数据库是mysql
后续会写一个详细的sqlalchemy处理关系表的文章,比对一下两种方法的优劣势

;