首页 > 数据库 >分享6个SQL小技巧

分享6个SQL小技巧

时间:2023-06-17 22:22:44浏览次数:50  
标签:join 技巧 course score SQL 分享 where id select

原创:扣钉日记(微信公众号ID:codelogs),欢迎分享,非公众号转载保留此声明。

简介

经常有小哥发出疑问,SQL还能这么写?我经常笑着回应,SQL确实可以这么写。其实SQL学起来简单,用起来也简单,但它还是能写出很多变化,这些变化读懂它不难,但要自己Get到这些变化,可能需要想一会或在网上找一会。

各种join

关于join的介绍,比较流行的就是这张图了,如下:
join
简单的解释如下:

  • join:内联接,也可写成inner join,取两表关联字段相交的那部分数据。
  • left join:左外联接,也可写成left outer join,取左表数据,若关联不到右表,右表为空。
  • right join:右外联接,也可写成right outer join,取右表数据,若关联不到左表,左表为空。
  • full join:全联接,也可写成full outer join,取左表和右表中所有数据。

但注意上图,里面还有几个Key is null的情况,它可以将两表相交的那部分数据排除掉!
也正是因为这个特性,一种很常见的SQL技巧是,用left join可替换not existsnot in等相关子查询,如下:

select * from tableA A 
where not exists (select 1 from tableB B where B.Key=A.Key)

-- 使用left join的等价写法
select * from tableA A 
left join tableB B on B.Key=A.Key where B.Key is null

也比较好理解,只有当左表的数据在右表中不存在时,B.Key is null才成立。

查询各类别最大的那条数据

比如在学籍管理系统中,有一类很常见的需求,查询每学科分数最高的那条数据,有如下几种写法:

select * from stu_score s 
where s.course_id in ('Maths','English') 
and s.score = (select max(score) from stu_score s1 where s1.course_id = s.course_id)

比较好理解,考分最高其实就是过滤出分数等于最大分数的记录。

在不能使用子查询的场景下,也可转换成join,如下:

select * from stu_score s 
left join stu_score s1 on s1.course_id = s.course_id and s1.score > s.score
where s.course_id in ('Maths','English') and s1.id is null

这和前面用left join改写not exists类似,通过s1.id is null过滤出left join关联条件不满足时的数据,什么情况left join关联条件不满足呢,当s表记录是分数最大的那条记录时,s1.score > s.score条件自然就不成立了,所以它过滤出来的数据,就是学科中分数最大的那条记录。

一直以来,我看到SQL的join的条件大都是a.field=b.field这种形式,导致我以为join只能写等值条件,实际上,join条件和where中一样,支持><likein甚至是exists子查询等条件,大家也一定不要忽视了这一点。

上面场景还有一种写法,就是使用group by先把各学科最大分算出来,然后再关联出相应数据,如下:

select * from
(select s.course_id,max(s.score) max_score stu_score s where s.course_id in ('Maths','English') group by s.course_id) sm
join stu_score s1 on s1.course_id = sm.course_id and s1.score=sm.max_score

查询各类别top n数据

比如在学籍管理系统中,查询每学科分数前5的记录,类似这种需求也很常见,比较简单明了的写法如下:

select * from stu_score s 
where s.course_id in ('Maths','English') 
and (select count(*) from stu_score s1 where s1.course_id = s.course_id and s1.score > s.score) < 5

很显然,第5名只有4个学生比它分数高,第4名只有3个学生比它分数高,依此类推。

LATERAL join

MySQL8为join提供了一个新的语法LATERAL,使得被关联表B在联接前可以先根据关联表A的字段过滤一下,然后再进行关联。

这个新的语法,可以非常简单的解决上面top n的场景,如下:

select * from stu_course c 
join LATERAL (select * from stu_score s where c.course_id = s.course_id order by s.score desc limit 5) s1 on c.course_id = s1.course_id
where c.course_name in ('数学','英语')

如上,每个学科查询出它的前5名记录,然后再关联起来。

统计多个数量

使用count(*)可以统计数量,但有些场景想统计多个数量,如统计1天内单量、1周内单量、1月内单量。

count(*)的话,需要扫描3次表,如下:

select count(*) from order where add_time > DATE_SUB(now(), INTERVAL 1 DAY)
union all
select count(*) from order where add_time > DATE_SUB(now(), INTERVAL 1 WEEK)
union all
select count(*) from order where add_time > DATE_SUB(now(), INTERVAL 1 MONTH)

其实扫描一次表也可以实现,用sum来代替count即可,如下:

select sum(IF(add_time > DATE_SUB(now(), INTERVAL 1 DAY)), 1, 0) day_order_cnt,
sum(IF(add_time > DATE_SUB(now(), INTERVAL 1 WEEK)), 1, 0) week_order_cnt,
sum(IF(add_time > DATE_SUB(now(), INTERVAL 1 MONTH)), 1, 0) month_order_cnt
from order where add_time > DATE_SUB(now(), INTERVAL 1 MONTH)

