【PostgreSQL里的子查询解析】

什么是子查询?

子查询是一种嵌套在其他SQL查询中的查询方式,也可以称作内查询或嵌套查询。当一个查询是另一个查询的条件时,就称之为子查询。子查询的语法格式与普通查询相同,但其在查询过程中起着临时结果集的作用,为主查询提供所需数据或对检索数据进行进一步的限制。

子查询最重要的方面是有所谓的相关子查询和不相关子查询。

相关子查询与不相关子查询

不相关子查询是不从外部引用任何其他内容的查询,而相关子查询是指子 SELECT 内部的查询从外部引用某些内容。

这个区别很重要,因为当 PostgreSQL规划器遇到不相关的子查询时,PostgreSQL不会将其拉入整体查询计划,会将其与主查询分开处理。当这种情况发生时,在计划中看到EXPLAIN所谓的InitPlan.这本质上是一个独立于查询的其余部分完成的初始计划,例如,可以用于执行其他一些查找。但该子查询本身不需要主查询的结果集。

不相关的子查询几乎从来都不是性能问题。原因是它们通常只执行一次。

image.png

标量子查询

标量子查询是指查询仅返回单个数据点、单个值。例如,当我们说WHERE我们正在匹配元素的数量时,我们SELECT COUNT(*) FROM something。那么这将是返回的单个值,返回的单个行。这是一个标量子查询。

在FROM返回多行的列表可以使用公共表表达式(CTE)以及IN 或 EXISTS 或 NOT EXISTS 表达式。

标量相关子查询实际上有时候会存在性能问题,如果有标量子查询,由于 PostgreSQL执行它的方式,它必须作为嵌套循环实现。对于遇到的每一行,它都必须评估相关标量子查询。这意味着,如果从开始的表扫描中获得了大量结果,则必须对每个结果运行标量子查询。这会变得非常慢。

优化方式:
将标量子查询重写为join时,PostgreSQL将能够使用除嵌套循环之外的其他连接。或者使用group by having来使PostgreSQL选择更好的连接策略。

IN 与 NOT IN的相关问题

在我们平时写SQL时,如果遇到需要排除某些数据时,往往使用id <> xxx and id <> xxx,进而改进为id not in (xxx, xxx)。
这样写没有问题,而且简化了SQL,但是有时候,使用not in就会造成极大的性能损耗,例如:

select * from t where id not in (select id from t2) and info like '%haha%';

这样的话select id from t2将成为一个子查询,而且不会走索引,每次走一遍全表扫描。
每一条满足info like '%haha%'的记录都会去调用这个方法去判断id是否不在子查询中,正确的方式应该使用not exists,将条件下推到里面,就不会出现子查询了

postgres<16.1>(ConnAs[postgres]:PID[322204] 2024-05-13/16:53:54)=# create table t(id int,info text);
CREATE TABLE
postgres<16.1>(ConnAs[postgres]:PID[322204] 2024-05-13/16:53:58)=# insert into t select generate_series(1, 100), 'haha'||round(random()*10000)::text;
INSERT 0 100
postgres<16.1>(ConnAs[postgres]:PID[322204] 2024-05-13/16:54:39)=# select * from t limit 10;
+----+----------+
| id |   info   |
+----+----------+
|  1 | haha8355 |
|  2 | haha677  |
|  3 | haha6474 |
|  4 | haha1194 |
|  5 | haha7640 |
|  6 | haha7955 |
|  7 | haha8621 |
|  8 | haha4712 |
|  9 | haha1299 |
| 10 | haha5464 |
+----+----------+
(10 rows)

postgres<16.1>(ConnAs[postgres]:PID[322204] 2024-05-13/16:54:51)=#  insert into t select generate_series(101, 200), 'ha'||round(random()*10000)::text;
INSERT 0 100
postgres<16.1>(ConnAs[postgres]:PID[322204] 2024-05-13/16:55:19)=# create table t2 as  select * from t where id between 50 and 70;
SELECT 21

