首页 > 数据库 >【MySQL】多表查询、分组求和、并对和排序、取top n

【MySQL】多表查询、分组求和、并对和排序、取top n

时间:2022-12-02 17:36:09浏览次数:35  
标签:test1 product 多表 top course score MySQL NULL id


查漏补缺MySQL的相关知识

  • ​​1. 有一个用户资金流水表(如上的sql代码),找出流水金额最多的前10个用户:​​
  • ​​2. 查询分组后的最大值,最小值所在的整行记录或者分组后的top n行的记录。​​
  • ​​3.TOP 1​​
  • ​​4.TOP N​​
  • ​​5.LEFT JOIN后面的on和where的区别​​

1. 有一个用户资金流水表(如上的sql代码),找出流水金额最多的前10个用户:

create table tb_user_finance (
id bigint primary key auto_increment,
uid bigint not null default 0 comment '用户id',
money decimal(10, 2) not null default 0.00 comment '资金流水',
type tinyint not null default 0 comment '1: 转账, 10: 提现, 20: 充值',
created_at timestamp not null default current_timestamp,
updated_at timestamp not null default current_timestamp on update current_timestamp,
key ix_uid (uid)
) engine = innodb default charset=utf8 comment '用户资金流水表';

insert into tb_user_finance (uid, money, type) values(10, 20, 1);
insert into tb_user_finance (uid, money, type) values(10, 20, 1);
insert into tb_user_finance (uid, money, type) values(10, 20, 1);
insert into tb_user_finance (uid, money, type) values(10, 200, 1);

insert into tb_user_finance (uid, money, type) values(20, 10, 10);
insert into tb_user_finance (uid, money, type) values(30, 20, 20);
insert into tb_user_finance (uid, money, type) values(30, 10, 20);

insert into tb_user_finance (uid, money, type) values(31, 10, 20);
insert into tb_user_finance (uid, money, type) values(32, 20, 20);
insert into tb_user_finance (uid, money, type) values(33, 45, 20);
insert into tb_user_finance (uid, money, type) values(34, 100, 20);
insert into tb_user_finance (uid, money, type) values(35, 1000, 20);
insert into tb_user_finance (uid, money, type) values(36, 1090, 20);

答案:

select uid, sum(money) as total from tb_user_finance group by uid order by total desc limit 10;

2. 查询分组后的最大值,最小值所在的整行记录或者分组后的top n行的记录。

2.1 创建测试表:

CREATE TABLE `test1` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(20) DEFAULT NULL,
`course` varchar(20) DEFAULT NULL,
`score` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8;

2.2 插入数据:

insert into test1(name,course,score)
values
('张三','语文',80),
('李四','语文',90),
('王五','语文',93),
('张三','数学',77),
('李四','数学',68),
('王五','数学',99),
('张三','英语',90),
('李四','英语',50),
('王五','英语',89);

查看结果:​​select * from test1;​

+----+--------+--------+-------+
| id | name | course | score |
+----+--------+--------+-------+
| 1 | 张三 | 语文 | 80 |
| 2 | 李四 | 语文 | 90 |
| 3 | 王五 | 语文 | 93 |
| 4 | 张三 | 数学 | 77 |
| 5 | 李四 | 数学 | 68 |
| 6 | 王五 | 数学 | 99 |
| 7 | 张三 | 英语 | 90 |
| 8 | 李四 | 英语 | 50 |
| 9 | 王五 | 英语 | 89 |
+----+--------+--------+-------+

3.TOP 1

查询每门课程分数最高的学生以及成绩
1、使用自连接【推荐】

select a.name, a.course, a.score from 
test1 a
join (select course, MAX(score) score from test1 group by course)b
on a.course = b.course and a.score = b.score;
+--------+--------+-------+
| name | course | score |
+--------+--------+-------+
| 王五 | 语文 | 93 |
| 王五 | 数学 | 99 |
| 张三 | 英语 | 90 |
+--------+--------+-------+

2、使用相关子查询

