如何优化这个永远需要的 sql 查询?

Posted

技术标签:

【中文标题】如何优化这个永远需要的 sql 查询?【英文标题】:How do I optimize this sql query that takes forever? 【发布时间】:2015-03-12 15:41:07 【问题描述】:

我正在尝试优化此查询,因为它需要 30 秒才能执行:

SELECT
   TOP 100 table1.* 
FROM
   table1 (NOLOCK)     
INNER JOIN
   DB1..table2 (NOLOCK) 
      ON DB1..table2.id = DB1..table1.id    
INNER JOIN
   DB1..table2_batch (NOLOCK) 
      ON DB1..table2_batch.table2_batch_id = DB1..table2.table2_batch_id    
INNER JOIN
   DB2..table4 (NOLOCK) 
      ON CASE 
         WHEN CHARINDEX(':',
      table2_batch.reference_number,
      3) > 3 THEN SUBSTRING(table2_batch.reference_number,
      3,
      CHARINDEX(':',
      table2_batch.reference_number,
      3) -3 ) 
      ELSE 
   RIGHT(table2_batch.reference_number,
   LEN(table2_batch.reference_number) -2) 
end = Cast(table4.PurchaseOrderID as int)    INNER JOIN
   DB2..table3 (NOLOCK) 
      ON  DB2..table3.key =  DB2..table4.key 
WHERE
   table1.id IS NOT NULL  
   AND (
      table1.id!='' 
      OR table1.id IS NULL
   ) 
   AND   DB2..table3.AccountTypeID != 30000 
   AND CHARINDEX('O', DB1..table2_batch.reference_number) = 1 
   AND  table1_id NOT IN (
      select
         lim.table1_id 
      from
         link_table1_message as lim (nolock) 
      inner join
         table1 as i (nolock)  
            on i.table1_id = lim.table1_id 
      where
         lim.message_id >= 90
   ) 
ORDER BY
   last_hit DESC

【问题讨论】:

这个问题不是更适合代码审查吗? 瓶颈在 DB2..table4 连接上。当我取出那部分时,它的查询速度非常快......但我真的需要在那里加入以确保满足这种情况的适当条件。 问题本身有更多的上下文,这确实很适合Code Review。 ON CASE WHEN... joins 也不太好闻。你试过 CTE 吗? @usr 什么时候有一个case when 和一个无用的cast 曾经对on 子句中的性能有益? @Mat'sMug 谁知道加入是否重要?连接的表可能包含零行。 99.9999% 的时间可以花在其他地方。 没有人知道任何事情。 我已经调整了 100 多个查询,但我不知道这里有什么慢的地方。 你永远无法从查询中判断出来。 【参考方案1】:

基于this comment:

瓶颈在于 DB2..table4 连接。当我拿出那部分时,它的查询速度非常快......但我真的需要在那里加入以确保满足这种情况的适当条件。

这有点猜测,因为我们不知道涉及多少行,也不知道您的架构是什么样的,但是做一些假设,这可能会有所帮助:

INNER JOIN
   DB2..table4 (NOLOCK) 
      ON CASE 
         WHEN CHARINDEX(':',
      table2_batch.reference_number,
      3) > 3 THEN SUBSTRING(table2_batch.reference_number,
      3,
      CHARINDEX(':',
      table2_batch.reference_number,
      3) -3 ) 
      ELSE 
   RIGHT(table2_batch.reference_number,
   LEN(table2_batch.reference_number) -2) 
end = Cast(table4.PurchaseOrderID as int)

我找到了join 的很多逻辑。

你有一个table2_batch,它有一个reference_number,它正在编码一个你最终需要提取并用于与table4.PurchaseOrderID连接的值;我会先选择它:

with cteTable2 as (
    select
        table2_batch_id
       ,reference_number
       ,case when charindex(':',reference_number,3) > 3 
             then substring(reference_number,3,charindex(':',reference_number,3) - 3)
             else right(reference_number,len(reference_number) - 2)
        end PurchaseOrderID
    from table2_batch
)

然后您可以在随后的 select 语句中将 cteTable2 视为一个完整的表,因此您可以使用 cteTable2 而不是加入 table2_batch

inner join DB2..table4 (NOLOCK)
    on cteTable2.PurchaseOrderID = cast(table4.PurchaseOrderID as int)

...cast 真的需要吗?

【讨论】:

以上是关于如何优化这个永远需要的 sql 查询?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用交叉连接优化 SQL 查询

如何优化这个嵌套的 SQL 查询

我如何优化此查询以用于计算响应的 sql

如何优化这个 sql 查询(内连接)

您如何优化这个复杂的 sql 查询,然后选择正确的表索引

我应该如何优化这个 SQL 查询?