postgres<16.1>(ConnAs[postgres]:PID[322204] 2024-05-13/16:58:01)=# explain (analyze,verbose) select * from t where id not in (select id from t2) and info like '%haha%';
+----------------------------------------------------------------------------------------------------------------+
|                                                   QUERY PLAN                                                   |
+----------------------------------------------------------------------------------------------------------------+
| Seq Scan on public.t  (cost=25.88..30.88 rows=49 width=11) (actual time=0.567..0.653 rows=79 loops=1)          |
|   Output: t.id, t.info                                                                                         |
|   Filter: ((NOT (hashed SubPlan 1)) AND (t.info ~~ '%haha%'::text))                                            |
|   Rows Removed by Filter: 121                                                                                  |
|   SubPlan 1                                                                                                    |
|     ->  Seq Scan on public.t2  (cost=0.00..22.70 rows=1270 width=4) (actual time=0.055..0.058 rows=21 loops=1) |
|           Output: t2.id                                                                                        |
| Query Identifier: -1149162323192504476                                                                         |
| Planning Time: 1.599 ms                                                                                        |
| Execution Time: 1.160 ms                                                                                       |
+----------------------------------------------------------------------------------------------------------------+
(10 rows)

postgres<16.1>(ConnAs[postgres]:PID[322204] 2024-05-13/16:58:41)=# explain  (analyze,verbose) select * from t where info like '%test%' and not exists (select 1 from t2 where t2.id = t.id);
+---------------------------------------------------------------------------------------------------------------+
|                                                  QUERY PLAN                                                   |
+---------------------------------------------------------------------------------------------------------------+
| Hash Right Anti Join  (cost=4.51..32.04 rows=1 width=11) (actual time=0.112..0.114 rows=0 loops=1)            |
|   Output: t.id, t.info                                                                                        |
|   Hash Cond: (t2.id = t.id)                                                                                   |
|   ->  Seq Scan on public.t2  (cost=0.00..22.70 rows=1270 width=4) (never executed)                            |
|         Output: t2.id, t2.info                                                                                |
|   ->  Hash  (cost=4.50..4.50 rows=1 width=11) (actual time=0.055..0.055 rows=0 loops=1)                       |
|         Output: t.id, t.info                                                                                  |
|         Buckets: 1024  Batches: 1  Memory Usage: 8kB                                                          |
|         ->  Seq Scan on public.t  (cost=0.00..4.50 rows=1 width=11) (actual time=0.051..0.051 rows=0 loops=1) |
|               Output: t.id, t.info                                                                            |
|               Filter: (t.info ~~ '%test%'::text)                                                              |
|               Rows Removed by Filter: 200                                                                     |
| Query Identifier: 4310501580026383116                                                                         |
| Planning Time: 0.405 ms                                                                                       |
| Execution Time: 0.152 ms                                                                                      |
+---------------------------------------------------------------------------------------------------------------+
(15 rows)

image.png

exits和not exits的意思是逐条将条件下放到判断条件,而join方式是先对表进行笛卡尔积,然后判断同行之间的各列值是否满足关系。exists,由于优化器会默认它只需要搜索到1条命中目标就不搜了,所以优化器评估是否使用hash table时,需要的内存相对较少,即使较小的work_mem也可能使用hashtable。in ,当出现在subquery中时,优化器评估这个subquery是否要构建哈希TABLE,直接和subquery的大小相关,所以需要较大的work_mem才会选择使用hashtable。

除此之外PostgreSQL的not in不能识别到null。not in 后面的集合中有null值,会导致查询失效。
所以可以在使用 not in 后的集合中 使用 is not null 避免查询失效,或者使用 not exists替代

postgres<16.1>(ConnAs[postgres]:PID[322204] 2024-05-13/17:25:31)=# CREATE TABLE a(info varchar(20));
CREATE TABLE
postgres<16.1>(ConnAs[postgres]:PID[322204] 2024-05-13/17:25:34)=# CREATE TABLE b(info varchar(20));
CREATE TABLE
postgres<16.1>(ConnAs[postgres]:PID[322204] 2024-05-13/17:25:37)=# insert into a values('aaa'),('bbb');
INSERT 0 2
postgres<16.1>(ConnAs[postgres]:PID[322204] 2024-05-13/17:25:40)=# insert into b values('bbb'),('ccc'),(null);
INSERT 0 3
postgres<16.1>(ConnAs[postgres]:PID[322204] 2024-05-13/17:25:44)=#
postgres<16.1>(ConnAs[postgres]:PID[322204] 2024-05-13/17:25:45)=# ---not in
postgres<16.1>(ConnAs[postgres]:PID[322204] 2024-05-13/17:25:51)=# select info from a where info not in (select info from b);
+------+
| info |
+------+
+------+
(0 rows)

