McS*_*onk 5 postgresql performance subquery postgresql-9.4 sqlalchemy query-performance
假设我们有以下查询:
1.
SELECT COUNT(*) FROM some_big_table WHERE some_col = 'some_val'
Run Code Online (Sandbox Code Playgroud)
2.
SELECT COUNT(*) FROM ( SELECT * FROM some_big_table WHERE some_col = 'some_val' )
Run Code Online (Sandbox Code Playgroud)
之前的任何查询是否性能更好?或者他们是一样的?
我使用的是 Postgresql 9.4,但是与其他 DBMS 有很大不同吗?
PS:我问这个问题是因为 SQLAlchemy 是一个基于 Python 的 ORM,COUNT它默认在子查询上执行操作,但有一个选项可以强制它COUNT直接在查询上执行。
您有理由相信它们的表现会有所不同吗?如果是这样,为什么不在您的 RDBMS 和您的数据上测试它们?一般来说,我会说从最简单的查询开始,测试性能,如果需要,只尝试更复杂的东西(例如选项 2)。
您没有列出 RDBMS,所以我将给出一个 SQL Server 示例。SQL Server 查询优化器不直接使用您编写的 SQL。它将其转换为内部格式以进行优化。下图描述了优化的各个阶段,并借鉴自Query Optimizer Deep Dive - Part 1 by Paul White:
这是我针对公开可用的 Adventure Works 2014 数据库的测试查询:
SELECT COUNT(*)
FROM Sales.SalesOrderDetail
WHERE CarrierTrackingNUmber = '2E53-4802-85'
Run Code Online (Sandbox Code Playgroud)
使用未记录的跟踪标志 8606 进行简化后,我可以获得查询树的表示:
*** Simplified Tree: ***
LogOp_GbAgg OUT(COL: Expr1002 ,)
LogOp_Select
LogOp_Get TBL: Sales.SalesOrderDetail Sales.SalesOrderDetail TableID=1154103152 TableReferenceID=0 IsRow: COL: IsBaseRow1000
ScaOp_Comp x_cmpEq
ScaOp_Identifier QCOL: [AdventureWorks2014].[Sales].[SalesOrderDetail].CarrierTrackingNumber
ScaOp_Const TI(nvarchar collate 872468488,Var,Trim,ML=24) XVAR(nvarchar,Owned,Value=Len,Data = (24,506953514552564850455653))
AncOp_PrjList
AncOp_PrjEl COL: Expr1002
ScaOp_AggFunc stopCount
ScaOp_Const TI(int,ML=4) XVAR(int,Not Owned,Value=0)
Run Code Online (Sandbox Code Playgroud)
以上是您的查询计划的内部表示。该查询计划中的实际步骤在这里并不重要。重要的是我为这个查询得到了完全相同的简化树:
SELECT COUNT(*)
FROM
(
SELECT *
FROM Sales.SalesOrderDetail
WHERE CarrierTrackingNUmber = '2E53-4802-85'
) t;
Run Code Online (Sandbox Code Playgroud)
实际上,这意味着 SQL Server 在继续优化之前将这两个查询重写为完全相同的内容。这意味着它们两者之间不会有性能差异。正如预期的那样,它们具有完全相同的查询计划:
使用 Postgres 进行测试设置:
create table data (id integer, some_value integer);
insert into data
select i, i % 10
from generate_series(1,1000000) i;
create index on data (some_value);
analyze data;
Run Code Online (Sandbox Code Playgroud)
所以我们有一个包含 100 万行和 10 个不同值的表some_value,所以像这样的条件some_value = 9将选择 100000 行:
explain (analyze,verbose)
select count(*)
from data
where some_value = 9;
Run Code Online (Sandbox Code Playgroud)
给我们:
create table data (id integer, some_value integer);
insert into data
select i, i % 10
from generate_series(1,1000000) i;
create index on data (some_value);
analyze data;
Run Code Online (Sandbox Code Playgroud)
和:
explain (analyze,verbose)
select count(*)
from (
select *
from data
where some_value = 9
) x;
Run Code Online (Sandbox Code Playgroud)
显示这个:
explain (analyze,verbose)
select count(*)
from data
where some_value = 9;
Run Code Online (Sandbox Code Playgroud)
如您所见:相同的计划和基本相同的运行时间
如果没有索引,我们再次得到相同的执行计划:
Aggregate (cost=7761.13..7761.14 rows=1 width=8) (actual time=16.790..16.791 rows=1 loops=1)
Output: count(*)
-> Bitmap Heap Scan on stuff.data (cost=1854.13..7514.13 rows=98800 width=0) (actual time=4.703..14.001 rows=100000 loops=1)
Output: id, some_value
Recheck Cond: (data.some_value = 9)
Heap Blocks: exact=4425
-> Bitmap Index Scan on data_some_value_idx (cost=0.00..1829.43 rows=98800 width=0) (actual time=4.243..4.243 rows=100000 loops=1)
Index Cond: (data.some_value = 9)
Planning time: 0.101 ms
Execution time: 16.876 ms
Run Code Online (Sandbox Code Playgroud)
explain (analyze,verbose)
select count(*)
from (
select *
from data
where some_value = 9
) x;
Run Code Online (Sandbox Code Playgroud)
在这个合理简化的示例中,任何合理的现代查询优化器都应该将这两种变体理解为相同的查询。结构化查询语言不是过程性的,而是声明性的。声明性语言描述了期望的结果,而不是如何获得结果。
你可以而且应该自己检查一下。运行这两个查询并检查查询处理器的每个变体的操作计划以查看您的系统实际执行情况非常容易。
我正在使用 SQL Server,并且碰巧有一个合理大小的表来检查它。使用 SSMS,从“查询”菜单中选择“包括实际执行计划”,然后运行:
SELECT COUNT(*) FROM dbo.Searchable;
SELECT COUNT(*) FROM (SELECT * FROM dbo.Searchable) st;
Run Code Online (Sandbox Code Playgroud)
计划:
几乎每个主要的 DBMS,包括 PostgreSQL,都提供了一些检查执行计划的机制,如上所述。