MySQL 技巧与优化,行转列,排名查询

常见技巧与优化

查找重复记录

查找重复记录使用分组加筛选的方式实现

使用 sql 语句查询表中重复的商品记录,代码如下所示:

-- 使用分组加筛选的方式实现
select pname,count(pname)
from product
group by pname -- 根据名字进行分组
having count(pname)>1;-- 加上条件count(pname)>1说明是重复的

使用 sql 语句查询表中商品名称和数量都重复的记录,代码如下所示:

-- 两条完全一样的数据
select pname,num,count(*)
from product 
group by pname,num
having count(pname)>1 and count(num)>1

删除重复记录

使用 sql 语句删除商品名称重复的记录,只保留 id 最大的记录,代码如下所示:

-- 删除商品重复记录
-- p1内连接p2(同一张表)
-- 名字相等且id小的
delete p1 from product p1
inner join product p2
where p1.pid<p2.pid 
and p1.pname=p2.pname;
-- 查询商品表
select * from product;
-- 思路:自己连接自己
-- 加上where条件(名字相等,id小的)

选择随机记录

select * from 表名
order by rand()
limit 要随机选择记录的条数;

使用 sql 语句从 studentinfo 表中随机选择 1 名学生记录,代码如下所示:

-- select * from 表名
select * from studentinfo
order by rand()
-- limit 要随机选择记录的条数;
limit 1;

选择第 n 个最高记录

查询库存第 2 多的商品信息,代码如下所示:

-- 先拿到前两条数据,把它当做一张表
select * from product
order by num desc
limit 2
-- 去查询这个表
select * from 
(select * from product
order by num desc
limit 2) as t1 -- 起一个别名
order by num asc limit 1; -- 进行升序排序拿到第一条

比较两个表的数据

现有两张表,请你查询出 A1 字段中,存在 t_a 表,但是不存在 t_b 表的数据,表数据如下图所示:

-- select * from 表名a where 字段 not in
select * from t_a where a1 not in
-- (select 字段 from 表名b)
(select a1 from t_b)

上面代码先通过子查询得到 t_b 表中 a1 字段的所有值,然后在外部查询中使用 not in 进行排除。这种方式本身没有错,但是在大量数据(百万级以上)的情况下,尤其是 A1 字段带有索引的情况下,查询速度会非常慢。除了 not in 还可以使用 left join 实现,代码如下所示:

-- select 表a.* from 表a left join 表b
select t_a.* from t_a left join t_b
-- on 表a.字段 = 表b.字段
on t_a.A1 = t_b.A1
-- where 表b.字段 is null;
where t_b.B1 is null;

行转列

为了方便查看每个同学的成绩,使用 sql 语句查询出如下结果:
MySQL 技巧与优化,行转列,排名查询_第1张图片
实现代码如下:

-- 先分组,拿到名字
select user_name from test_tb_grade
group by user_name;
-- 最终代码如下:
select user_name,
	-- 拿到名字,根据名字,判断科目,根据科目拿到成绩的最大值,拿到张三数学的最大值
	MAX(CASE course WHEN '数学' THEN score ELSE 0 END ) 数学,-- ELSE 0 END如果没有成绩默认为0
	MAX(CASE course WHEN '语文' THEN score ELSE 0 END ) 语文,
	MAX(CASE course WHEN '英语' THEN score ELSE 0 END ) 英语,
	sum(score) 总分
from test_tb_grade
group by user_name;-- group by后只能显示聚合函数

exists 查询

EXISTS 关键字后面的参数是一个任意的子查询,系统对子查询进行运算以判断它是否返回行,如果至少返回一行,那么 EXISTS 的结果是 TRUE,此时外层查询语句将进行查询;如果子查询没有返回任何行,那么 EXISTS 返回的结果是 FALSE,此时外层语句将不进行查询。

查询成绩表中科目编号为 2 的考试成绩中是否存在不及格的学生,如果存在不及格的学生就将参加科目编号 2 考试的学生编号和成绩全部查询显示出来,代码如下所示:

SELECT StudentID,Exam FROM EXAM WHERE SubjectID=2 AND EXISTS (SELECT StudentID from EXAM WHERE
Exam<60)

all、any、some 查询

ALL 用在子查询前,通过比较运算符将一个表达式或列的值与子查询返回的一列值中的每一行进行比较,只要有一次比较的结果为 FALSE,则 ALL 测试返回 FALSE。

查询成绩比科目编号为“1”的这门课程的所有成绩都大的学生考试信息,代码如下所示:

