当前位置: 首页 > news >正文

Java面试题031:一文深入了解MySQL(3)

Java面试题029:一文深入了解MySQL(1)

Java面试题030:一文深入了解MySQL(2)

1、MySQL多表查询

(1)内连接 inner join

        返回两个表中完全匹配的行,即只保留两个表连接字段值相等的行。

(2)外连接

        左外连接

        LEFT JOIN 或 LEFT OUTER JOIN 左外连接返回左表中的所有行,以及右表中满足连接条件的行(如果左表中的某行在右表中没有匹配的行,那么结果集中该行的右表列将包含 NULL 值)。

        右外连接

        右外连接(RIGHT JOIN 或 RIGHT OUTER JOIN):右外连接返回右表中的所有行,以及左表中满足连接条件的行(如果右表中的某行在左表中没有匹配的行,那么结果集中该行的左表列将包含 NULL 值)。

(3)UNION UNION ALL

-- UNION 语法
SELECT column1, column2 FROM table1
UNION
SELECT column1, column2 FROM table2;
-- 这里使用了 column1, column2将字段一一列出来,如果 table1和table2字段的顺序一致,可以直接写为 select * ,下方 UNION ALL 同理
select * from table1
UNION
SELECT * FROM table2;-- UNION ALL 语法
SELECT column1, column2 FROM table1
UNION ALL
SELECT column1, column2 FROM table2;
或
select * from table1
UNION ALL
SELECT * FROM table2;

(1)列数一致:所有 SELECT 语句的列数必须相同;
(2)数据类型兼容:对应列的数据类型需兼容(如 VARCHAR 与 TEXT 兼容);
(3)列名规则:最终结果集的列名以第一个 SELECT 的列名为准;

  • 字段相同,顺序相同

user1:

user2:

使用UNION查询 会自动去重,合并后的结果就是7条数据:

UNION ALL 不会自动去重,而是将两张表的全部数据合并,一共十条数据:

  • 字段相同,顺序不同

user1:

user4:

        此时不能直接使用 select * ,否则不管是 UNION,还是UNION ALL,最终的表字段顺序会以第一张表 user1 的字段顺序为准,就会产生一个问题, user4 表中,passwor 和 username 顺序是反着的,会导致查询结果中,user4 表用户的密码 password 被当作 username,用户的 username 被当作 password;造成数据错误。

此时就需要将字段一一对应。

  • 字段数量不同

user3:与user1对比,user3 少了一个 age 年龄字段

        此时查询的时候就需要"补列字段" ,可以使用 "NULL AS age"对查询结果中 user3 的age字段进行填充,使其全部为 NULL(也可以使用具体数据)。

2、笛卡尔积

        假设有两个集合A和B。A的元素是{a1, a2, …},B的元素是{b1, b2, …}。那么,A和B的笛卡尔积就是从A中取一个元素,和从B中取一个元素,形成一个有序对,这样的所有有序对构成的集合就是笛卡尔积。数学上表示为:A × B = {(a1, b1), (a1, b2), …, (a2, b1), (a2, b2), …}。

        在数据库中,当你进行表连接操作时,如果没有指定任何连接条件(如使用WHERE子句),就会产生两个表的笛卡尔积。这意味着第一个表中的每一行都会与第二个表中的每一行配对,产生巨大数量的数据行。

(1)产生条件

        两表关联查询语句中没有指定连接条件。

表Employees中有两条数据:

表Departments中有两条数据: 

 不加查询条件进行查询,会发现出现4条数据,而且两个表的字段全部进行了展示

(2)避免笛卡尔积

        为了避免笛卡尔积,我们应该使用适当的连接条件。

使用显式的连接类型

  • INNER JOIN: 使用INNER JOIN并指定连接条件,可以确保只连接相关的行。
  • LEFT/RIGHT OUTER JOIN: 这些连接类型允许连接两个表,并包括左表/右表中的所有行,即使它们在右表/左表中没有匹配项。
  • FULL OUTER JOIN: 它结合了LEFT和RIGHT JOIN的特点,如果左表或右表中的行没有匹配项,它也会被包含在结果中。

使用WHERE子句
        添加过滤条件: 在WHERE子句中明确指定连接条件可以防止产生笛卡尔积,因为它会限制只返回满足特定条件的行。

使用子查询
        子查询作为连接条件: 在连接的ON子句或WHERE子句中使用子查询,可以精确控制要返回的行。

使用聚合函数和GROUP BY
        分组和聚合: 当需要根据某个字段进行分组时,使用GROUP BY子句可以避免笛卡尔积,尤其是在进行统计计算时。

使用DISTINCT关键字
        消除重复行: 如果查询产生了重复行(这在某些类型的笛卡尔积中可能发生),使用DISTINCT关键字可以移除重复的结果集。