postgres<16.1>(ConnAs[postgres]:PID[322204] 2024-05-13/17:25:56)=# ---not in + is not null
postgres<16.1>(ConnAs[postgres]:PID[322204] 2024-05-13/17:26:11)=# select info from a where info not in (select info from b where info is not null);
+------+
| info |
+------+
| aaa  |
+------+
(1 row)

postgres<16.1>(ConnAs[postgres]:PID[322204] 2024-05-13/17:26:16)=# ---not exists
postgres<16.1>(ConnAs[postgres]:PID[322204] 2024-05-13/17:26:26)=# select info from  a
where not exists (
  select info from b where a.info = b.info
);
+------+
| info |
+------+
| aaa  |
+------+
(1 row)

image.png


执行计划对比如下

image.png

因此需要注意not in的这个集合中有null值,会导致查询失效问题,并且酌情可以考虑使用exists优化in。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/624411.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

冰川秘境:全球冰川可视化大屏带你穿越冰原

在浩瀚无垠的宇宙中&#xff0c;地球以其独特的蓝色光环吸引着人们的目光。而在这颗蓝色星球上&#xff0c;冰川这一大自然的杰作&#xff0c;更是以其壮美与神秘&#xff0c;让人们心驰神往。 从阿尔卑斯山脉的冰川到南极洲的冰盖&#xff0c;从格陵兰岛的冰山到喜马拉雅山脉的…

Visual Studio生成C++的DLL文件(最简单版)

前言 当你在使用C编写一些可重用的代码时&#xff0c;将其打包成一个动态链接库&#xff08;DLL&#xff09;可以使其更容易地被其他项目或者程序调用和使用。Visual Studio提供了一种简单的方式来生成C的DLL文件。下面是一个关于如何在Visual Studio中生成C的DLL文件的简单教…

基于ChatGPT 和 OpenAI 模型的现代生成式 AI

书籍&#xff1a;Modern Generative AI with ChatGPT and OpenAI Models: Leverage the capabilities of OpenAIs LLM for productivity and innovation with GPT3 and GPT4 作者&#xff1a;Valentina Alto 出版&#xff1a;Packt Publishing 书籍下载-《基于ChatGPT 和 Op…

【Unity】 HTFramework框架(四十八)使用Location设置Transform位置、旋转、缩放

更新日期&#xff1a;2024年5月14日。 Github源码&#xff1a;[点我获取源码] Gitee源码&#xff1a;[点我获取源码] 索引 Location定义Location复制Location变量的值复制Transform组件的Location值粘贴Location变量的值粘贴Location值到Transform组件在代码中使用Location Loc…

2.3 应用集成技术

第2章 信息技术知识 2.3 应用集成技术 2.3.1 数据库与数据仓库技术 数据库 以单一的数据源即数据库为中心进行事务处理、批处理、决策分析等各种数据处理工作操作型处理也称事务处理&#xff0c;指的是对联机数据库的日常操作&#xff0c;通常是对数据库中记录的查询和修改…

微信小程序主体变更的操作教程

小程序迁移变更主体有什么作用&#xff1f;进行小程序主体迁移变更&#xff0c;那可是益处多多呀&#xff01;比方说&#xff0c;能够解锁更多权限功能&#xff1b;在公司变更或注销时&#xff0c;还能保障账号的正常使用&#xff1b;此外&#xff0c;收购账号后&#xff0c;也…

Nat Plants | 植物抽核单细胞!多组学探究大豆根瘤成熟过程

发表时间&#xff1a;2023-04 发表期刊&#xff1a;Nature Plants 影响因子&#xff1a;17.352 DOI&#xff1a;10.1038/s41477-023-01387-z 研究背景 根瘤菌是亲和互作寄主植物&#xff0c;感染宿主并在根部形成共生器官根瘤&#xff0c;具有固氮…

vue3中通过自定义指令实现loading加载效果

前言 在现代Web开发中&#xff0c;提升用户体验一直是开发者们追求的目标之一。其中&#xff0c;一个常见的场景就是在用户与应用程序进行交互时&#xff0c;特别是当进行异步操作时&#xff08;如网络请求&#xff09;&#xff0c;为用户提供即时的反馈&#xff0c;避免用户因…

docker实验

1.Docker安装部署 &#xff08;1&#xff09;.关闭防火墙 &#xff08;2&#xff09;.更新源 &#xff08;3&#xff09;设置Docker仓库 &#xff08;4&#xff09;启动docker &#xff08;5&#xff09;查看版本&#xff1a; 2.Docker pull 容器并运行服务&#xff1b; 拉取…

