MySQL多表查询

目录

多表查询概述

连接查询

数据准备

交叉连接[慎用]

内连接(常用)

左外连接

右外连接

连接查询练习

准备数据

子查询

自连接

SQL底层执行顺序

开窗函数

配合聚合函数

配合排序函数


多表查询概述

表与表之间的关系:一对一,一对多,多对多

本质:把多个表通过主外键关联关系连接(join)合并成一个大表,再去查询

连接查询

数据准备

# 多表查询数据准备
# 创建hero表
CREATE TABLE hero
(
    hid       INT PRIMARY KEY,
    hname     VARCHAR(255),
    kongfu_id INT
);

# 创建kongfu表
CREATE TABLE kongfu
(
    kid   INT PRIMARY KEY,
    kname VARCHAR(255)
);

# 插入hero数据
INSERT INTO hero VALUES(1, '鸠摩智', 9),(3, '乔峰', 1),(4, '虚竹', 4),(5, '段誉', 12);

# 插入kongfu数据
INSERT INTO kongfu VALUES(1, '降龙十八掌'),(2, '乾坤大挪移'),(3, '猴子偷桃'),(4, '天山折梅手');

交叉连接[慎用]

交叉连接关键字: cross join

显式交叉连接格式: select * from 左表 cross join 右表;

隐式交叉连接格式: select * from 左表,右表;

注意: 交叉连接了解即可,因为它本质就是一个错误,又叫笛卡尔积(两个表记录数的乘积),实际工作中禁止使用

注意: 左表和右表没有特殊含义,只是在前面是左表,在后面的是右表

# 示例:
# 1.交叉连接(慎用)
# 显式
select * from hero cross join kongfu;
# 隐式
select * from hero , kongfu;

内连接(常用)

内连接关键字: inner join ... in

显式内连接格式: select * from 左表 inner join 右表 on 关联条件

隐式内连接格式: select * from 左表,右表 where 关联条件

#隐式

select * from hero,kongfu where hero.kongfu_id=kongfu.kid;

#显式

select * from hero inner join kongfu on hero.kongfu_id=kongfu.kid;

左外连接

左外连接关键字: left outer join ... on

左外连接格式: select * from 左表 left outer join 右表 on 关联条件

# 需求2: 获取所有的英雄名称和对应的功夫,没有功夫的null补全
# 左表所有数据展示,右表只展示和左表关联上的数据,其他用null补全

select hname,kname from hero left outer join kongfu on hero.kongfu_id=kongfu.kid

右外连接

左外连接关键字: right outer join ... on

左外连接格式: select * from 左表 right outer join 右表 on 关联条件

# 需求3: 获取所有的功夫和对应的英雄,没有英雄的null补全
# 左表所有数据展示,右表只展示和左表关联上的数据,其他用null补全
select kname,hname from hero right outer join kongfu on hero.kongfu_id=kongfu.kid;

连接查询练习

准备数据

# 创建表
drop table if exists products;
drop table if exists category;
# 分类表
CREATE TABLE category (
  cid VARCHAR(32) PRIMARY KEY ,
  cname VARCHAR(50)
);
# 商品表
CREATE TABLE products(
  pid VARCHAR(32) PRIMARY KEY ,
  pname VARCHAR(50),
  price INT,
  flag VARCHAR(2),    #是否上架标记为:1表示上架、0表示下架
  category_id VARCHAR(32),
  CONSTRAINT products_fk FOREIGN KEY (category_id) REFERENCES category (cid)
);

# 添加数据
#分类
INSERT INTO category(cid,cname) VALUES('c001','家电');
INSERT INTO category(cid,cname) VALUES('c002','服饰');
INSERT INTO category(cid,cname) VALUES('c003','化妆品');
INSERT INTO category(cid,cname) VALUES('c004','奢侈品');

