2011-10-25 102 views
1

我有一个T-SQL查询,我想让它更快。SQL Server T-SQL查询优化

我有EntityAddress表,并希望带回地址,如果邮寄地址存在。

有时对于任何给定的实体有多个地址。有一个主要的邮寄地址tinyint,有时会被设置,有时不会,这里没有规则可以有5个默认的邮件地址所有的标志设置或没有设置标志。

这对11k行运行大约20秒我真的需要得到这个时间,任何人都可以帮忙吗?

SELECT 
    e.*, addr.* 
FROM 
    [Entity] e 
    --Address does not always exist 
    --PrimaryAddress is a Not Null TinyInt, sometimes this flag is enable twice for a given entity. 
LEFT OUTER JOIN 
    [Address] addr ON addr.[EntityID] = e.[EntityID] 
    AND addr.Code = 'MAILING'   
    AND addr.[AddressID] = (
     --This remove duplicates but add's a long delay(15 seconds) to execution time. 
     SELECT Top 1 a.[AddressID] 
     FROM [Address] AS a 
     WHERE a.Code = 'MAILING' 
     AND a.[EntityID] = e.[EntityID]  
     ORDER BY a.[PrimaryAddress] DESC) 

还应当指出的是,我不能任何索引添加到两个表要么:(

亲切的问候 西蒙·杰克逊

+0

这是第三方数据库,任何修改都不是“支持”的。 – Simon

+0

@marc_s,在不改变索引的情况下,通常会有很多可行的选择来进行性能调整。 – HLGEM

回答

1

这是您的查询的简化版本,我你可以告诉我,

SELECT 
    e.*, 
    addr.* 
FROM 
    [Entity] e 
    OUTER APPLY (
       SELECT TOP(1) * 
       FROM addr as a 
       WHERE a.Code = 'MAILING' 
       AND a.[EntityID] = e.[EntityID] 
       ORDER BY a.[PrimaryAddress] DESC 
      ) as addr 
+0

谢谢,这已经改进了一些东西,显然第一次运行时间大约是14秒,第二次是2秒。 – Simon

+0

@Simon:使用DBCC FREEPROCCACHE等在运行之前清除缓存 – sll

+0

DBCC FREEPROCCACHE,哦,亲爱的,23分钟和20秒的外层,我现在是我的原始版本。有很多分层视图。 – Simon

0

如果你是在SQL Server 20上,你会告诉我,如果这会比你的版本更快, 05或更高版本,你可以尝试以下方法:

WITH ranked AS (
    SELECT 
    *, 
    rn = ROW_NUMBER() OVER (PARTITION BY EntityID ORDER BY [PrimaryAddress] DESC) 
    FROM [Address] 
    WHERE Code = 'MAILING' 
) 
SELECT 
    e.*, a.* 
FROM [Entity] e 
    LEFT JOIN [Address] a ON a.[EntityID] = e.[EntityID] AND a.rn = 1 

此查询的结果将有超过你那一个微小的差别:会有的rn一个附加列与1的和/或NULL小号在里面。然而,我不认为这是一个问题,因为首先在生产查询中不推荐使用蒙版SELECT列表,如果这是非生产脚本,那么一个额外的列将不会妨碍。

参考文献:

+0

或者你可以在临时表中做到这一点,而不是CTE,它可以放置缺失的索引。 – HLGEM

+0

测试过这种类型的查询,平均得到9秒。感谢分享。 – Simon

1

你可以停止使用select *,你两次返回实体ID,这是浪费服务器和网络资源。你真的需要其他领域的每一个?消除你不需要的任何东西。无论如何,选择*不应该用于生产代码。

您拥有在痛苦的排排运行相关子查询,请尝试使用联接代替:

SELECT  e.*, addr.* 
FROM  [Entity] e  
LEFT JOIN (SELECT addr.* 
      FROM [Address] a 
      JOIN  
       (SELECT Top 1 a.[AddressID]   
       FROM [Address] AS a   
       WHERE a.Code = 'MAILING'   
       AND a.[EntityID] = e.[EntityID]    
       ORDER BY a.[PrimaryAddress] DESC) dedup 
        ON a.address_id = dedup.address_id) addr 
    ON addr.[EntityID] = e.[EntityID] 

而且又没有使用select *,我不知道你的领域或者我会指定他们以上。

当然,解决这个问题的真正方法是修复设计糟糕的数据库。它不应该允许多个主要地址(我们通过触发器强制执行此操作),那么您不需要昂贵的删除重复任务。我认识到在你的情况下这是不可能的,但它可能会让别人考虑他们的设计缺陷。由于这是第三方产品,因此我会要求他们修复该问题以仅允许一个主要地址。最终,如果有足够的人抱怨,他们可能会。

+0

感谢您的反馈我测试了您的加入,平均需要6秒钟:) – Simon

+0

我只添加了*以保持简单并专注于关键字段。即使这样,这里使用的表格和字段名称也不能反映真实的名称,如果你看到了我正在处理的内容,那么我担心的答案是关于约定而不是问题。 感谢您的时间和帮助。 – Simon

+0

我已经标记了这个答案,因为它提供了最快的性能提升。 我喜欢@ Mikael-Eriksson的答案以及它的语法如此简单,但它慢了几秒(在我的查询中)。 – Simon