【问题标题】:Query with many CASE statements - optimization使用许多 CASE 语句进行查询 - 优化
【发布时间】:2011-05-30 10:10:11
【问题描述】:

我有一个非常脏的查询,肯定可以优化,因为其中有很多 CASE 语句!

SELECT 
    (CASE pa.KplusTable_Id WHEN 1 THEN sp.sp_id 
          WHEN 2 THEN fw.fw_id
          WHEN 3 THEN s.sw_Id
          WHEN 4 THEN id.ia_id END) as Deal_Id,
max(CASE pa.KplusTable_Id WHEN 1 THEN sp.Trans_Id 
                          WHEN 2 THEN fw.Trans_Id
                          WHEN 3 THEN s.Trans_Id
                          WHEN 4 THEN id.Trans_Id END) as TransId_CurrentMax
INTO #MaxRazlicitOdNull
FROM #PotencijalniAktuelni pa LEFT JOIN kplus_sp sp (nolock) on sp.sp_id=pa.Deal_Id AND pa.KplusTable_Id=1
    LEFT JOIN kplus_fw fw (nolock) on fw.fw_id=pa.Deal_Id AND pa.KplusTable_Id=2        
    LEFT JOIN dev_sw s (nolock) on s.sw_Id=pa.Deal_Id AND pa.KplusTable_Id=3
    LEFT JOIN kplus_ia id (nolock) on id.ia_id=pa.Deal_Id AND pa.KplusTable_Id=4
WHERE isnull(CASE pa.KplusTable_Id WHEN 1 THEN sp.BROJ_TIKETA 
                                   WHEN 2 THEN fw.BROJ_TIKETA
                                   WHEN 3 THEN s.tiket
                                   WHEN 4 THEN id.BROJ_TIKETA END, '')<>'' 
GROUP BY CASE pa.KplusTable_Id WHEN 1 THEN sp.sp_id 
                               WHEN 2 THEN fw.fw_id
                               WHEN 3 THEN s.sw_Id
                               WHEN 4 THEN id.ia_id END

因为我有几次相同的条件,你知道如何优化查询,让它更简单更好。欢迎所有建议!

提前TnX!

内曼加

【问题讨论】:

  • 只有四个简单的 SELECT 语句,KplusTable_Id 的每个 case 一个,然后将它们联合在一起不是更容易吗?这样一来,您就可以忘记所有 CASE 的东西......
  • 我会尝试使用 UNION。您是否有其他想法可以在这些情况下使用?看起来很常见的问题可能有一些已知的模式。
  • 看起来这是对超类型/子类型结构的尝试,其中 pa.KplusTable_Id 是类型鉴别器。你能确认sp, fw, s, idpa 的子类型吗?比如pa.Deal_Id = 1 and sp.sp_id = 1,那么在fw, s, id中是否有与_id = 1的记录?
  • @Damir Sudarevic - 在这种情况下,可能有 fw_id=1、s_id=1 和 ia_id=1,但不会是相同的交易。 Deal 是唯一的,具体取决于 Deal_Id 和放置它的表(在这种情况下也可以是 KplusTable_Id)。我相信你理解你的问题,所以给出了适当的答案......

标签: sql sql-server tsql query-optimization case-statement


【解决方案1】:

这些箱子对我来说没问题。通常比 Union 更快。 将查询的几个变体放在计划中并比较批次。 只有一个(无关紧要的)细节 改变

WHERE isnull(CASE pa.KplusTable_Id WHEN 1 THEN sp.BROJ_TIKETA 
                                   WHEN 2 THEN fw.BROJ_TIKETA
                                   WHEN 3 THEN s.tiket
                                   WHEN 4 THEN id.BROJ_TIKETA END, '')<>'' 

为此

WHERE CASE pa.KplusTable_Id WHEN 1 THEN sp.BROJ_TIKETA 
                                   WHEN 2 THEN fw.BROJ_TIKETA
                                   WHEN 3 THEN s.tiket
                                   WHEN 4 THEN id.BROJ_TIKETA END is not null

另一个 sol(使用 UNION):

SELECT  pa.Deal_Id, MAX(Q.Trans_Id) AS TransId_CurrentMax
INTO #MaxRazlicitOdNull
FROM 
(SELECT 1 A KplusTable_Id, Trans_Id, sp_id AS Deal_Id  FROM kplus_sp
UNION 
SELECT 2 AS KplusTable_Id, Trans_Id, fw_id AS Deal_Id FROM  kplus_fw  
UNION 
SELECT 3 AS KplusTable_Id, Trans_Id, sw_Id AS Deal_Id FROM  dev_sw
UNION 
SELECT 4 AS KplusTable_Id, Trans_Id, ia_id AS Deal_Id FROM kplus_ia) AS Q 
 INNER  JOIN #PotencijalniAktuelni pa ON pa.KplusTable_Id=Q.KplusTable_Id AND pa.Deal_Id=Q.Deal_Id
 GROUP BY pa.Deal_Id