项目9-网页聊天室1(注册+Bycrpt加密)

1.准备工作 1.1.前端页面展示 1.2 数据库的建立 我们通过注册页面&#xff0c;考虑如何设计用户表数据库。 用户id&#xff0c;userId用户名&#xff0c;唯一&#xff0c;username用户密码&#xff0c;password&#xff08;包括密码和确认密码ensurePssword【数据库没有该字段…

PXI/PXIe规格 A429/717 航电总线适配卡

A429是一款标准的PXI/PXIe1规格的多协议总线适配卡。该产品最多支持36个A429通道&#xff0c;或32个A429通道加4个A717通道&#xff0c;每个A429和A717通道可由软件配置成接收或发送&#xff0c;可满足A429总线和A717总线的通讯、测试和数据分析等应用需求。 该产品的每个A429通…

儿童身高成长:关注每一厘米的成长

引言&#xff1a; 儿童的身高发育是家长和教育者普遍关注的问题&#xff0c;它不仅关乎孩子的外貌形象&#xff0c;更与孩子的健康成长密切相关。本文将深入探讨儿童身高的注意事项&#xff0c;为家长和教育者提供科学的指导&#xff0c;帮助孩子健康成长。 1. 身高发育的基本知…

BM11 链表相加(二)

描述 假设链表中每一个节点的值都在 0 - 9 之间&#xff0c;那么链表整体就可以代表一个整数。 给定两个这种链表&#xff0c;请生成代表两个整数相加值的结果链表。 数据范围&#xff1a;0≤&#x1d45b;,&#x1d45a;≤10000000≤n,m≤1000000&#xff0c;链表任意值 0≤…

前端面试:项目细节|项目重难点|已工作|做分享

面试官提问&#xff1a;分享一个项目中记忆比较深刻的需求&#xff1f;说说你是怎么解决的&#xff1f;解决过程有没有遇到什么困难&#xff1f; 答&#xff1a;我的回答&#xff08;我分点写思路&#xff0c;便于大家观看&#xff09;&#xff1a; &#xff08;1&#xff09…

C语言例题41、八进制转换为十进制

#include<stdio.h>void main() {int x;printf("请输入一个8进制整数&#xff1a;");scanf("%o", &x);printf("转换成十进制后的整数为%d\n", x); }运行结果&#xff1a; 本章C语言经典例题合集&#xff1a;http://t.csdnimg.cn/FK0Qg…

Web3时代的技术革新:区块链与人工智能的融合

随着科技的飞速发展&#xff0c;区块链和人工智能作为两大颠覆性技术正呈现出日益紧密的融合趋势。在Web3时代&#xff0c;这种融合将推动技术革新&#xff0c;引领着我们进入全新的数字时代。本文将深入探讨区块链与人工智能的融合&#xff0c;探索其在各个领域的应用前景和挑…

美国多IP服务器为企业的数据分析提供了强大的技术支持

美国多IP服务器为企业的数据分析提供了强大的技术支持 在当今数字化时代&#xff0c;数据分析已经成为企业决策和战略规划的核心。而美国多IP服务器则为企业提供了强大的技术支持&#xff0c;帮助它们有效地进行数据分析&#xff0c;从而更好地理解市场、优化运营&#xff0c;…

常见物联网面试题详解

物联网一直是非常火热的行业&#xff0c;G端如智慧城市、智慧工厂、智慧园区、智慧水利、智慧矿山等行业&#xff0c;都会涉及到物联网&#xff0c;基本都是软硬一体&#xff0c;因此当面试相关企业时&#xff0c;物联网平台是面试企业重点考察的项&#xff0c;小伙伴如果从事相…

十一、 进行个人信息保护认证的流程是怎样的?

2022 年 11 月 18 日&#xff0c;国家市场监督管理总局和国家网信办发布的《认证公告》以及附件《认证规则》&#xff0c;对开展个人信息保护认证的流程进行了细节说明&#xff0c;包括认证委托、技术验证、现场审核、认证结果评价和批准等环节。《认证公告》指出“从事个人信息…

软件测试之【软件测试初级工程师技能点全解】

读者大大们好呀&#xff01;&#xff01;!☀️☀️☀️ &#x1f525; 欢迎来到我的博客 &#x1f440;期待大大的关注哦❗️❗️❗️ &#x1f680;欢迎收看我的主页文章➡️寻至善的主页 文章目录 &#x1f525;前言&#x1f680;初级测试工程师技能点&#x1f449;测试理论基…