终极指南——如何编写更好的SQL查询?
反向模型中隐含的事实是,建立查询时基于集合和程序的方法之间存在着不同。
- 查询的程序方法是一种非常类似于编程的方法:你告诉系统需要做些什么以及如何做。例如上一篇文章中的示例,通过执行一个函数然后调用另一个函数来查询数据库,或者使用包含循环、条件和用户定义函数(UDF)的逻辑方式来获得最终查询结果。你会发现通过这种方式,一直在请求一层一层中数据的子集。这种方法也经常被称为逐步或逐行查询。
- 另一种是基于集合的方法,只需指定需要执行的操作。使用这种方法要做的事情就是,指定你想通过查询获得的结果的条件和要求。在检索数据过程中,你不需要关注实现查询的内部机制:数据库引擎会决定最佳的执行查询的算法和逻辑。
由于 SQL 是基于集合的,所以这种方法比起程序方法更加有效,这也解释了为什么在某些情况下,SQL 可以比代码工作地更快。
基于集合的查询方法也是数据挖掘分析行业要求你必须掌握的技能!因为你需要熟练的在这两种方法之间进行切换。如果你发现自己的查询中存在程序查询,则应该考虑是否需要重写这部分。
反向模式不是静止不变的。在你成为 SQL 开发者的过程中,避免查询反向模型和重写查询可能会是一个很艰难的任务。所以时常需要使用工具以一种更加结构化的方法来优化你的查询。
对性能的思考不仅需要更结构化的方法,还需要更深入的方法。
然而,这种结构化和深入的方法主要是基于查询计划的。查询计划首先被解析为“解析树”并且准确定义了每个操作使用什么算法以及如何协调操作过程。
查询优化在优化查询时,很可能需要手动检查优化器生成的计划。在这种情况下,将需要通过查看查询计划来再次分析你的查询。
要掌握这样的查询计划,你需要使用一些数据库管理系统提供给你的工具。你可以使用以下的一些工具:
- 一些软件包功能工具可以生成查询计划的图形表示。
- 其它工具能够为你提供查询计划的文本描述。
请注意,如果你正在使用 PostgreSQL,则可以区分不同的 EXPLAIN,你只需获取描述,说明 planner 如何在不运行计划的情况下执行查询。同时 EXPLAIN ANALYZE 会执行查询,并返回给你一个评估查询计划与实际查询计划的分析报告。一般来说,实际执行计划会切实的执行这个计划,而评估执行计划可以在不执行查询的情况下,解决这个问题。在逻辑上,实际执行计划更为有用,因为它包含了执行查询时,实际发生的其它细节和统计信息。
接下来你将了解 XPLAIN 和 ANALYZE 的更多信息,以及如何使用这两个命令来进一步了解你的查询计划和查询性能。要做到这一点,你需要开始使用两个表: one_million 和 half_million 来做一些示例。
你可以借助 EXPLAIN 来检索 one_million 表的当前信息:确保已将其放在运行查询的首要位置,在运行完成之后,会返回到查询计划中:
EXPLAIN SELECT * FROM one_million; QUERY PLAN <span class="hljs-strong">_____</span><span class="hljs-strong">_____</span><span class="hljs-strong">_____</span><span class="hljs-strong">_____</span><span class="hljs-strong">_____</span><span class="hljs-strong">_____</span><span class="hljs-strong">_____</span><span class="hljs-strong">_____</span><span class="hljs-strong">_____</span><span class="hljs-emphasis">___</span>_ Seq Scan on one_million (cost=0.00..18584.82 rows=1025082 width=36) (1 row)
在以上示例中,我们看到查询的 Cost 是0.00..18584.82 ,行数是1025082,列宽是36。
同时,也可以借助 ANALYZE 来更新统计信息 。
ANALYZE one_million; EXPLAIN SELECT * FROM one_million; QUERY PLAN <span class="hljs-strong">_____</span><span class="hljs-strong">_____</span><span class="hljs-strong">_____</span><span class="hljs-strong">_____</span><span class="hljs-strong">_____</span><span class="hljs-strong">_____</span><span class="hljs-strong">_____</span><span class="hljs-strong">_____</span><span class="hljs-strong">_____</span><span class="hljs-emphasis">___</span>_ Seq Scan on one_million (cost=0.00..18334.00 rows=1000000 width=37) (1 row)
除了 EXPLAIN 和 ANALYZE,你也可以借助 EXPLAIN ANALYZE 来检索实际执行时间:
EXPLAIN ANALYZE SELECT * FROM one_million; QUERY PLAN <span class="hljs-strong">_____</span><span class="hljs-strong">_____</span><span class="hljs-strong">_____</span><span class="hljs-strong">_____</span><span class="hljs-strong">_____</span><span class="hljs-strong">_____</span><span class="hljs-strong">_____</span><span class="hljs-strong">_____</span><span class="hljs-strong">_____</span><span class="hljs-strong">_____</span>_ Seq Scan on one_million (cost=0.00..18334.00 rows=1000000 width=37) (actual time=0.015..1207.019 rows=1000000 loops=1) Total runtime: 2320.146 ms (2 rows)
使用 EXPLAIN ANALYZE 的缺点就是需要实际执行查询,这点值得注意!
到目前为止,我们看到的所有算法是顺序扫描或全表扫描:这是一种在数据库上进行扫描的方法,扫描的表的每一行都是以顺序(串行)的顺序进行读取,每一列都会检查是否符合条件。在性能方面,顺序扫描不是最佳的执行计划,因为需要扫描整个表。但是如果使用慢磁盘,顺序读取也会很快。
还有一些其它算法的示例:
EXPLAIN ANALYZE SELECT * FROM one<span class="hljs-emphasis">_million JOIN half_</span>million ON (one<span class="hljs-emphasis">_million.counter=half_</span>million.counter); QUERY PLAN <span class="hljs-strong">_____</span><span class="hljs-strong">_____</span><span class="hljs-strong">_____</span><span class="hljs-strong">_____</span><span class="hljs-strong">_____</span><span class="hljs-strong">_____</span><span class="hljs-strong">_____</span><span class="hljs-strong">_____</span><span class="hljs-strong">_____</span><span class="hljs-strong">_____</span><span class="hljs-strong">_____</span><span class="hljs-strong">_____</span>_ Hash Join (cost=15417.00..68831.00 rows=500000 width=42) (actual time=1241.471..5912.553 rows=500000 loops=1) Hash Cond: (one<span class="hljs-emphasis">_million.counter = half_</span>million.counter) <span class="hljs-code"> -> Seq Scan on one_million</span> <span class="hljs-code"> (cost=0.00..18334.00 rows=1000000 width=37)</span> <span class="hljs-code"> (actual time=0.007..1254.027 rows=1000000 loops=1)</span> <span class="hljs-code"> -> Hash (cost=7213.00..7213.00 rows=500000 width=5)</span> <span class="hljs-code"> (actual time=1241.251..1241.251 rows=500000 loops=1)</span> <span class="hljs-code"> Buckets: 4096 Batches: 16 Memory Usage: 770kB</span> <span class="hljs-code"> -> Seq Scan on half_million</span> <span class="hljs-code"> (cost=0.00..7213.00 rows=500000 width=5)</span> (actual time=0.008..601.128 rows=500000 loops=1) Total runtime: 6468.337 ms
我们可以看到查询优化器选择了 Hash Join。请记住这个操作,因为我们需要使用这个来评估查询的时间复杂度。我们注意到了上面示例中没有 half_million.counter 索引,我们可以在下面示例中添加索引 :
<span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">INDEX</span> <span class="hljs-keyword">ON</span> half_million(counter); <span class="hljs-keyword">EXPLAIN</span> <span class="hljs-keyword">ANALYZE</span> <span class="hljs-keyword">SELECT</span> * <span class="hljs-keyword">FROM</span> one_million <span class="hljs-keyword">JOIN</span> half_million <span class="hljs-keyword">ON</span> (one_million.counter=half_million.counter); QUERY PLAN ______________________________________________________________ <span class="hljs-keyword">Merge</span> <span class="hljs-keyword">Join</span> (<span class="hljs-keyword">cost</span>=<span class="hljs-number">4.12</span>.<span class="hljs-number">.37650</span><span class="hljs-number">.65</span> <span class="hljs-keyword">rows</span>=<span class="hljs-number">500000</span> width=<span class="hljs-number">42</span>) (actual <span class="hljs-keyword">time</span>=<span class="hljs-number">0.033</span>.<span class="hljs-number">.3272</span><span class="hljs-number">.940</span> <span class="hljs-keyword">rows</span>=<span class="hljs-number">500000</span> loops=<span class="hljs-number">1</span>) <span class="hljs-keyword">Merge</span> Cond: (one_million.counter = half_million.counter) -> <span class="hljs-keyword">Index</span> <span class="hljs-keyword">Scan</span> <span class="hljs-keyword">using</span> one_million_counter_idx <span class="hljs-keyword">on</span> one_million (<span class="hljs-keyword">cost</span>=<span class="hljs-number">0.00</span>.<span class="hljs-number">.32129</span><span class="hljs-number">.34</span> <span class="hljs-keyword">rows</span>=<span class="hljs-number">1000000</span> width=<span class="hljs-number">37</span>) (actual <span class="hljs-keyword">time</span>=<span class="hljs-number">0.011</span>.<span class="hljs-number">.694</span><span class="hljs-number">.466</span> <span class="hljs-keyword">rows</span>=<span class="hljs-number">500001</span> loops=<span class="hljs-number">1</span>) -> <span class="hljs-keyword">Index</span> <span class="hljs-keyword">Scan</span> <span class="hljs-keyword">using</span> half_million_counter_idx <span class="hljs-keyword">on</span> half_million (<span class="hljs-keyword">cost</span>=<span class="hljs-number">0.00</span>.<span class="hljs-number">.14120</span><span class="hljs-number">.29</span> <span class="hljs-keyword">rows</span>=<span class="hljs-number">500000</span> width=<span class="hljs-number">5</span>) (actual <span class="hljs-keyword">time</span>=<span class="hljs-number">0.010</span>.<span class="hljs-number">.683</span><span class="hljs-number">.674</span> <span class="hljs-keyword">rows</span>=<span class="hljs-number">500000</span> loops=<span class="hljs-number">1</span>) Total runtime: <span class="hljs-number">3833.310</span> ms (<span class="hljs-number">5</span> <span class="hljs-keyword">rows</span>)
通过创建索引,查询优化器已经决定了索引扫描时,如何查找 Merge join。
请注意,索引扫描和全表扫描(顺序扫描)之间的区别:后者(也称为“表扫描”)是通过扫描所有数据或索引所有页面来查找到适合的结果,而前者只扫描表中的每一行。
教程的第二部分内容,就介绍到这里。后续还会有《如何编写更好的SQL查询》系列的最后一篇文章,敬请期待。
转载请注明出自:葡萄城控件
以上是终极指南——如何编写更好的SQL查询?的详细内容。更多信息请关注PHP中文网其他相关文章!

