这个简单的SQL查询可以优化吗?

时间:2010-05-12 13:15:18

标签: sql sql-server

我有以下查询:

SELECT COUNT(*) 
FROM Address adr INNER JOIN 
     Audit a on adr.UniqueId = a.UniqueId
  • 在数据库上(130万个地址,超过400万次审核)
  • 两个UniqueId列都是群集主键

查询需要很长时间才能完成。我觉得愚蠢,但有没有办法优化它?我想计算所有具有基础可审计的地址条目。

编辑:非常感谢您的所有输入,以下是更多详情:

  • 查询不会经常运行(它仅用于验证),但是感谢索引的视图提示,我肯定会将其添加到我的知识中。
  • 所有地址都有相关的一对一审核。并非所有审核都是地址。
  • 查询需要1分钟以上才能完成。我发现这个问题太长了,无法进行简单统计。

8 个答案:

答案 0 :(得分:11)

由于您有两组数据,按相同的值排序..您是否尝试过合并连接而不是嵌套循环连接?

SET STATISTICS IO ON
SET STATISTICS TIME ON

SELECT COUNT(*)  
FROM Address adr INNER JOIN  
     Auditable a on adr.UniqueId = a.UniqueId 
OPTION (LOOP JOIN)

SELECT COUNT(*)  
FROM Address adr INNER JOIN  
     Auditable a on adr.UniqueId = a.UniqueId 
OPTION (MERGE JOIN)

SELECT COUNT(*)  
FROM Address adr INNER JOIN  
     Auditable a on adr.UniqueId = a.UniqueId 
OPTION (HASH JOIN)

编辑:

这些解释是概念性的。 SQL Server可能比我的示例显示更复杂的操作。这种概念性理解与SET STATISTICS命令对时间和逻辑IO的测量以及查询执行计划的检查相匹配,构成了我的查询优化技术(长达四年)的基础。愿它和你一样为你服务。

设置

  • 获得5副牌。
  • 取1个牌组并生成父数据集。
  • 取其他4个套牌并生成子数据集。
  • 按卡值订购每个数据集。
  • 设m为父数据集中的卡数。
  • 设n为子数据集中的卡数。

NestedLoop

  • 从父数据集的顶部取出一张卡片。
  • 在第一次出现匹配项的子数据集中搜索(使用二进制搜索)。
  • 从第一个匹配项中查找子数据集,直到找到不匹配项。你现在找到了所有的比赛。
  • 对父数据集中的每张卡重复此操作。

嵌套循环算法迭代父数据集,然后为每个父数据集搜索子数据集一次,使其成本为:m * log(n)

合并

  • 从父数据集的顶部取出一张卡片。
  • 从子数据集的顶部取一张卡片。
  • 如果牌匹配,从每个牌组的顶部拉出牌,直到找到不匹配的牌。在父匹配和子匹配之间生成每个匹配对。
  • 如果卡片不匹配,请在父卡和子卡之间找到较小的卡片,然后从该数据集的顶部取出卡片。

合并连接算法迭代父数据集一次,子数据集迭代一次,使其成本为:m + n。它依赖于订购的数据。如果您要求对未订购的数据进行合并连接,则需要进行订购操作!这使得成本为(m * log(m))+(n * log(n))+ m + n。在某些情况下,甚至可能比嵌套循环更好。

哈希

  • 拿一张牌桌。
  • 从父数据集中取出每张卡并将其放在卡片桌上,您可以在其中找到它(不必与卡片值有任何关系,只需方便您)。
  • 从子数据集中取出每张卡片,在纸板桌子上找到匹配的父卡片并生成匹配的一对。

散列连接算法迭代父数据集一次,子数据集迭代一次,使其成本为:m + n。它依赖于拥有足够大的卡表来保存父数据集的全部内容。

答案 1 :(得分:6)

如果经常运行此查询并且需要超快,请创建它的物化索引视图。 INSERT / UPDATE / DELETE会有轻微的开销,但这个查询只是即时的。聚合可以预先计算并存储在索引中,以最大限度地减少查询执行期间的昂贵计算。

Improving Performance with SQL Server 2005 Indexed Views

答案 2 :(得分:2)

真正的问题是嵌套循环连接。对于地址表中的每个1.4百万行,您正在进行索引搜索Audble表。这意味着1.4M根块,分支块和叶块读取总共4.2M块读取。整个索引可能只有5K块左右......它应该进行散列连接,因此它会读取两个索引,并通过它们进行哈希。

如果您认为这些表很大,我猜这是在没有大量内存的小盒子上。您需要确保分配足够的内存以使整个索引适合内存,以使散列连接有效。

答案 3 :(得分:1)

Auditable.UniqueID是否是对Address.UniqueID的外键引用,这意味着Auditable中没有值也不存在于地址中?

如果是这样,这可能会有效并可能更快:

SELECT COUNT(DISTINCT Auditable.UniqueID)
FROM Auditable

注意:这也假设UniqueID在地址表中是唯一的(/主键),但在Auditable表中不是唯一的

答案 4 :(得分:1)

EXISTS子句的运行成本低于INNER JOIN。

select COUNT(adr.UniqueId)
    from Addresses adr
    where EXISTS (
        select 1
            from Auditables aud
            where aud.UniqueId = adr.UniqueId
    )

这是否适合您的需要?

N.B。 Guids对于数据库引擎来说非常昂贵。

答案 5 :(得分:0)

不确定它是否会更快但您可以尝试以下

SELECT COUNT(adr.UniqueID) FROM Address adr INNER JOIN Auditable a on adr.UniqueId = a.UniqueId

它应该给你相同的数量,因为unqieieid永远不会为空。

答案 6 :(得分:0)

我会说,缺少外键索引。

  • 140万和400万不是大表,它们很小。请注意,当你通过5亿条款时说得很大。

  • 要获得真实答案,我们需要执行计划/查询计划,以便我们了解会发生什么。

  • 很高兴知道你的世界里有什么“龙”(考虑到你认为400万行很多)。这个问题永远不会在1秒内回答 - 那么你期待什么以及会发生什么?

  • 我敢打赌,你的索引缺失了。顺便说一句,我会开始指着硬件(因为我也看到了,作为废话性能的原因)。

答案 7 :(得分:0)

对于诸如此类的大型表,您可能希望对数据进行分区以提高查询性能。此外,如果您还没有,请尝试运行Tuning Advisor以查看是否存在可能有利的任何其他索引。此外,您最近是否重新组织了聚簇索引 - 这是维护包的一部分吗?很多时候,这也会大大提高你的表现。