测试计划中的每个查询变体并更快地选择

【讨论】:

  • 我看到我公司的做法是使用 isnull(something, '')''。一开始这对我来说有点奇怪,但后来我接受了。你知道'is not null'语句之间有什么重要区别吗?
  • 重要的区别?取决于你的样子。一个区别是:比较与评估+比较+比较。 SQLServer 通常但并非总是对此进行优化。在这种情况下,当您需要知道行是否为空时,我更喜欢空值来寻找替代品。替换可以生成类型错误或使用现有值。关于联合。由于多次扫描同一个表,联合几乎总是会变慢。联合看起来像画家的算法。但很容易检查:将两个变体放在一起并询问计划。工会始终超过 50% 的成本
  • 今天我比较了这两种解决方案(UNION vs LEFT JOIN-s),并没有发现执行时间有什么大的差异。原因可能是包含此代码的存储过程部分仅占执行计划时间的 2%,因此,差异不会很明显。
【解决方案2】:

对我来说,这看起来像是子类型的拙劣尝试。这就是我认为你现在拥有的。

根据模型,以下应该可以工作:

;
with
q_00 as (
    select
         pa.Deal_Id                                                             as Deal_Id
       , coalesce(sp.BROJ_TIKETA, fw.BROJ_TIKETA, sw.tiket, ia.BROJ_TIKETA, '') as Ticket_No
       , coalesce(sp.Trans_Id, fw.Trans_Id, sw.Trans_Id, ia.Trans_Id)           as Trans_Id
    from #PotencijalniAktuelni as pa
    left join kplus_sp         as sp on sp.sp_Id = pa.Deal_Id and pa.KplusTable_Id = 1
    left join kplus_fw         as fw on fw.fw_Id = pa.Deal_Id and pa.KplusTable_Id = 2        
    left join dev_sw           as sw on sw.sw_Id = pa.Deal_Id and pa.KplusTable_Id = 3
    left join kplus_ia         as ia on ia.ia_Id = pa.Deal_Id and pa.KplusTable_Id = 4
)
select
      Deal_Id
    , max(Trans_Id) as TransId_CurrentMax
into #MaxRazlicitOdNull
from  q_00
where Ticket_No <> ''
group by Deal_Id ;

SQL Server 2005 +

【讨论】:

  • 正如 renegm 所说(我同时有一些读数),UNION 可能是非常“昂贵”的操作(特别是当有大量数据的表时),所以我相信这个解决方案与 LEFT JOIN-s 和 COALESCE 非常好。谢谢达米尔!
【解决方案3】:

最快的查询可能是将 4 个子句中的每一个联合起来,然后将它们联合在一起。代码结束时更长,但更清楚每个块的作用 - 特别是如果您将它们一起注释。

-- When KplusTable_Id  = 1
Select 
 sp.sp_id as as Deal_Id,
max(sp.Trans_Id) as TransId_CurrentMax
FROM #PotencijalniAktuelni pa LEFT JOIN kplus_sp sp (nolock) on sp.sp_id=pa.Deal_Id AND pa.KplusTable_Id=1
    LEFT JOIN kplus_fw fw (nolock) on fw.fw_id=pa.Deal_Id AND pa.KplusTable_Id=2        
    LEFT JOIN dev_sw s (nolock) on s.sw_Id=pa.Deal_Id AND pa.KplusTable_Id=3
    LEFT JOIN kplus_ia id (nolock) on id.ia_id=pa.Deal_Id AND pa.KplusTable_Id=4
WHERE sp.BROJ_TIKETA <>'' 
and pa.KplusTable_Id = 1
GROUP BY  sp.sp_id 

Union ...

-- When 2

将整个查询包装在一个选择中以插入到 #MaxRazlicitOdNull 中

【讨论】:

  • 实际上,您可能还可以将 LEFT JOIN 的数量减少到“符合”您当前标准的那个(KPlusTable_Id = 1 --> LEFT JOIN kplus_sp - 所有其他在这里都不相关)
  • @Doc Brown - 所有 kplus 表(包括 dev_sw)都具有相似的结构(可能无法提供有关表的所有详细信息,因此我不会浪费您的时间),数据正在存储(选择适当的表格)取决于交易类型。临时表 #PotencijalniAktuelni 存储先前过滤的数据,包括 Deal_Id 以及表数(将在 CASE 语句中使用),以及在此示例中不重要的其他几列。请让我知道还有哪些有用的信息,我会尽力提供。
猜你喜欢
  • 2021-06-02
  • 2017-03-13
  • 1970-01-01
  • 2011-02-14
  • 1970-01-01
  • 2012-05-10
  • 2016-04-10
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多