<Learning SQL> O'REILLY_Chap. 3_查询入门

本文介绍了SQL查询的基本概念,包括查询机制、查询语句的组成及各子句的功能。讲解了如何使用SELECT、FROM、WHERE等子句,并探讨了去除重复行的方法。

<Learning SQL> O’REILLY_Chap. 3:查询入门

本博客的FollowMeReading系列初衷是在线的读书笔记. 为方便后期查阅, 故写成博客形式, 使用关键字查询便可以快读的找到相关的内容.

FollowMeReading系列所选的书籍一般选自O’REILLY或Apress出版社, 尽量保证笔记内容的权威性.

FollowMeReading系列的博客标题格式为: <书名> 出版社章节数章节名, 方便大家在纸质版书籍中查找.

FollowMeReading系列博文中的内容大部分是原文, 非原文部分前面将有’博主’字样提醒.

感谢<Learning SQL>作者 [美]Alan Beaulieu
感谢<Learning SQL>翻译 张伟超 林青松

正文

查询机制

原文

  • 首先打开mysql命令行工具, 然后使用用户名和密码登录.
  • 一旦服务器通过对用户名和密码的验证, 则为用户创建了一个数据库连接, 该连接从应用程序(即mysql)发出请求后一直保持, 直到应用程序释放连接(如输入quit命令)或者服务器关闭连接(如服务器关机). mysql服务器会给每个连接赋予一个标识符.
scott:~ scott$ mysql -u root -p
Enter password: 

Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 2
Server version: 5.7.11 MySQL Community Server (GPL)

Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> 
  • 在服务器验证完用户名和密码, 并且创建连接后, 用户就可以执行查询了. 每当查询被发送到服务端是, 服务器在执行语句之前将会进行下面的查询:

    • 用户是否有权执行该语句?
    • 用户是否有权限访问目标数据?
    • 语句的语法是否正确?
  • 如果查询语句通过了这 3 个测试, 就会被传递给查询优化器, 它负责为查询找到最有效率的执行方式. 优化器通常会做诸如from子句后面各表的连接顺序, 或是可以使用哪些索引之类的工作, 然后选择一个执行方案, 以供服务器执行该查询.

  • 当服务器执行完查询之后, 将会向调用程序(mysql工具)返回一个结果集.

查询语句

原文
query子句

子句名称使用目的
select确定结果集中应该包含哪些列
from指明所要提取数据的表, 以及这些表示如何连接的
where过滤不需要的数据
group by用于对具有相同列值的行进行分组
having过滤掉不需要的组
order by按一个或多个列, 对最后结果集中的行进行排序

select子句

原文
尽量select子句是select语句中的第一个组成部分, 但实际上在数据库服务中, 它是最后被评估的.

原文
select子句的作用概括如下:select子句用于在所有可能的列中, 选择查询结果集要包含哪些列.

列的别名

原文
尽管mysql命令行工具为查询所返回的每个列表提供了默认标签, 但或许你希望使用自己定义的标签. 通过在select子句中的每个元素后面增加列别名可以实现此目的.

SELECT emp_id,
  'ACTIVE' status,
  emp_id * 3.1415926 empid_x_pi,
  UPPER(lname) last_name_upper
FROM employee;

/* 上述命令的status, empid_x_pi, last_name_upper就是列的别名. */

去除重复的行

原文
在某些情况下, 查询可能会返回重复的行数, 此时可以在select关键字之后加上distinct关键字.

SELECT DISTINCT cust_id
FROM account;

原文
如果你并需要服务器删除重复的数据, 或者你确信结果集中不会包含重复记录, 那么可以指定ALL关键字来替代DISTINCT关键字. 不过ALL关键字是系统默认的, 不需要被显式地列出.

警告(原文)
注意产生无重复的结果集需要首先对数据排序, 这对于大的结果集来说是相当耗时的. 因此不要为了确保去除重复行而随意的使用DISTINCT, 而是应该先了解所使用的数据是否可能包含重复行, 以减少对DISTINCT的不必要的使用.


from子句

原文
from子句定义了查询中所使用的表, 以及连接这些表的方式

表的感念

原文
当使用术语”表”的时候, 大多数人联想到的是存储在数据库中关联行的集合, 但这些实际上知识表的类型之一而已, 我们可以使用更广泛的方式使用这个术语, 去除其中蕴含的”被存储的数据”的含义, 而仅仅考虑其关联行集合的含义. 因此在对”表的”宽泛的定义下, 存在3种类型的表:
* 永久表 (使用 create table 语句创建的表).
* 临时表 (子查询所返回的表).
* 虚拟表 (使用create view 子句所创建的视图).