#商品
INSERT INTO products(pid, pname,price,flag,category_id) VALUES('p001','联想',5000,'1','c001');
INSERT INTO products(pid, pname,price,flag,category_id) VALUES('p002','海尔',3000,'1','c001');
INSERT INTO products(pid, pname,price,flag,category_id) VALUES('p003','雷神',5000,'1','c001');
INSERT INTO products (pid, pname,price,flag,category_id) VALUES('p004','JACK JONES',800,'1','c002');
INSERT INTO products (pid, pname,price,flag,category_id) VALUES('p005','真维斯',200,'1','c002');
INSERT INTO products (pid, pname,price,flag,category_id) VALUES('p006','花花公子',440,'1','c002');
INSERT INTO products (pid, pname,price,flag,category_id) VALUES('p007','劲霸',2000,'1','c002');
INSERT INTO products (pid, pname,price,flag,category_id) VALUES('p008','香奈儿',800,'1','c003');
INSERT INTO products (pid, pname,price,flag,category_id) VALUES('p009','相宜本草',200,'1','c003');
INSERT INTO products (pid, pname,price,flag,category_id) VALUES('p010','华为',9999,'0',null);

示例

# 实战需求:
# 需求1.1:查询哪些分类的商品已经上架,要求展示分类id
# 分析: 完成需求需要的数据来源几个表,如果是一个表直接查询,如果是多个表需要先合成一个表再去查
select distinct category_id from products where flag=1;
# 需求1.2:查询哪些分类的商品已经上架,要求展示分类名称
# 分析: 完成需求需要的数据来源几个表,如果是一个表直接查询,如果是多个表需要先合成一个表再去查
select  distinct category_id,cname from products inner join category on products.category_id = category.cid where flag=1;
# 需求2:查询所有分类商品的个数,要求展示分类名称和个数
# 分析: 完成需求需要的数据来源几个表,如果是一个表直接查询,如果是多个表需要先合成一个表再去查
# 注意: 此题需要利用聚合函数(字段名)忽略null值的特点
select cname,count(category_id) from products right join  category on products.category_id = category.cid group by cname;
# 需求3: 查询'服饰'这个分类对应的所有商品
select * from category c inner join products p on c.cid = p.category_id where cname='服饰';
# 需求4: 查询'服饰'这个分类上架的所有商品
select * from category c inner join products p on c.cid = p.category_id where cname='服饰' and flag='1';
# 需求5: 查询'服饰'和'化妆品'这两个分类的所有商品
select * from category c inner join products p on c.cid = p.category_id where cname in('服饰','化妆品');
select *
from products p inner join category c ON p.category_id = c.cid
where cname = '服饰' or cname = '化妆品';

子查询

知识点

子查询: 一个select语句作为另一个select一句的一部分(表或者条件)

注意:子查询语句作为表使用的时候需要设置别名

# 需求1: 查询'服饰'这个分类对应的所有商品
# 方式1:作为条件
select * from products where category_id=(select cid from category where cname='服饰');
# 方式2:作为表
select *
from products p
    inner join (select * from category where cname = '服饰') c
    	on p.category_id=c.cid;

# 需求2: 查询'家电'和'化妆品'两个分类对应的所有商品
# 方式1:作为条件
select * from products where category_id in(select cid from category where cname in('服饰','化妆品'));
# 方式2:作为表
select * from products p inner join
(select * from category where cname in('服饰','化妆品')) c
on p.category_id=c.cid;

自连接

自连接: 本质就是内外连接,唯一区别是左表和右表是同一张表

注意: 自连接为了作为多个表使用,必须起别名进行区分

注意: 自连接应用场景比较局限,主要是省市县三级区域表或者上下级员工表

# 需求1: 查询河北省下的所有城市
select shi.title
from areas sheng
         inner join areas shi on shi.pid = sheng.id
where sheng.title = '河北省';

# 需求2: 查询邯郸市下的所有区县
select xian.title
from areas xian
         inner join areas shi on xian.pid = shi.id
where shi.title = '邯郸市';


# 子查询方式
# 需求1: 查询河北省下的所有城市
select title
from areas
where pid = (select id from areas where title = '河北省');
# 需求2: 查询邯郸市下的所有区县
select title
from areas
where pid = (select id from areas where title = '邯郸市');

