热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

sqlleftjoin如果为空不关联_一日一技:一句SQL,我有6种写法

导读最近在刷LeetCode中数据库题目时,有一道排名题目,用了6种写法分别代表6种SQL思维来实现,想想也算是有趣。文章来源࿱

导读

最近在刷LeetCode中数据库题目时,有一道排名题目,用了6种写法分别代表6种SQL思维来实现,想想也算是有趣。

14c39a3f02ec8895ca59d9bc2b119612.pngb66c39908574145f67ca0d527ad091d2.png

文章来源:小数志

作者:luanhz

题目描述:

90516e83bdee54be5cc8236cf0f49ec7.png

题意理解不难,无非就是查找排名为N的记录,但常用SQL的都知道这里存在一个歧义,即排名是否存在相同和是否跳级的问题。经测试,这里的排名是"致密"排名(dense_rank),即同薪同名且不跳级那种。例如对于薪水3000/2000/2000/1000排名之后为1、2、2、3,若取N=3,则返回结果1000。另外,题目形式是一个自定义函数,但本质仍是一个SQL查询。

面对这样的一道题,你能迅速想到几种SQL写法呢?


解法1 limit+offset由于这里题目需求很简单,仅仅是返回全局的第N高薪水,而不存在分组排名或其他需求,所以最简单的办法就是用limit+offset关键字直接获取。SQL语句:

1CREATE FUNCTION getNthHighestSalary(N INT) RETURNS INT
2BEGIN
3  SET N = N - 1;
4  RETURN (
5      SELECT 
6            salary
7      FROM 
8            employee
9      GROUP BY 
10            salary
11      ORDER BY 
12            salary DESC
13      LIMIT 1 OFFSET N
14  );
15END
执行效率:

2934646f6f68ea440ea995f2735daab6.png

由于只进行单表查询+单字段排序,对salary字段建立索引时查询效率会非常高。


解法2 子查询

既然是排名为N,那么就意味着大于等于目标薪水的记录数为N,更准确的说这里是去重后的记录数为N。基于此想法,很快可以写出相应SQL:

SQL语句:

1CREATE FUNCTION getNthHighestSalary(N INT) RETURNS INT
2BEGIN
3  RETURN (
4      SELECT 
5          DISTINCT e.salary
6      FROM 
7          employee e
8      WHERE 
9          (SELECT count(DISTINCT salary) FROM employee WHERE salary>=e.salary) = N
10  );
11END
执行效率:

714703dd53af4883c6ecc4cf512b5521.png

这个子查询效率要低不少,因为每条记录都要执行一条子查询判断聚合次数是否等于N。
解法3 连接查询

个人认为,SQL最强大也最有代表性的操作在于多表关联,这个问题自然也可以用连接查询。MySQL中主要支持join、left join和right join三种连接方式。具体到这一题,可以选用任何一种。例如,如果限定连接条件是薪水大于等于(含等于),则可直接用join实现两表自连接,然后对另一个计数即可;而如果限定连接条件是薪水大于(不含等于),则必须用left join,避免N取特殊值1时出现关联结果为空而查询失败的情况。具体来说:

应用join的SQL语句:

1CREATE FUNCTION getNthHighestSalary(N INT) RETURNS INT
2BEGIN
3  RETURN (
4      SELECT 
5          DISTINCT e1.salary
6      FROM 
7          employee e1 JOIN employee e2 ON e1.salary <&#61; e2.salary
8      GROUP BY 
9          e1.salary
10      HAVING 
11          count(DISTINCT e2.salary) &#61; N
12  );
13END
执行效率&#xff1a;

642d0e7fd7381dadf9f8e80b0b87bf7b.png

应用left join的SQL语句&#xff1a;

1CREATE FUNCTION getNthHighestSalary(N INT) RETURNS INT
2BEGIN
3  RETURN (
4      SELECT 
5          DISTINCT e1.salary
6      FROM 
7          employee e1 LEFT JOIN employee e2 ON e1.salary  8      GROUP BY 
9          e1.salary
10      HAVING 
11          count(DISTINCT e2.salary) &#61; N-1
12  );
13END
另外&#xff0c;right join本质上和left join是一致的&#xff0c;简单交换两表顺序可以很容实现right join写法。执行效率&#xff1a;

