关于 SQL 查询复杂性与性能是不是有任何一般规则?
Posted
技术标签:
【中文标题】关于 SQL 查询复杂性与性能是不是有任何一般规则?【英文标题】:Is there any general rule on SQL query complexity Vs performance?关于 SQL 查询复杂性与性能是否有任何一般规则? 【发布时间】:2010-01-14 16:38:03 【问题描述】:1)如果不使用索引,SQL 查询执行时间与连接数相比是否为 O(n)?如果不是,我们可能期待什么样的关系?索引能否提高实际的大 O 时间复杂度,还是仅将整个查询时间减少某个常数因子?
有点模糊的问题,我敢肯定它变化很大,但我在这里说的是一般意义上的。
2) 如果您有如下查询:
SELECT T1.name, T2.date
FROM T1, T2
WHERE T1.id=T2.id
AND T1.color='red'
AND T2.type='CAR'
我是否正确假设 DB 在评估多表条件之前先对 T1.color 和 T2.type 进行单表过滤?在这种情况下,使查询更复杂可能会使其更快,因为更少的行需要进行连接级测试?
【问题讨论】:
我认为您的数据库的设计方式(例如,索引)和查询中的内容有很大的依赖性,所以它不仅仅是一种您可以查看查询并给出确定的答案。查询分析器将是您找出要更改查询和数据库以获得最快结果的最佳选择。 所以你告诉我数据库设计没有基本的科学原理?我不买它,数据库上有大量的 CS 理论。 @John,数据库设计有很多合理的原则,甚至更多人不知道这些原则。没有任何东西可以保证良好的设计原则已应用于任何特定的数据库。正如 Will 所说,Query Analyzer 确实是最好的选择。看执行计划。查看选定的索引,并注意可能需要索引或可能存在但未使用的索引。运行 SQL Profiler 并收集性能日志,然后分析这些日志。 Cylon,是的,这就是我问这个问题的原因,以了解有关该理论的更多信息。分析器应该用于优化数据库,而不是设计它! 【参考方案1】:这取决于使用的查询计划。
即使没有索引,现代服务器也可以使用比O(N * M)
更快的HASH JOIN
和MERGE JOIN
更具体地说,HASH JOIN
的复杂度是O(N + M)
,其中N
是哈希表,M
是查找表。散列和散列查找具有恒定的复杂性。
MERGE JOIN
的复杂度是 O(N*Log(N) + M*Log(M))
:它是对两个表进行排序的时间加上扫描它们的时间的总和。
SELECT T1.name, T2.date
FROM T1, T2
WHERE T1.id=T2.id
AND T1.color='red'
AND T2.type='CAR'
如果没有定义索引,引擎将选择HASH JOIN
或MERGE JOIN
。
HASH JOIN
的工作原理如下:
选择散列表(通常是记录较少的表)。说是t1
扫描来自t1
的所有记录。如果记录包含color='red'
,则此记录以id
作为键,name
作为值进入哈希表。
扫描来自t2
的所有记录。如果记录为type='CAR'
,则在哈希表中搜索其id
,并返回所有哈希命中的name
值以及data
的当前值。
MERGE JOIN
的工作原理如下:
t1 (id, name)
的副本已创建,按id
排序
t2 (id, data)
的副本已创建,并按id
排序
两个表中的指针都设置为最小值:
>1 2<
2 3
2 4
3 5
指针在循环中进行比较,如果匹配,则返回记录。如果它们不匹配,则具有最小值的指针前进:
>1 2< - no match, left pointer is less. Advance left pointer
2 3
2 4
3 5
1 2< - match, return records and advance both pointers
>2 3
2 4
3 5
1 2 - match, return records and advance both pointers
2 3<
2 4
>3 5
1 2 - the left pointer is out of range, the query is over.
2 3
2 4<
3 5
>
在这种情况下,使查询更复杂可能会使其更快,因为更少的行需要进行连接级测试?
当然。
没有WHERE
子句的查询:
SELECT T1.name, T2.date
FROM T1, T2
更简单,但返回的结果更多,运行时间更长。
【讨论】:
连接通常不会对非唯一值进行交叉连接吗?也就是说,在Merge Join的第4步的第二步之后,它应该增加左指针而不是右指针,实际上保持3个不同的指针,更一般的情况下是4个。 (其中表 B 也有第二个 2)在所有值都相同的可怕情况下,确实应该有 n*m 个结果行。例如***.com/questions/916414/… 是的,哎呀。它应该只有 3,因为您只需要一个嵌套循环。 哈希索引可能基于某些内存引擎,而不是所有引擎?对吗? @brucenan:你什么意思? 您的伪图形,先生,使这个答案绝对精彩。非常感谢!【参考方案2】:注意不要把太多不同的东西混为一谈。您有一个基于要检查的行数的查询逻辑成本,一个(可能)较小的基于实际返回的行数的逻辑成本和一个不相关的基于必须检查的页数的物理成本。
这三个是相关的,但不是很密切。
检查的行数是这些成本中最大的,也是最不容易控制的。这些行必须通过连接算法进行匹配。这也是最不相关的。
返回的行数成本更高,因为这是客户端应用程序和数据库之间的 I/O 带宽。
读取的页数是最昂贵的,因为它需要更多的物理 I/O。这是最昂贵的,因为这是数据库内部的负载,会影响所有客户端。
一个表的 SQL 查询是 O( n )。那就是行数。根据页数,它也是 O( p )。
对于多个表,检查的行数为 O(nm...)。这就是嵌套循环算法。然而,根据关系的基数,结果集可能小到 O( n ),因为关系都是 1:1。但是必须检查每个表的匹配行。
哈希联接用 O( n ) 直接哈希查找替换 O( n*log(n) ) 索引 + 表读取。您仍然需要处理 O( n ) 行,但会绕过一些索引读取。
Merge Join 将 O( nm ) 嵌套循环替换为 O( log(n+m)(n+m) )排序操作。
使用索引,如果仅检查表是否存在,物理成本可能会降低到O(log(n)m)。如果需要行,则索引会加快对行的访问速度,但必须处理所有匹配的行。 O(nm) 因为这是结果集的大小,与索引无关。
为这项工作检查的页面可能会更小,这取决于索引的选择性。
索引的意义不是减少检查的行数。这是为了减少获取行的物理 I/O 成本。
【讨论】:
没有索引,嵌套循环算法几乎不会被任何体面的引擎选择。它将是HASH JOIN
或 MERGE JOIN
。
看来你的答案和 Quassnoi 是相辅相成的。一个用于索引版本,另一个用于非索引版本。我想说的是,在 Merge Join 的索引案例中,如果它遵循 Quassnoi 提出的策略,它将是 O(n+m)。但是,如果它很聪明,它会比较 O(n+m) 和 O(m log(n)),取较小的,然后使用上述策略或使用索引在表 n 中查找结果,顺便说一句应该是更大的表。【参考方案3】:
如果不使用索引,SQL 查询执行时间与连接数相比是否为 O(n)?
一般情况下,它们将是 O(n^m),其中 n 是涉及的每个表的记录数,m 是要连接的表数。
索引能否提高实际的大 O 时间复杂度,还是仅将整个查询时间减少某个常数因子?
两者兼而有之。当连接被大量过滤时(即使用良好的 WHERE 子句),索引允许直接查找,并且当它们位于正确的列上时,它们允许更快的连接。
当索引不在被连接或过滤的列上时,它们没有任何帮助。
【讨论】:
【参考方案4】:查看 clustered 与 non-clustered indexes 的工作原理
这是从纯技术的角度来看...为了便于解释,我的好伙伴 mladen 编写了一个简单的 article to understand 索引。
索引肯定有帮助,但我确实建议阅读以了解利弊。
【讨论】:
以上是关于关于 SQL 查询复杂性与性能是不是有任何一般规则?的主要内容,如果未能解决你的问题,请参考以下文章