SQL底层执行顺序

书写顺序: select -> from -> where -> group by ->having -> order by -> limit

执行顺序:  from -> where -> group by ->having -> select -> order by -> limit

SELECT
    category_id AS cid,
    COUNT(*)    AS cnt
FROM
    day03.product
WHERE
    category_id IS NOT NULL
GROUP BY
    cid
HAVING
    cnt > 1
ORDER BY
    cnt DESC
LIMIT 1;

开窗函数

开窗函数:MySQL8的新功能,保证输出结果的记录数和输入的数据记录数一致

开窗函数格式: ... 开窗函数 over(partition by 分组字段 order by 排序字段 asc|desc) ...

聚合函数:  sum()   max()  min()  avg()  count()

排序函数:  row_number():  巧记1234   唯一且连续

                  rank()   : 巧记1224  并列不连续

                 dense_rank() : 巧记1223  并列且连续

开窗函数好处: 能够减少大量的子查询语句,方便做各种计算
开窗函数原理: 保证输出的结果和输入的结果条数一致
开窗函数特点: 计算结果在原表末尾单独一列展示

配合聚合函数

# 开窗函数和聚合函数配合使用
# 需求1: 查询每个商品和最低价格的差额
# 子查询方式
SELECT
    pname,
    price,
    (select min(price) from products) as '最小价格',
    price - (select min(price) from products) as '差额'
FROM
    products;
# 开窗函数方式
SELECT
    pname,
    price,
    min(price) over() as '最小价格',
    price - min(price) over() as '差额'
FROM
    products;

# 需求2: 查询每个商品和最高价格的差额
SELECT
    pname,
    price,
    max(price) over() as '最高价格',
    price - max(price) over() as '差额'
FROM
    products;

# 需求3: 查询每个商品单价占总价的比例
SELECT
    pname,
    price,
    sum(price) over() as '总价格',
    price / sum(price) over() as '占比'
FROM
    products;

# 需求4: 查询每个商品和平均价格的差额
SELECT
    pname,
    price,
    avg(price) over() as '平均价格',
    price - avg(price) over() as '差额'
FROM
    products;


# 函数: round(数据,保留位数)
SELECT
    pname,
    price,
    round(avg(price) over(),2) as '平均价格',
    round(price - avg(price) over(),2) as '差额'
FROM
    products;


# 需求5: 查询商品名称商品价格以及商品总个数
SELECT
    pname,
    price,
    count(*) over() as '总个数'
FROM
    products;

配合排序函数

# 建表
create table employee (
    empid int,ename varchar(20) ,deptid int ,salary decimal(10,2)
);
# 插入数据
insert into employee values(1,'刘备',10,5500.00);
insert into employee values(2,'赵云',10,4500.00);
insert into employee values(3,'张飞',10,3500.00);
insert into employee values(4,'关羽',10,4500.00);

insert into employee values(5,'曹操',20,1900.00);
insert into employee values(6,'许褚',20,4800.00);
insert into employee values(7,'张辽',20,6500.00);
insert into employee values(8,'徐晃',20,14500.00);

insert into employee values(9,'孙权',30,44500.00);
insert into employee values(10,'周瑜',30,6500.00);
insert into employee values(11,'陆逊',30,7500.00);
# 需求1: 分别使用三种排序方式,根据所有员工的工资进行降序排名
 select *,row_number() over (order by salary desc) from employee ;
select *,rank() over (order by salary desc) from employee ;
select *,dense_rank() over (order by salary desc) from employee ;
# 需求2: 分别使用三种排序方式,根据每个部门员工的工资进行降序排名
select *,
       row_number() over (partition by deptid order by salary desc),
       rank() over (partition by deptid order by salary desc),
       dense_rank() over (partition by deptid order by salary desc)
 from employee;

# 综合需求: 查询每个部门工资最高的员工信息
select *
from (
         select *,
                rank() over (partition by deptid order by salary desc) rk
         from employee
     ) emp
where rk=1;

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值