aca88c4ed03482322465967b7591dee4.png

可见&#xff0c;无论是用内连接还是外连接&#xff0c;效率都不是太高&#xff0c;与子查询效率相当。
解法4 笛卡尔积

用join连接方式实现的SQL&#xff0c;都能用笛卡尔积实现&#xff0c;且一般来说笛卡尔效率要略低于连接查询&#xff0c;但很多情况下MySQL优化器会将笛卡尔积形式的查询优化成join形式&#xff0c;此时二者执行过程是一致的。可以很容易将解法3中的形式改成笛卡尔积形式的写法。

SQL语句&#xff1a;

1CREATE FUNCTION getNthHighestSalary(N INT) RETURNS INT
2BEGIN
3  RETURN (
4      SELECT 
5          DISTINCT e1.salary
6      FROM 
7          employee e1, employee e2 
8      WHERE 
9          e1.salary <&#61; e2.salary
10      GROUP BY 
11          e1.salary
12      HAVING 
13          count(DISTINCT e2.salary) &#61; N
14  );
15END
执行效率&#xff1a;

b2a1f6015927f54b9ca525450b994894.png

这个查询的效率相比连接查询和子查询又要略低一些。
解法5 自定义变量

前面已经介绍了4种解法&#xff0c;对比来看&#xff1a;解法2-4中都存在两表关联的问题&#xff0c;而解法1因为仅涉及到单表排序&#xff0c;所以效率相比之下更高&#xff1b;另一方面&#xff0c;解法2-4功能更具扩展性&#xff1a;例如可以很容易实现分组查询排名第N高&#xff0c;而这是简单的limit&#43;offset写法所不能实现的。那么&#xff0c;有没有既能拓展到分组查询、同时又具有单表查询的高效呢&#xff1f;答案是肯定的&#xff0c;例如下面的自定义变量写法&#xff0c;通过设定一个自变量&#xff0c;获取每个薪水的排名信息&#xff0c;然后筛选排名为N的薪水即可。

SQL语句&#xff1a;