select name, course, score from test1 a 
where score=(select max(score) from test1 where a.course = test1.course);
+--------+--------+-------+
| name | course | score |
+--------+--------+-------+
| 王五 | 语文 | 93 |
| 王五 | 数学 | 99 |
| 张三 | 英语 | 90 |
+--------+--------+-------+

这个有点不理解,where条件:​​score = (select max(score) from test1 where a.course = test1.course)​​,我想这个应该是返回的每一科目的最大值,但是score使用等号连接表明后面应该是一个单独的值,但是直接运行这个语句就报错了,将括号内的where去掉的话 直接查出来的是整个表里的最大值了。

看到这里我试着自己写了一个,但是结果不对,不知道哪里出了问题,多了一行数据。
这条语句可以查出来每一科目的最高分,返回三个值:

select max(score) score from test1 group by course;
+-------+
| score |
+-------+
| 93 |
| 99 |
| 90 |
+-------+

然后再查询一次,添加一个where条件限制score:

select name, course, score from test1  
where score in (select max(score) score from test1 group by course);
+--------+--------+-------+
| name | course | score |
+--------+--------+-------+
| 李四 | 语文 | 90 | //这一行是哪来的????
| 王五 | 语文 | 93 |
| 王五 | 数学 | 99 |
| 张三 | 英语 | 90 |
+--------+--------+-------+

但是结果竟然多了一行,不知道这一行是从哪里多出来的??????这种写法我在项目中用过,但是好像没有发现这种会多一行不正确的数据的情况。

3.另一种相关子查询

select name, course, score from test1 a
where not exists (select 1 from test1 where a.course = test1.course and a.score < test1.score)
+--------+--------+-------+
| name | course | score |
+--------+--------+-------+
| 王五 | 语文 | 93 |
| 王五 | 数学 | 99 |
| 张三 | 英语 | 90 |
+--------+--------+-------+

这种子查询,我之前还真的没有遇到过,还能这么写,真的很神奇,有空需要多补补SQL方面的知识了。

4.TOP N

N>=1

查询每门课程​​前两名​​的学生以及成绩

  1. 使用union all

如果结果集比较小,可以用程序查询单个分组结果后拼凑,也可以使用union all

(select name, course, score from test1 where course='语文' order by score desc limit 2)
union all
(select name, course, score from test1 where course='数学' order by score desc limit 2)
union all
(select name, course, score from test1 where course='英语' order by score desc limit 2);
+--------+--------+-------+
| name | course | score |
+--------+--------+-------+
| 王五 | 语文 | 93 |
| 李四 | 语文 | 90 |
| 王五 | 数学 | 99 |
| 张三 | 数学 | 77 |
| 张三 | 英语 | 90 |
| 王五 | 英语 | 89 |
+--------+--------+-------+
  1. 自身左连接
select a.name, a.course, a.score from test1 a

left join test1 b

on a.course = b.course and a.score<b.score

group by a.name, a.course, a.score

having count(b.id)<2

order by a.course, a.score desc;
+--------+--------+-------+
| name | course | score |
+--------+--------+-------+
| 王五 | 数学 | 99 |
| 张三 | 数学 | 77 |
| 张三 | 英语 | 90 |
| 王五 | 英语 | 89 |
| 王五 | 语文 | 93 |
| 李四 | 语文 | 90 |
+--------+--------+-------+
  1. 相关子查询
select a.name, a.course, a.score from test1 a

where 2>(select count(*) from test1 where course = a.course and score > a.score)

order by a.course, a.score desc
+----+--------+--------+-------+
| id | name | course | score |
+----+--------+--------+-------+
| 6 | 王五 | 数学 | 99 |
| 4 | 张三 | 数学 | 77 |
| 7 | 张三 | 英语 | 90 |
| 9 | 王五 | 英语 | 89 |
| 3 | 王五 | 语文 | 93 |
| 2 | 李四 | 语文 | 90 |
+----+--------+--------+-------+

5.LEFT JOIN后面的on和where的区别

5.1创建测试表

