2023.10.25 mysql学习之开窗函数,多表查询

目录

1.外键约束

2. 连接查询,本质就是通过外键主键的关系,用join合并成一个大表,再去查询

 3.内,左右外连接

 4.连接查询练习

 5.子查询

6.自连接

 7.开窗函数


 

1.外键约束

外键概念: 在从表(多方)创建一个字段,引用主表(一方)的主键,对应的这个字段就是外键。

外键特点:
    1:从表外键的值是对主表主键的引用。
    2:从表外键类型,必须与主表主键类型一致。

外键约束关键字: foreign key

外键约束作用: 
        限制从表插入: 如果从表插入的外键值,在主表中不存在,就插入失败
        限制主表删除: 如果主表的主键值已经被从表引用,在主表删除该数据的时候,就删除失败
    
注意: 如果想要使用外键约束,存储引擎需要是InnoDB

建表时添加外键约束:  create table 从表名(...[CONSTRAINT] foreign key(外键名) references 主表名(主键名));
建表后添加外键约束:  alter table 从表名 add [CONSTRAINT] foreign key(外键名) references 主表名(主键名);

 

 

# 多表查询准备工作
create database day04;
use day04;
# 分类表
CREATE TABLE category
(
    cid   VARCHAR(32) PRIMARY KEY,
    cname VARCHAR(100) #分类名称
);
# 商品表
CREATE TABLE products
(
    pid         varchar(32) PRIMARY KEY,
    pname       VARCHAR(40),
    price       DOUBLE,
    category_id varchar(32),
    foreign key(category_id) references category(cid)
);


/*外键约束关键字: foreign key
外键约束作用:
		限制从表插入: 如果从表插入的外键值,在主表主键中不存在,就插入失败
		限制主表删除: 如果主表的主键值已经被从表引用,在主表删除该数据的时候,就删除失败
*/
# 演示外键约束限制从表插入数据
insert into products values ('p01','联想笔记本',9999,'c001'); # 插入失败
# 如何让从表插入数据成功? 方式1: 主表提前插入从表要引用的c001数据, 方式2: 从表插入的时候外键保留位空
# 方式1: 主表提前插入从表要引用的c001数据
insert into category values('c001','电脑');
# 再次插入数据
insert into products values ('p01','联想笔记本',9999,'c001'); # 插入成功
insert into products values ('p03','小米笔记本',5999,'c001'); # 插入成功
# 方式2: 从表插入的时候外键保留位空
insert into products values ('p02','联想笔记本',9999,null); # 插入成功


# 演示外键约束限制主表删除数据
delete from category where cid = 'c001';
# 如何让主表删除数据成功? 方式1: 直接把引用主表c001的从表数据删除, 方式2: 把引用主表c001d从表外键值改为空
# 方式1: 直接把引用主表c001的从表数据删除
delete from products where pid = 'p01';
# 方式2: 把引用主表c001d从表外键值改为空
update products set category_id = null where pid = 'p03';
# 主表再次删除
delete from category where cid = 'c001';

2. 连接查询,本质就是通过外键主键的关系,用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, '天山折梅手');

 3.内,左右外连接

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

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

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

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

内连接关键字: left outer join ... on

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

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

 4.连接查询练习

# 创建表
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 cname
from products as p inner join category as c ON p.category_id = c.cid
where flag = '1';


# 需求2:查询所有分类商品的个数,要求展示分类名称和个数
# 分析: 完成需求需要的数据来源几个表,如果是一个表直接查询,如果是多个表需要先合成一个表再去查
# 注意: 此题需要利用聚合函数(字段名)忽略null值的特点
select cid,count(category_id) 
from products p right outer join category as c ON p.category_id = c.cid 
group by cid;

# 需求3: 查询'服饰'这个分类对应的所有商品
select * 
from products p inner join category c ON p.category_id = c.cid 
where cname = '服饰';


# 需求4: 查询'服饰'这个分类上架的所有商品
select * 
from products p inner join category c ON p.category_id = c.cid 
where cname = '服饰' and flag = 1;


# 需求5: 查询'服饰'和'化妆品'这两个分类的所有商品
select * 
from products p inner join category c ON p.category_id = c.cid 
where cname = '服饰' or cname = '化妆品';

select * 
from products p inner join category c ON p.category_id = c.cid 
where cname in('服饰' ,'化妆品') ;

 5.子查询

# 子查询
# 需求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;

6.自连接

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

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

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

 7.开窗函数

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

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

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

        排序函数: row_number()   rank()   dense_rank()
                row_number():  巧记1234   唯一且连续
                rank()      :  巧记1224   并列不连续
                dense_rank():  巧记1223   并列且连续

# over() 配合聚合函数
/*
开窗函数好处: 能够减少大量的子查询语句,方便做各种计算
开窗函数原理: 保证输出的结果和输入的结果条数一致
开窗函数特点: 计算结果在原表末尾单独一列展示
*/
# 开窗函数和聚合函数配合使用
# 需求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) as rn,
       rank() OVER (ORDER BY salary desc) rk,
       dense_rank() OVER (ORDER BY salary desc) dr
from employee ;

# 需求2: 分别使用三种排序方式,根据每个部门员工的工资进行降序排名
select *,
       row_number() OVER (PARTITION by deptid ORDER BY salary desc ) as rn,
       rank() OVER (PARTITION by deptid ORDER BY salary desc) rk,
       dense_rank() OVER (PARTITION by deptid ORDER BY salary desc) dr
from employee ;


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

你可能感兴趣的:(mysql,学习,数据库)