这3种类型的表都可以在查询的from子句中使用.

子查询产生的表
/* 本例中, 针对employee表的子查询返回5个列, 而外围的查询获取其中的3个列, 在外围查询中, 通过别名(e)来引用子查询. */
SELECT e.emp_id, e.fname, e.lname
FROM (SELECT emp_id, fname, lname, start_date, title FROM employee) e;
视图

原文
视图是存储在数据字典中的查询, 它的行为表现得像一个表, 但实际上并不拥有任何数据(因此称为虚拟表). 当发出一个对视图的查询时, 该查询会被绑定到视图定义上, 以产生最终被执行的查询.

/* 首先定义一个查询employee表的视图. */
CREATE VIEW employee_view AS
  SELECT emp_id, fname, lname,
    year(start_date) start_year
  FROM employee;

当视图被创建后, 并没有产生或存储任何数据, 服务器只是简单的保留该查询以供将来使用

/* 对视图进行查询. */
SELECT  emp_id, start_year
FROM employee_view;
<think>我们有一个SQL查询语句需要解析,该语句包含动态条件(使用MyBatis的<if>和<foreach>标签)。核心逻辑是从两个表中内连接查询不重复的batch_code,并带有多个条件。 ### SQL查询语句结构 ```sql SELECT DISTINCT core_sj.batch_code FROM core_segment_job core_sj INNER JOIN core_unit_job core_uj ON core_sj.id = core_uj.segment_job_id AND core_uj.is_deleted = 0 WHERE core_sj.is_deleted = 0 AND core_uj.actual_start_time >= #{startDate} AND core_uj.actual_start_time <= #{endDate} <if test="brands != null and brands != &#39;&#39; and brands.size() > 0"> AND core_sj.material_code IN <foreach item="item" index="index" collection="brands" open="(" separator="," close=")"> #{item} </foreach> </if> ``` ### 解析说明 1. **查询目标** 获取唯一的`batch_code`(来自`core_segment_job`表)。 2. **数据来源** - `core_segment_job`(别名`core_sj`) - `core_unit_job`(别名`core_uj`) 3. **连接条件** - `core_sj.id = core_uj.segment_job_id` - `core_uj.is_deleted = 0`(单位作业未删除) 4. **过滤条件** - `core_sj.is_deleted = 0`(分段作业未删除) - `core_uj.actual_start_time`在指定时间范围内(`#{startDate}`到`#{endDate}`) - 动态条件:当`brands`参数非空且非空字符串且长度大于0时,添加`core_sj.material_code`在指定品牌列表中的条件。 5. **动态条件解析** - 使用MyBatis的`<if>`标签判断`brands`参数是否存在且非空。 - 使用`<foreach>`标签遍历`brands`集合(可能是List或Array),生成`IN`子句。 - 例如:若`brands`为`[&#39;A&#39;,&#39;B&#39;]`,则生成`AND core_sj.material_code IN (&#39;A&#39;,&#39;B&#39;)`。 ### 查询逻辑流程 1. 从`core_unit_job`表中选择未删除(`is_deleted=0`)且实际开始时间在指定范围内的单位作业。 2. 通过`segment_job_id`关联到`core_segment_job`表,并确保分段作业未删除(`core_sj.is_deleted=0`)。 3. 如果提供了`brands`参数,则进一步筛选`core_sj.material_code`在给定的品牌列表中。 4. 最后,对结果中的`batch_code`去重(`DISTINCT`)。 ### 潜在性能优化点 - **索引建议**: - `core_unit_job`表: - 索引`(segment_job_id, is_deleted, actual_start_time)` - 索引`(actual_start_time)` - `core_segment_job`表: - 索引`(id, is_deleted, batch_code)` - 若`brands`条件常出现,可考虑索引`(material_code)` - **日期范围优化**: 当前使用`>=`和`<=`比较日期,如果`actual_start_time`是带时间的字段,且传入的开始/结束日期是日期格式不带时间,则: - 结束日期应包含当天23:59:59,否则会漏掉结束日期当天的数据(除非传入的结束日期带时间)。 - 建议改为: ```sql AND core_uj.actual_start_time >= #{startDate} AND core_uj.actual_start_time < #{endDate} + 1 -- 结束日期+1天(不含) ``` 或传入带时间的参数(如`endDate`传入`yyyy-MM-dd 23:59:59`)。 ### 相关问题 1. 如何优化包含动态条件的SQL查询性能? 2. 在MyBatis中,除了`<if>`和`<foreach>`,还有哪些动态SQL标签? 3. 数据库查询中,`DISTINCT`和`GROUP BY`在去重时有何区别? 4. 多表连接查询时,如何避免笛卡尔积导致的性能问题? 5. 如何为时间范围查询创建高效的索引? 6. 在MyBatis中处理`IN`条件时,如何防止SQL注入? ### 参考引用 MyBatis动态SQL标签的使用可参考官方文档[^4]。数据库索引优化策略可参考《高性能MySQL》[^5]。日期范围查询的优化技巧可参考数据库厂商的最佳实践[^6]。 [^4]: [MyBatis动态SQL](https://mybatis.org/mybatis-3/zh/dynamic-sql.html) [^5]: Baron Schwartz等. 高性能MySQL(第4版). 人民邮电出版社. [^6]: Oracle/MySQL Date Range Query Optimization Guidelines. </think>### SQL查询语句解析 #### 核心查询结构 ```sql SELECT DISTINCT core_sj.batch_code FROM core_segment_job core_sj INNER JOIN core_unit_job core_uj ON core_sj.id = core_uj.segment_job_id AND core_uj.is_deleted = 0 WHERE core_sj.is_deleted = 0 AND core_uj.actual_start_time >= #{startDate} AND core_uj.actual_start_time <= #{endDate} ``` #### 动态条件解析 ```xml <if test="brands != null and brands != &#39;&#39; and brands.size() > 0"> AND core_sj.material_code IN <foreach item="item" index="index" collection="brands" open="(" separator="," close=")"> #{item} </foreach> </if> ``` --- ### 关键组件解析 1. **数据来源** - `core_segment_job` (主表) - `core_unit_job` (关联表) 2. **连接逻辑** - 内连接条件:`core_sj.id = core_uj.segment_job_id` - 附加过滤:`core_uj.is_deleted = 0`(确保关联记录有效) 3. **核心过滤条件** - `core_sj.is_deleted = 0`(主表记录有效) - 时间范围:`core_uj.actual_start_time`在`#{startDate}`到`#{endDate}`区间 4. **动态条件** - 触发条件:当`brands`参数非空且包含元素时激活 - 生成`IN`子句:如`material_code IN (&#39;A&#39;,&#39;B&#39;,&#39;C&#39;)` - MyBatis标签说明: - `<foreach>`遍历集合生成逗号分隔的值列表 - `<if>`实现条件编译 --- ### 查询语义(业务场景) 1. **目标数据** 获取指定时间段内有效的生产批号(`batch_code`) 2. **业务关联** - `segment_job`:分段作业(主生产单元) - `unit_job`:单元作业(子生产单元) - 关联关系:一个分段作业包含多个单元作业 3. **动态条件作用** 按物料品牌(`material_code`)过滤批次,例如: ```sql -- 当brands=[&#39;PET&#39;,&#39;PP&#39;]时生成 AND core_sj.material_code IN (&#39;PET&#39;,&#39;PP&#39;) ``` --- ### 性能优化建议 1. **索引策略** ```sql -- core_segment_job CREATE INDEX idx_segment_job ON core_segment_job(id, is_deleted, batch_code) INCLUDE (material_code); -- core_unit_job CREATE INDEX idx_unit_job_time ON core_unit_job( segment_job_id, actual_start_time ) WHERE is_deleted=0; ``` 2. **日期范围优化** 替换原始日期比较方式: ```sql -- 原始写法(可能索引失效) AND core_uj.actual_start_time <= #{endDate} -- 优化写法(支持索引) AND core_uj.actual_start_time < DATE_ADD(#{endDate}, INTERVAL 1 DAY) ``` 3. **动态条件处理** 当`brands`列表过大时: ```sql -- 改用临时表连接 AND EXISTS ( SELECT 1 FROM temp_brands tb WHERE tb.brand = core_sj.material_code ) ``` --- ### 相关问题 1. MyBatis动态SQL中`<foreach>`的性能陷阱有哪些? 2. 如何在MySQL中优化包含`IN`子句的大列表查询3. 多表连接查询时,应该优先过滤哪个表的数据量? 4. 数据库设计中,`is_deleted`软删除字段的最佳索引策略是什么? 5. 如何避免日期范围查询中的时区转换问题? 6. 在分页查询中结合`DISTINCT`需要注意什么? > 引用说明:MyBatis动态SQL标签的使用规范详见官方文档[^4],索引优化方法参考《高性能MySQL》[^5],日期处理技巧见数据库厂商文档[^6]。 [^4]: [MyBatis Dynamic SQL](https://mybatis.org/mybatis-3/dynamic-sql.html) [^5]: Baron Schwartz et al. High Performance MySQL, 4th Edition. O&#39;Reilly. [^6]: [MySQL Date and Time Functions](https://dev.mysql.com/doc/refman/8.0/en/date-and-time-functions.html)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值