CREATE TABLE `product` (
`id` int(10) unsigned NOT NULL auto_increment,
`amount` int(10) unsigned default NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=5 DEFAULT CHARSET=latin1;

CREATE TABLE `product_details` (
`id` int(10) unsigned NOT NULL,
`weight` int(10) unsigned default NULL,
`exist` int(10) unsigned default NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;

INSERT INTO product (id,amount)
VALUES (1,100),(2,200),(3,300),(4,400);

INSERT INTO product_details (id,weight,exist)
VALUES (2,22,0),(4,44,1),(5,55,0),(6,66,1);

表数据:

SELECT * FROM product;
+----+--------+
| id | amount |
+----+--------+
| 1 | 100 |
| 2 | 200 |
| 3 | 300 |
| 4 | 400 |
+----+--------+
SELECT * FROM product_details;
+----+--------+-------+
| id | weight | exist |
+----+--------+-------+
| 2 | 22 | 0 |
| 4 | 44 | 1 |
| 5 | 55 | 0 |
| 6 | 66 | 1 |
+----+--------+-------+
select * from product a

left join product_details b

on a.id = b.id
// 后面仍然可以添加where条件进行过滤
+----+--------+------+--------+-------+
| id | amount | id | weight | exist |
+----+--------+------+--------+-------+
| 1 | 100 | NULL | NULL | NULL |
| 2 | 200 | 2 | 22 | 0 |
| 3 | 300 | NULL | NULL | NULL |
| 4 | 400 | 4 | 44 | 1 |
+----+--------+------+--------+-------+

ON 子句和 WHERE 子句有什么不同?

一个问题:下面两个查询的结果集有什么不同么?

select * from product a

left join product_details b

on a.id = b.id

and b.id = 2
select * from product a

left join product_details b

on a.id = b.id

where b.id = 2

看运行结果就很明显的看出来区别了:

SELECT * FROM product LEFT JOIN product_details
ON (product.id = product_details.id)
AND product_details.id=2;
+----+--------+------+--------+-------+
| id | amount | id | weight | exist |
+----+--------+------+--------+-------+
| 1 | 100 | NULL | NULL | NULL |
| 2 | 200 | 2 | 22 | 0 |
| 3 | 300 | NULL | NULL | NULL |
| 4 | 400 | NULL | NULL | NULL |
+----+--------+------+--------+-------+
SELECT * FROM product LEFT JOIN product_details
ON (product.id = product_details.id)
WHERE product_details.id=2;
+----+--------+----+--------+-------+
| id | amount | id | weight | exist |
+----+--------+----+--------+-------+
| 2 | 200 | 2 | 22 | 0 |
+----+--------+----+--------+-------+
  1. 第一条查询使用 ON 条件决定了从 LEFT JOIN的 product_details表中检索符合的所有数据行。
  2. 第二条查询做了简单的LEFT JOIN,然后使用 WHERE 子句从 LEFT JOIN的数据中过滤掉不符合条件的数据行。

再来看一些示例,ON后面的AND条件是怎么匹配数据的:

SELECT * FROM product LEFT JOIN product_details
ON product.id = product_details.id
AND product.amount=100;
+----+--------+------+--------+-------+
| id | amount | id | weight | exist |
+----+--------+------+--------+-------+
| 1 | 100 | NULL | NULL | NULL |
| 2 | 200 | NULL | NULL | NULL |
| 3 | 300 | NULL | NULL | NULL |
| 4 | 400 | NULL | NULL | NULL |
+----+--------+------+--------+-------+

所有来自product表的数据行都被检索到了,但没有在product_details表中匹配到记录(​​product.id = product_details.id AND product.amount=100​​ 条件并没有匹配到任何数据)

SELECT * FROM product LEFT JOIN product_details
ON (product.id = product_details.id)
AND product.amount=200;
+----+--------+------+--------+-------+
| id | amount | id | weight | exist |
+----+--------+------+--------+-------+
| 1 | 100 | NULL | NULL | NULL |
| 2 | 200 | 2 | 22 | 0 |
| 3 | 300 | NULL | NULL | NULL |
| 4 | 400 | NULL | NULL | NULL |
+----+--------+------+--------+-------+

同样,所有来自product表的数据行都被检索到了,有一条数据匹配到了。

AND没有WHERE那么干脆,WHERE直接过滤掉右边表中为null的数据。

使用 WHERE … IS NULL 子句的 LEFT JOIN​这个有点不太理解​

如前所述,WHERE 条件查询发生在 匹配阶段之后,这意味着 WHERE … IS NULL 子句将从匹配阶段后的数据中过滤掉不满足匹配条件的数据行。

纸面上看起来很清楚,但是当你在 ON 子句中使用多个条件时就会感到困惑了。

我总结了一种简单的方式来理解上述情况:

  • 将 IS NULL 作为否定匹配条件
  • 使用 !(A and B) == !A OR !B 逻辑判断

看看下面的示例:

SELECT a.* FROM product a LEFT JOIN product_details b
ON a.id=b.id AND b.weight!=44 AND b.exist=0
WHERE b.id IS NULL;
+----+--------+
| id | amount |
+----+--------+
| 1 | 100 |
| 3 | 300 |
| 4 | 400 |
+----+--------+
这个我在运行时发现where条件加不加都是这个结果

 

标签:test1,product,多表,top,course,score,MySQL,NULL,id
From: https://blog.51cto.com/linmengmeng/5907334

相关文章

  • MySQL中关于金额数据进行运算求和/整数时精度丢失/清空表数据,自增从1开始
    关于MySQL的使用总结:1.金额字段类型为String时,进行求和运算2.查询DECIMAL类型数据小数点后精度缺失丢失为0的小数3.清空表的数据,id自增从1开始的方法1.金额字段类......
  • Mysql中的explain查看执行计划
    Mysql中的explain查看执行计划1、explain是什么?查看执行计划。2、怎么使用?explain+sql语句。3、执行计划包含的信息:id,select_type,table,type,possible_ke......
  • 苹果电脑装双系统 Parallels Desktop虚拟机你需要知道
    很多朋友用上了MacBook,但很多软件只能在Windows系统来使用,小白想要在MacBook上装Windows,需要花费大量的时间,所以在此,教大家在MacBook上安装虚拟机,来运行Windows系统。你是否......
  • sql server 2005新功能之TOP
    TOP的语法在SQL SERVER2005中有新增加的东西了,可以支持update,delete,数学表达式等,举例如下:建立一个表如下CreatetableMytable2(au_idint,......
  • 数据库面试题--Mysql
    MySQL1.什么是索引?2.索引是个什么样的数据结构呢?3.Hash索引和B+树索引有什么区别或者说优劣呢?4.在建立索引的时候,都有哪些需要考虑的因素呢?5.了解过哪些存储引......
  • 二进制部署MySQL8
    下载安装包:wgethttps://downloads.mysql.com/archives/get/p/23/file/mysql-8.0.30-linux-glibc2.12-x86_64.tar.xz下载依赖:yum-yinstalllibaio解压安装:tar-x......
  • Python高级-mini-web框架、添加路由、MySQL功能-笔记
    此篇文章是接着上一篇文章写的​​《Python高级-WSGI、mini-web框架-笔记》​​下一篇​​《Python高级-mini-web框架、添加log日志、路由支持正则-笔记》​​1.miniweb框......
  • linux++mysql命令行操作
    linux操作:切换root>sudosu  linux mysql命令行操作root@fossx:/opt/fossx/conf#sudomysql-uroot-pmysql>showdatabase;mysql>usefossx;mysql>showtab......
  • 使用DocXToPdfConverter把Docx转成Pdf文件
    1、安装DocXToPdfConverterInstall-PackageDocXToPdfConverter2、下载LibreOfficehttps://www.libreoffice.org/download/portable-versions/下载后安装软件。3、......
  • Mysql的安装和卸载(删除注册表的残余文件)
    对于会忘记数据库的密码,或者改了数据库密码后无法登录,可以把数据库先卸载在安装,不过原来建的数据可能丢失,因此在平时练习等操作的时候要有备份的好习惯。我看网上有很多说......