优化给定的 sql 查询以提高速度
Posted
技术标签:
【中文标题】优化给定的 sql 查询以提高速度【英文标题】:optimize given sql query for speed 【发布时间】:2021-11-02 12:06:19 【问题描述】:我从一位程序员同事那里得到了这个查询,但它执行得非常慢。运行大约需要 4 秒。
能否优化此查询以提供相同的结果,但性能更好?
SELECT checkedcrates,
pv.name as powervision,
cg.name as cgrp,
cs.name as csz,
c.name as cname
FROM public.inspectionresultsstatistic e,
crates c,
powervisions pv,
lines l,
quality q,
cratesgroupscrates cgc,
cratesgroups cg,
cratessizes cs
where
c.id = e.crateid
and l.id = e.lineid
and pv.id = l.powervisionid
and q.id = e.qualityid
and c.id = cgc.crateid
and cs.id = cgc.cratesizeid
and cg.id = cgc.crategroupid
and qualityid = 0
and pv.name in ('PV101')
and c.name in ('24603','104','136','154','186','106','156','216','246','206')
and cg.name in ('Black','Blue','DLL','Green')
and cs.name in ('30x40','60x40')
and to_timestamp(e.startts) >= '2021-10-18T17:45:22Z'
and to_timestamp(e.stopts-1) <= '2021-10-18T19:45:22Z'
group by
powervision,
cgrp,
csz,
cname,
checkedcrates,
startts
编辑:实际上只是注意到内部选择很慢...通过删除外部查询更新了上面的查询
EDIT2:也许我应该添加一些索引?我为每个连接到另一个表的表(所以所有 ID 列)都有一个索引,并且对于检查结果统计有一个 id + qualityid + startts + stopts 的索引
EDIT3:根据要求,我尝试提供有关我的表格和数据的更多信息。
我正在使用 PostgreSql 12,
表结构如下:
http://sqlfiddle.com/#!17/bb5a6/1
除了包含大约 12.000.000 行的inspectionresultsstatistics 之外,所有表都相当小,条目少于50 个。
【问题讨论】:
这里的优化路径是为您的表添加有用的索引。为了帮助您解决这个问题,我们需要更多信息。请使用您使用的 DBMS 标记您的问题(oracle、mysql、sql-server、postgresql)。并且,请read this,然后edit 你的问题。 构造to_timestamp(column)
通过使 WHERE 子句 non-sargable 来击败 column
上的任何索引。如果不了解列的数据类型,就不可能给你体面的建议。
@O.Jones 好的,谢谢,尽快更新。 to_timestamp 问题是否有解决方法?这可以放在等式的右边吗?
是的,to_timestamp() 子句可以重构。但并非不知道这些列中的内容。
@O.Jones 我更新了帖子,使用包含所有表的 SQL 小提琴。所有表都相当小,条目少于 50 个,但包含大约 12.000.000 行的inspectionresultsstatistics 除外。
【参考方案1】:
我最大的特点是你的连接不是用更新的语法编写的。当您在 FROM 子句中的表之间使用逗号时,它会创建两个表的“笛卡尔积”。这比使用 'INNER JOIN' 后跟 'ON' 子句更耗费资源。应使用 ON 子句代替 WHERE 子句中的约束。
类似的,
SELECT checkedcrates,
pv.name as powervision,
cg.name as cgrp,
cs.name as csz,
c.name as cname
FROM public.inspectionresultsstatistic e
INNER JOIN crates c
ON c.id = e.crateid
INNER JOIN lines l
ON l.id = e.lineid
INNER JOIN powervisions pv
ON pv.id = l.powervisionid
INNER JOIN quality q
ON q.id = e.qualityid
INNER JOIN cratesgroupscrates cgc
ON c.id = cgc.crateid
INNER JOIN cratesgroups cg
ON cg.id = cgc.crategroupid
INNER JOIN cratessizes cs
ON cs.id = cgc.cratesizeid
where qualityid = 0
and pv.name in ('PV101')
and c.name in ('24603','104','136','154','186','106','156','216','246','206')
and cg.name in ('Black','Blue','DLL','Green')
and cs.name in ('30x40','60x40')
and to_timestamp(e.startts) >= '2021-10-18T17:45:22Z'
and to_timestamp(e.stopts-1) <= '2021-10-18T19:45:22Z'
group by
powervision,
cgrp,
csz,
cname,
checkedcrates,
startts
【讨论】:
实际上,Oracle 基于成本的查询计划器使用 WHERE 子句中的连接条件识别和处理您祖父的逗号连接,其性能与使用 ON 子句的连接相同。 ON 子句仍然更容易阅读。 感谢您的回答。我只是尝试运行您的查询,它需要相同的时间。也许我应该创建一些索引?我应该创建哪些索引来加速这个特定的查询? (更新问题以包括此) @O.Jones 这就是为什么 Oracle 的每个核心是 5 位数!我已经看到使用 SQL Server 显着提高了性能,但在这种情况下似乎没有帮助:( 其他 DBMS 品牌和模型也可以有效地处理逗号连接语法,包括 sql-server、mysql 和 postgresql。你说得对,Oracle 的价格高得惊人。 规划者对 WHERE 中的连接条件没有任何问题。编写和读取查询的人受益于将连接分解为显式 ON。【参考方案2】:您的小提琴不包括索引。您应该使用 db-fiddle.com 而不是 sqlfiddle,因为后者本质上已经损坏,没有比 9.6 更新的版本。
被查询的时间范围很小,所以想必是很有选择性的。
您最好将其作为一个范围进行查询:
and tstzrange(to_timestamp(e.startts),to_timestamp(e.stopts-1),'[]') <@
tstzrange('2021-10-18T17:45:22Z','2021-10-18T19:45:22Z','[]')
这将受益于表达式索引
on inspectionresultsstatistic using gist (tstzrange(to_timestamp(startts),to_timestamp(stopts-1),'[]'))
但实际上,最好将时间戳存储为 timestamptz 而不是 int8,或者甚至直接将它们存储为 tstzrange。
【讨论】:
以上是关于优化给定的 sql 查询以提高速度的主要内容,如果未能解决你的问题,请参考以下文章