1CREATE FUNCTION getNthHighestSalary(N INT) RETURNS INT
2BEGIN
3  RETURN (
4      SELECT 
5          DISTINCT salary 
6      FROM 
7          (SELECT 
8                salary, &#64;r:&#61;IF(&#64;p&#61;salary, &#64;r, &#64;r&#43;1) AS rnk,  &#64;p:&#61; salary 
9            FROM  
10                employee, (SELECT &#64;r:&#61;0, &#64;p:&#61;NULL)init 
11            ORDER BY 
12                salary DESC) tmp
13      WHERE rnk &#61; N
14  );
15END
执行效率&#xff1a;

b4a59f2df52015e365fb2766b947bf47.png

因为仅涉及到单表查询&#xff0c;所以效率更高&#xff0c;与直接用limit&#43;offset效率相当。
解法6 窗口函数

实际上&#xff0c;解法5中的自定义变量查询写法在MySQL8.0以后有相应的窗口函数可以实现。窗口函数在MySQL8.0版本首次引进&#xff0c;而其他很多SQL语言则早已内置。具体而言&#xff0c;对于本题获取"致密"排名的薪水&#xff0c;用到的窗口函数就是dense_rank()。

SQL语句&#xff1a;

1CREATE FUNCTION getNthHighestSalary(N INT) RETURNS INT
2BEGIN
3  RETURN (
4        SELECT 
5            DISTINCT salary
6        FROM 
7            (SELECT 
8                salary, dense_rank() over(ORDER BY salary DESC) AS rnk
9             FROM 
10                employee) tmp
11        WHERE rnk &#61; N
12  );
13END
实际执行过程和解法5是一样的&#xff0c;只是调用内置函数写法更加简洁&#xff0c;效率也与解法5相当并略高于后者。因为当前OJ系统应用MySQL5.6版本&#xff0c;所以无法测试效率。


对比总结

以上用6种写法实现同一需求&#xff0c;实际上这应该也代表了绝大多数写SQL查询的一般性思路&#xff1a;

  • 能用单表优先用单表&#xff0c;即便是需要用group by、order by、limit等&#xff0c;效率一般也比多表高

  • 不能用单表时优先用连接&#xff0c;连接是SQL中非常强大的用法&#xff0c;小表驱动大表&#43;建立合适索引&#43;合理运用连接条件&#xff0c;基本上连接可以解决绝大部分问题。但join级数不宜过多&#xff0c;毕竟是一个接近指数级增长的关联效果

  • 能不用子查询、笛卡尔积尽量不用&#xff0c;虽然很多情况下MySQL优化器会将其优化成连接方式的执行过程&#xff0c;但效率仍然难以保证

  • 自定义变量在复杂SQL实现中会很有用&#xff0c;例如LeetCode中困难级别的数据库题目很多都需要借助自定义变量实现

  • 如果MySQL版本允许&#xff0c;窗口函数是一个最优选择&#xff0c;除了经典的获取3种排名信息&#xff0c;还有聚合函数、向前向后取值、百分位等&#xff0c;具体可参考官方指南(本号回复关键字"教程"提供网盘下载)

7364055072b1261262fb0db326d13187.png

MySQL8.0内置窗口函数

如果你觉得文章不错的话&#xff0c;分享、收藏、在看、留言666是对老表的最大支持。

40c2f68d0db393e546e84e9d2e55d2e7.png

老表Pro已经满了

所以大家加老表Max吧

每日留言

说说你最近遇到的一个编程问题&#xff1f;

或者新学的一个小技巧&#xff1f;

(字数不少于15字)

留言赠书

3e13a16794345234cbbd903f90e6ffcc.png

完整Python基础知识要点

Python小知识 | 这些技能你不会?(一)Python小知识 | 这些技能你不会?(二)Python小知识 | 这些技能你不会?(三)Python小知识 | 这些技能你不会?(四)

近期推荐阅读&#xff1a;

【1】整理了我开始分享学习笔记到现在超过250篇优质文章&#xff0c;涵盖数据分析、爬虫、机器学习等方面&#xff0c;别再说不知道该从哪开始&#xff0c;实战哪里找了【2】【终篇】Pandas中文官方文档&#xff1a;基础用法6(含1-5)

好文章&#xff0c;我在看❤️




推荐阅读
  • 本文介绍了如何利用Shell脚本高效地部署MHA(MySQL High Availability)高可用集群。通过详细的脚本编写和配置示例,展示了自动化部署过程中的关键步骤和注意事项。该方法不仅简化了集群的部署流程,还提高了系统的稳定性和可用性。 ... [详细]
  • 触发器的稳态数量分析及其应用价值
    本文对数据库中的SQL触发器进行了稳态数量的详细分析,探讨了其在实际应用中的重要价值。通过研究触发器在不同场景下的表现,揭示了其在数据完整性和业务逻辑自动化方面的关键作用。此外,还介绍了如何在Ubuntu 22.04环境下配置和使用触发器,以及在Tomcat和SQLite等平台上的具体实现方法。 ... [详细]
  • 深入解析 SQL 数据库查询技术
    本文深入探讨了SQL数据库查询技术,重点讲解了单表查询的各种方法。首先,介绍了如何从表中选择特定的列,包括查询指定列、查询所有列以及计算值的查询。此外,还详细解释了如何使用列别名来修改查询结果的列标题,并介绍了更名运算的应用场景和实现方式。通过这些内容,读者可以更好地理解和掌握SQL查询的基本技巧和高级用法。 ... [详细]
  • Python 程序转换为 EXE 文件:详细解析 .py 脚本打包成独立可执行文件的方法与技巧
    在开发了几个简单的爬虫 Python 程序后,我决定将其封装成独立的可执行文件以便于分发和使用。为了实现这一目标,首先需要解决的是如何将 Python 脚本转换为 EXE 文件。在这个过程中,我选择了 Qt 作为 GUI 框架,因为之前对此并不熟悉,希望通过这个项目进一步学习和掌握 Qt 的基本用法。本文将详细介绍从 .py 脚本到 EXE 文件的整个过程,包括所需工具、具体步骤以及常见问题的解决方案。 ... [详细]
  • 本文详细介绍了在 Oracle 数据库中使用 MyBatis 实现增删改查操作的方法。针对查询操作,文章解释了如何通过创建字段映射来处理数据库字段风格与 Java 对象之间的差异,确保查询结果能够正确映射到持久层对象。此外,还探讨了插入、更新和删除操作的具体实现及其最佳实践,帮助开发者高效地管理和操作 Oracle 数据库中的数据。 ... [详细]
  • 在 Axublog 1.1.0 版本的 `c_login.php` 文件中发现了一个严重的 SQL 注入漏洞。该漏洞允许攻击者通过操纵登录请求中的参数,注入恶意 SQL 代码,从而可能获取敏感信息或对数据库进行未授权操作。建议用户尽快更新到最新版本并采取相应的安全措施以防止潜在的风险。 ... [详细]
  • 本指南介绍了 `requests` 库的基本使用方法,详细解释了其七个主要函数。其中,`requests.request()` 是构建请求的基础方法,支持其他高级功能的实现。此外,我们还重点介绍了如何使用 `requests.get()` 方法来获取 HTML 网页内容,这是进行网页数据抓取和解析的重要步骤。通过这些基础方法,读者可以轻松上手并掌握网页数据抓取的核心技巧。 ... [详细]
  • 在关系型数据库中,数据约束是指在向数据表中插入数据时必须遵循的限制条件。在MySQL和MariaDB中,常见的数据约束包括主键约束、唯一键约束、外键约束以及非空约束等。这些约束确保了数据的完整性和一致性,是数据库管理中的重要组成部分。通过合理设置和使用这些约束,可以有效防止数据冗余和错误,提升数据库的可靠性和性能。 ... [详细]
  • 如何有效防御网站中的SQL注入攻击
    本期文章将深入探讨网站如何有效防御SQL注入攻击。我们将从技术层面详细解析防范措施,并结合实际案例进行阐述,旨在帮助读者全面了解并掌握有效的防护策略。希望本文能为您的网络安全提供有益参考。 ... [详细]
  • 在将Excel数据导入MySQL数据库的过程中,如何确保不会生成重复记录?本文介绍了一种方法,通过PHP脚本检查数据库中是否存在相同的“Code”字段值,从而避免重复记录的产生。该方法不仅提高了数据导入的准确性,还增强了系统的健壮性。 ... [详细]
  • 针对MySQL Undo空间满载及Oracle Undo表空间溢出的问题,本文详细探讨了其原因与解决策略。首先,通过启动SQL*Plus并以SYS用户身份登录数据库,查询当前数据库的UNDO表空间名称,确认当前状态。接着,分析导致Undo空间满载的常见原因,如长时间运行的事务、频繁的更新操作等,并提出相应的解决方案,包括调整Undo表空间大小、优化事务管理、定期清理历史数据等。最后,结合实际案例,提供具体的实施步骤和注意事项,帮助DBA有效应对这些问题。 ... [详细]
  • MyISAM和InnoDB是MySQL中最为广泛使用的两种存储引擎,每种引擎都有其独特的优势和适用场景。MyISAM引擎以其简单的结构和高效的读取速度著称,适用于以读操作为主、对事务支持要求不高的应用。而InnoDB引擎则以其强大的事务处理能力和行级锁定机制,在需要高并发写操作和数据完整性的场景下表现出色。选择合适的存储引擎应综合考虑业务需求、性能要求和数据一致性等因素。 ... [详细]
  • 本文探讨了如何在C#应用程序中通过选择ComboBox项从MySQL数据库中检索数据值。具体介绍了在事件处理方法 `comboBox2_SelectedIndexChanged` 中可能出现的常见错误,并提供了详细的解决方案和优化建议,以确保数据能够正确且高效地从数据库中读取并显示在界面上。此外,还讨论了连接字符串的配置、SQL查询语句的编写以及异常处理的最佳实践,帮助开发者避免常见的陷阱并提高代码的健壮性。 ... [详细]
  • 本文介绍了如何利用Struts1框架构建一个简易的四则运算计算器。通过采用DispatchAction来处理不同类型的计算请求,并使用动态Form来优化开发流程,确保代码的简洁性和可维护性。同时,系统提供了用户友好的错误提示,以增强用户体验。 ... [详细]
  • 如何撰写适应变化的高效代码:策略与实践
    编写高质量且适应变化的代码是每位程序员的追求。优质代码的关键在于其可维护性和可扩展性。本文将从面向对象编程的角度出发,探讨实现这一目标的具体策略与实践方法,帮助开发者提升代码效率和灵活性。 ... [详细]
author-avatar
1021365712_3a478e
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有