热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

AI Hentai Generator
免费生成ai无尽的。

热门文章

热工具

记事本++7.3.1
好用且免费的代码编辑器

SublimeText3汉化版
中文版,非常好用

禅工作室 13.0.1
功能强大的PHP集成开发环境

Dreamweaver CS6
视觉化网页开发工具

SublimeText3 Mac版
神级代码编辑软件(SublimeText3)

热门话题

CentOS 和 Ubuntu 的关键差异在于:起源(CentOS 源自 Red Hat,面向企业;Ubuntu 源自 Debian,面向个人)、包管理(CentOS 使用 yum,注重稳定;Ubuntu 使用 apt,更新频率高)、支持周期(CentOS 提供 10 年支持,Ubuntu 提供 5 年 LTS 支持)、社区支持(CentOS 侧重稳定,Ubuntu 提供广泛教程和文档)、用途(CentOS 偏向服务器,Ubuntu 适用于服务器和桌面),其他差异包括安装精简度(CentOS 精

CentOS 安装步骤:下载 ISO 映像并刻录可引导媒体;启动并选择安装源;选择语言和键盘布局;配置网络;分区硬盘;设置系统时钟;创建 root 用户;选择软件包;开始安装;安装完成后重启并从硬盘启动。

CentOS 已停止维护,替代选择包括:1. Rocky Linux(兼容性最佳);2. AlmaLinux(与 CentOS 兼容);3. Ubuntu Server(需要配置);4. Red Hat Enterprise Linux(商业版,付费许可);5. Oracle Linux(与 CentOS 和 RHEL 兼容)。在迁移时,考虑因素有:兼容性、可用性、支持、成本和社区支持。

如何使用 Docker Desktop?Docker Desktop 是一款工具,用于在本地机器上运行 Docker 容器。其使用步骤包括:1. 安装 Docker Desktop;2. 启动 Docker Desktop;3. 创建 Docker 镜像(使用 Dockerfile);4. 构建 Docker 镜像(使用 docker build);5. 运行 Docker 容器(使用 docker run)。

Docker利用Linux内核特性,提供高效、隔离的应用运行环境。其工作原理如下:1. 镜像作为只读模板,包含运行应用所需的一切;2. 联合文件系统(UnionFS)层叠多个文件系统,只存储差异部分,节省空间并加快速度;3. 守护进程管理镜像和容器,客户端用于交互;4. Namespaces和cgroups实现容器隔离和资源限制;5. 多种网络模式支持容器互联。理解这些核心概念,才能更好地利用Docker。

Docker镜像构建失败的故障排除步骤:检查Dockerfile语法和依赖项版本。检查构建上下文中是否包含所需源代码和依赖项。查看构建日志以获取错误详细信息。使用--target选项构建分层阶段以识别失败点。确保使用最新版本的Docker引擎。使用--t [image-name]:debug模式构建镜像以调试问题。检查磁盘空间并确保足够。禁用SELinux以防止干扰构建过程。向社区平台寻求帮助,提供Dockerfile和构建日志描述以获得更具体的建议。

VS Code 系统要求:操作系统:Windows 10 及以上、macOS 10.12 及以上、Linux 发行版处理器:最低 1.6 GHz,推荐 2.0 GHz 及以上内存:最低 512 MB,推荐 4 GB 及以上存储空间:最低 250 MB,推荐 1 GB 及以上其他要求:稳定网络连接,Xorg/Wayland(Linux)

CentOS 停止维护后,用户可以采取以下措施应对:选择兼容发行版:如 AlmaLinux、Rocky Linux、CentOS Stream。迁移到商业发行版:如 Red Hat Enterprise Linux、Oracle Linux。升级到 CentOS 9 Stream:滚动发行版,提供最新技术。选择其他 Linux 发行版:如 Ubuntu、Debian。评估容器、虚拟机或云平台等其他选项。