使用LIMIT子句
        限制返回行数: 在进行初步测试和调试时,使用LIMIT子句可以限制查询结果的行数,从而避免大量的输出,尤其是在处理可能产生笛卡尔积的复杂查询时。

3、SQL 查询语句的执行顺序

(8)Select
(9)distinct 字段名1,字段名2,
(6)[fun(字段名)]  
(1)from 表1
(3)<join类型>join 表2 
(2)on <join条件> 
(4)where <where条件> 
(5)group by <字段> 
(7)having <having条件> 
(10)order by <排序字段> 
(11)limit <起始偏移量,行数>

1. FROM:对 FROM 子句中的表1和表2执行笛卡儿积,产生虚拟表VT1

2. ON:对虚拟表 VT1 应用 ON 筛选,只有那些符合join条件的行才被插入虚拟表 VT2

3. JOIN:如果指定了 OUTER JOIN(如 LEFT JOINRIGHT JOIN),那么保留表中未匹配的行

作为外部行添加到虚拟表 VT2 中,产生虚拟表 VT3。如果 FROM 子句包含两个以上表,则对上一个连接生成的结果表 VT3 和下一个表重复执行步骤 1~步骤 3,直到处理完所有的表为止

4. WHERE:对虚拟表 VT3 应用 WHERE 过滤条件,只有符合条件的记录才被插入虚拟表 VT4

5. GROUP BY:根据 GROUP BY 子句中的列,对 VT4 中的记录进行分组操作,产生 VT5

6. 聚合函数:对表 VT5 进行 CUBE ROLLUP 操作,产生表 VT6

7. HAVING:对虚拟表 VT6 应用 HAVING 过滤器,只有符合条件的记录才被插入虚拟表 VT7中。

8. SELECT:第二次执行 SELECT 操作,选择指定的列,插入到虚拟表 VT8

9. DISTINCT:去除重复数据,产生虚拟表 VT9

10. ORDER BY:将虚拟表 VT9 中的记录按照排序字段进行排序操作,产生虚拟表 VT10。

11. LIMIT:取出指定行的记录,产生虚拟表 VT11,并返回给查询⽤用户

举例:

用户表user :

订单表orders :

目标:查询来自北京,并且订单数少于2的客户。

SELECT a.user_id,COUNT(b.order_id) as total_orders
FROM  user as a
LEFT JOIN orders as b
ON a.user_id = b.user_id
WHERE a.city = 'beijing'
GROUP BY a.user_id
HAVING COUNT(b.order_id) < 2
ORDER BY total_orders desc

(1)FROM语句对两个表执行笛卡尔积,会得到一个虚拟表,VT1(vitual table 1)

(2)执行ON过滤

根据ON中指定的条件,去掉那些不符合条件的数据,得到VT2如下:

select * from user as a inner JOIN orders as b ON a.user_id = b.user_id;

(3)执行left join子句:user表作为保留表,未匹配的记录添加到VT2中形成VT3

(4)执行where条件过滤:对添加了外部行的数据进行where条件过滤,执行WHERE a.city = 'beijing'  得到VT4如下:

(5)执行group by分组语句:执行GROUP BY a.user_id 得到VT5如下:

(6)执行having:HAVING子句主要和GROUP BY子句配合使用,对分组得到VT5的数据进行条件过滤,执行 HAVING COUNT(b.order_id) < 2,得到VT6如下:

(7)select列表:执行测试语句中的SELECT a.user_id,user_name,COUNT(b.order_id) as total_orders,从VT6中选择出我们需要的内容,得到VT7如下:

(8)执行distinct去重复数据:如果在查询中指定了DISTINCT子句,则会创建一张内存临时表(如果内存放不下,就需要存放在硬盘了)。这张临时表的表结构和上一步产生的虚拟表是一样的,不同的是对进行DISTINCT操作的列增加了一个唯一索引,以此来除重复数据。

(9)执行order by字句:对虚拟表VT7中的内容按照指定的列进行排序,然后返回一个新的虚拟表

(10)执行limit字句:

LIMIT子句从上一步得到的虚拟表中选出从指定位置开始的指定行数据,常用来做分页;

MySQL数据库的LIMIT支持如下形式的选择:limit n,m

表示从第n条记录开始选择m条记录。对于小数据,使用LIMIT子句没有任何问题,当数据量非常大的时候,使用LIMIT n, m是非常低效的。因为LIMIT的机制是每次都是从头开始扫描,如果需要从第60万行开始,读取3条数据,就需要先扫描定位到60万行,然后再进行读取,而扫描的过程是一个非常低效的过程。

4、count(1)count(*) count(列名) 的区别

(1)count(1) and count(*)


        当表的数据量大些时,对表作分析之后,使用count(1)还要比使用count()用时多了!
        从执行计划来看,count(1)和count()的效果是一样的。但是在表做过分析之后,count(1)会比count(*)的用时少些(1w以内数据量),不过差不了多少。

        如果count(1)是聚索引,id,那肯定是count(1)快,但是差的很小的。
        因为count(),自动会优化指定到那一个字段。所以没必要去count(1),用count(),sql会帮你完成优化的,因此:count(1)和count(*)基本没有差别!