IF是mysql的逻辑判断函数,当其第一个参数为true时,返回第二个参数值,即1,否则返回第三个参数值0,然后再使用sum加起来,就是各条件为true的数量了。

数据对比

有时,我们需要对比两个表的数据是否一致,最简单的方法,就是在两边查询出结果集,然后逐行逐字段对比。

但是这样对比的效率比较低下,因为它要两个表的数据全都查出来,其实我们不一定非要都查出来,只要计算出一个hash值,然后对比hash值即可,如下:

select BIT_XOR(CRC32(CONCAT(ifnull(column1,''),ifnull(column2,'')))) as checksum 
from table_name where add_time > '2020-02-20' and add_time < '2020-02-21';  

先使用CONCAT将要对比的列连接起来,然后使用CRC32或MD5计算hash值,最后使用聚合函数BIT_XOR将多行hash值异或合并为一个hash值。

这个查询最终只会返回1条hash值,查询数据量大大减少了,数据对比效率就上去了。

总结

SQL看起来简单,其实有很多细节与技巧,如果你也有其它技巧,欢迎留言分享讨论

标签:join,技巧,course,score,SQL,分享,where,id,select
From: https://www.cnblogs.com/codelogs/p/17488367.html

相关文章

  • nas docker安装mysql 整理
    前提:群晖nas nas已安装docker一、下载MySQL由于查询注册表失败,所以通过ssh工具xshell连接docker来下载MySQL;xshell下载地址:https://www.xshell.com/zh/free-for-home-school/ (填写相关信息,下载免费版本)nas启用ssh功能: xshell连接nas 1.切换到rootsudo-i ......
  • 初识SQL语句
    1、create使用CREATEDATABASEtest//j建库ON{name=test_data,FIlENAME='D:test_data.mdf',SIZE=3,MAXSIZE=5,FILEGROWTH=1}LOGon{name=test_log,FILENAME='D:\test_data_log',SIZE=1MB,MAXSIZE=2MB,FILEGROWTH=1}go......
  • C++增删改查+MySQL
    右键项目属性 点击编辑 选择mysql安装目录的include文件夹 包含了头文件之后包含库文件 点击编辑在mysql安装路径下面选择  选择编辑之后添加看这个文件下面有没有这个输入的文件依赖 有就代表成功连接数据库 创建数据库:createdatabasestudent_mana......
  • mysql5.7密码策略说明
    一、mysql5.7在创建用户设置密码时提示“ERROR1819(HY000):Yourpassworddoesnotsatisfythecurrentpolicyrequirements”createuser'tom'@localhostidentifiedby'123456';ERROR1819(HY000):Yourpassworddoesnotsatisfythecurrentpolicyrequi......
  • 马拉松资讯获取及报名渠道分享
    1、马拉松赛事1.1马拉松赛事组别马拉松赛事分为全程马拉松(42.195公里)、半程马拉松(21.0975公里)、短程跑(健康跑、亲子跑、家庭跑之类的)这几个类别,大型田协认证赛事三种类型都有,大多中小型赛事长距离支持半马,更小型公司冠名的商业路跑仅有健康跑、徒步等。以2023兰州马拉松为例,是国际......
  • 工具使用技巧
    vscode快捷键1.alt+上下箭头-->移动本行2.alt+shift+上下箭头-->复制本行并前后粘贴3.ctrl+d-->多个选中当前选中的4.alt+鼠标-->光标多行选中一个一个的加5.shift+alt+鼠标-->光标多行选中类似shift配合鼠标选桌面文件6.Ctrl+Shift+P,F1:显示......
  • SQL语句_数字运算
    Store_Info表:store_namesalesdateA50001-01-2000B20002-01-2000A150002-10-2000D100003-08-2000AVG(平均):SELECT AVG(sales)FROMStory_InfoWHEREstore_name='A' 查商品A的平均售价。AVG(sales)1000COUNT(计数):SELECTCOUNT......
  • 数据验证序列自动去重(Excel技巧集团)
    数据验证》序列》来源,输入一行或一列数据,就可以从下拉选项中选取需要输入的数据。当数据源是一列带有重复值的数据时,下拉选项里也会忠实地显示所有内容,包括重复的内容。如果想要去重,就必须添加辅助列。但那都是过去子,现在,就只要直接…… ......
  • 批量插入图片(Excel技巧集团)
    以前插入图片以后,图片是在单元格上方的,且同时插入多张图片后,那效果……不忍目睹。但是现在,多出来了个【旋转在单元格中】的功能,图片变成了单元格对象。不止如此,这些图片还可以成为数据验证序列的来源。MM再也不用担心我不会做带照片的花名册了哈哈哈……......
  • python: pymssql stored procedures insert output
    sqlscript:IFEXISTS(SELECT*FROMsysobjectsWHERE[name]='proc_Insert_BookKindOut')DROPPROCEDUREproc_Insert_BookKindOutGOCREATEPROCEDUREproc_Insert_InsuranceMoneyOut(@InsuranceNameNVarChar(1000),@InsuranceCostfloat,......