-- 科目编号为一的成绩
select exam from exam where subjectid=1
-- 查询比科目一所有成绩都大的成绩=大于科目一最大值
-- all所有的
-- 只要有一次比较的结果为 FALSE,则 ALL 测试返回 FALSE。
select * from exam where exam>all(select exam from exam where subjectid=1);

在这里,>ALL 表示大于每一个值。换句话说,它表示大于最大值。例如,>ALL (1, 2, 3) 表示大于 3。

ANY 与子查询在一起使用时,按照比较运算符、表达式或字段对子查询的结果的每一行进行一次计算和比较。只要有一次满足条件,那么 ANY 的结果就是真。

查询成绩比科目编号为“1”的任意一个成绩都大的考试信息,代码如下所示:

-- 科目编号为一的成绩
select exam from exam where subjectid=1
-- 查询比科目一最小成绩都大的成绩=大于科目一最小值
-- 只要有一次满足条件,那么 ANY 的结果就是真。
select * from exam where exam>any(select exam from exam where subjectid=1);

SOME 与 ANY 的作用相同

注意:"=ANY"运算符与"IN"等效。"< >ANY"运算符则不同于"NOT IN"。"< >ANY(A,B,C)" 表示不等于 A,或者不等于 B,或者不等于 C。"NOT IN(A,B,C)“表示不等于 A、不等于 B 并且不等于 C。”< >ALL"与"NOT IN"表示的意思相同。

union 合并查询

MySQL union 操作符用于连接两个以上的 select 语句的结果组合到一个结果集合中并删除重复的数据。

union 在合并时会删除重复记录,相当于 distinct,如果不想去重,可以使用 union all。

-- select 字段 from 表a
-- union/union all
-- select 字段 from 表b(a和b的字段要一致)
select id,name,age from student
-- union 在合并时会删除重复记录,union all显示所有记录
union all
select id,name,age from teacher;

注意: 使用 union 查询有以下几个要点:

  • union 联合的两个 select 必须拥有相同数量的列;
  • 列必须有相似的数据类型;
  • 列的顺序必须相同;
  • union 因为要去重,效率远不如 union all。

排名查询

要求查询出,科目编号为 2 的科目成绩及排名,代码如下:

-- 查询出科目为二的成绩,并排序
select * from exam a where subjectid=2 order by a.exam desc;
select 
examid,studentid,exam,
-- “(select @rownum:=0) b”的作用是给变量 rownum 赋值为 0
@rownum:=@rownum+1 as rank
from exam a,(select @rownum:=0) b
where subjectid=2 order by a.exam desc;

运行结果如下:
MySQL 技巧与优化,行转列,排名查询_第2张图片

要求当出现并列名次时,后面的同学进行跳跃排名,实现代码如下:

select examid,studentid,exam,
CASE
-- 成绩一样@rownum不变
when @prev = a.exam then @rownum
-- 成绩不一样@rownum加一
when @prev := a.exam then @rownum := @rownum+1
end as rank
from exam a,(select @rownum:=0,@prev:=null) b
where SubjectId=2
order by a.exam desc;

运行结果如下:
MySQL 技巧与优化,行转列,排名查询_第3张图片

要求当出现并列名次时,后面的同学进行跳跃排名,实现代码如下:

select examid,studentid,exam,rank from
(select examid,studentid,exam,
-- 如果@rownum:=if(@prev=a.exam,@rownum,@inc)
@rownum:=if(@prev=a.exam,@rownum,@inc) as rank,
-- 如果一样每次加一
@inc:=@inc+1,
@prev:=a.exam
from exam a,(select @rownum:=0,@prev:=null,@inc:=1) b
where SubjectId=2
order by a.exam desc) tb;

运行结果如下:
MySQL 技巧与优化,行转列,排名查询_第4张图片

分组统计

查询每个城市每个月份的点击量,代码如下:

select city_name,state_month,sum(sx_sum) 
from sx_target
-- 根据城市、月份排序
group by city_name,state_month;
-- 使用group by 后select 后要么是group by后的字段,要么是聚合函数

果加一个条件,在现有统计基础上,显示每个城市的合计,以及所有城市的合计,以下代码用来实现合计功能:

select city_name,state_month,sum(sx_sum)
from sx_target
group by city_name,state_month with rollup

with rollup 用来在分组的基础上再进行统计,例如 group by a,b with rollup,先根据(a,b)统计,然后根据(a)=统计,然后根据(null)统计。

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