(2)count(1) and count(字段)


        count(1) 会统计表中的所有的记录数,包含字段为null 的记录。
        count(字段) 会统计该字段在表中出现的次数,忽略字段为null 的情况。即不统计字段为null 的记录。

(3)执行效率:

  • 列名为主键,count(列名)会比count(1)快。

  • 列名不为主键,count(1)会比count(列名)快。

  • 如果有主键,则 select count(主键)的执行效率是最优的。

5、 in exists 的区别

(1)exists

        exists对外表用loop逐条查询,每次查询都会查看exists的条件语句,当exists里的条件语句能够返回记录行时(无论记录行是的多少,只要能返回),条件就为真,返回当前loop到的这条记录;反之,如果exists里的条件语句不能返回记录行,则当前loop到的这条记录被丢弃,exists的条件就像一个bool条件,当能返回结果集则为true,不能返回结果集则为false。

select * from user where exists (select 1);

        对user表的记录逐条取出,由于子条件中的select 1永远能返回记录行,那么user表的所有记录都将被加入结果集,所以与select * from user;是一样的。

select * from user where exists (select * from user where user_id = 0);

        user表进行loop时,检查条件语句(select * from user where user_id = 0),由于user_id永远不为0,所以条件语句永远返回空集,条件永远为false,那么user表的所有记录都将被丢弃。

        如果A表有n条记录,那么exists查询就是将这n条记录逐条取出,然后判断n遍exists条件。

(2)in

        in查询就是先将子查询条件的记录全都查出来,假设结果集为B,共有m条记录,然后再将子查询条件的结果集分解成m个,再进行m次查询。

        in查询相当于多个or条件的叠加。

select * from user where user_id in (1, 2, 3);等效于select * from user where user_id = 1 or user_id = 2 or user_id = 3;

(3)性能对比

  • IN查询在内部表和外部表上都可以使用到索引。

  • Exists查询仅在内部表上可以使用到索引。

  • 当子查询结果集很大,而外部表较小的时候,Exists的Block Nested Loop(Block 嵌套循环)的作用开始显现,并弥补外部表无法用到索引的缺陷,查询效率会优于IN。

  • 当子查询结果集较小,而外部表很大的时候,Exists的Block嵌套循环优化效果不明显,IN 的外表索引优势占主要作用,此时IN的查询效率会优于Exists。

  • 表的规模不是看内部表和外部表,而是外部表和子查询结果集。

欢迎大家关注我的专栏,该专栏会持续更新,从原理角度覆盖Java知识体系的方方面面。

一文吃透JAVA知识体系(面试题)https://blog.csdn.net/wuxinyan123/category_7521898.html?fromshare=blogcolumn&sharetype=blogcolumn&sharerId=7521898&sharerefer=PC&sharesource=wuxinyan123&sharefrom=from_link

http://www.lqws.cn/news/566965.html

相关文章:

  • Vivado关联Vscode
  • Rust标量、复合类型与自定义类型、第三方并发结构
  • 【软考--软件设计师】2025-05 我的选择题错题总结
  • ListExtension 扩展方法增加 转DataTable()方法
  • 商业行业项目创业计划书PPT模版
  • 什么是区块链的跨链操作?
  • 穿越时空的光
  • 详解快速排序
  • SRS流媒体服务器(8)源码分析之rtc/rtmp互相转码详解
  • 数据可视化 - 单子图
  • 第10章 数组和指针
  • 左神算法之螺旋打印
  • SQL Server从入门到项目实践(超值版)读书笔记 19
  • 从GPTs到Real智能体:目前常见的几种创建智能体方式
  • spring:BeanPostProcessor后置处理器介绍
  • 小米路由器 AX3000T自定义子网掩码
  • Mybatis多条件查询设置参数的三种方法
  • stm32hal模块驱动(1)hpdl1414驱动
  • Vue的watch函数实现
  • 华为云 Flexus+DeepSeek 征文|华为云 Flexus 云服务 Dify-LLM 平台深度部署指南:从基础搭建到高可用实践
  • 智能制造——解读西门子数字化工厂规划报告(三年实施计划)【附全文阅读】
  • 机器学习在智能供应链中的应用:需求预测与库存优化
  • 大事件项目记录12-文章管理接口开发-总
  • 设计模式之适配器模式
  • OpenCV读取照片和可视化详解和代码示例
  • MySQL 安装使用教程
  • Java垃圾收集机制Test
  • PL-SLAM: Real-Time Monocular Visual SLAM with Points and Lines
  • Ai工具分享(2):Vscode+Cline无限免费的使用教程
  • XWPFDocument导出word文件