优化SQL,CASE中同一行多次

Posted

技术标签:

【中文标题】优化SQL,CASE中同一行多次【英文标题】:Optimize SQL, same line multiple times in CASE 【发布时间】:2016-11-29 13:37:36 【问题描述】:

我编写了一个 SQL Servre 存储过程 (SQL Server 2014),它查看某个表中的某个列,如果结果为 null,我想从其他列中获取值。

这行我写了两次:

(Select t_EC 
 from TTCIB 
 where rtrim(ltrim(t_item)) = rtrim(ltrim(t1.t_item)) 
   and t_comp = '50') 

检查一次是否为空。

如果它不为空,则第二次将值实际添加到我的结果中。

有没有办法优化这个?这样系统就不需要运行这条线两次了? (它有两次 ltrim 函数和两次 rtrim 函数,所以很有用)

SELECT DISTINCT TOP 15 
    (SELECT t_EC 
     FROM TTCIB 
     WHERE rtrim(ltrim(t_item)) = rtrim(ltrim(t1.t_item)) 
       AND t_comp = '18') as EC18,
    CASE 
       WHEN (SELECT t_EC FROM TTCIB 
             WHERE rtrim(ltrim(t_item)) = rtrim(ltrim(t1.t_item)) 
               AND t_comp = '50') IS NULL
          THEN (SELECT t_EC 
                FROM TTCIB 
                WHERE rtrim(ltrim(t_item)) = rtrim(ltrim(t1.t_item)) 
                  AND t_comp = '51')
          ELSE (SELECT t_EC 
                FROM TTCIB 
                WHERE rtrim(ltrim(t_item)) = rtrim(ltrim(t1.t_item)) 
                  AND t_comp = '50') 
    END AS EC50,
    RTRIM(t1.t_sern) AS SerialNumber, t1.t_item AS Item, 
    RTRIM(t1.t_desc) AS ItemDesc, t1.t_ofbp AS SoldToBP,    
    RTRIM(t2.t_nama) AS BPName, t1.t_cwte AS Warranty, 
    t1.t_dltm AS DeliveryTime, t_optm AS InstallationTime,   
    t_clst AS Cluster, ISNULL(t3.t_endt,'1970-01-01') AS EndDate,
    t4.t_csig as ItemSignalCode, t5.t_dsca as ItemSignalCodeDesc  
FROM 
    FnGetSerialNos(@SerialNumber, @MasterCompany) t1  
INNER JOIN 
    ttccom100900 t2 ON t1.t_ofbp = t2.t_bpid
LEFT OUTER JOIN 
    ttsctm120900 t3 ON t1.t_term = t3.t_term
LEFT OUTER JOIN 
    ttcibd001900 t4 ON ltrim(t1.t_item) = ltrim(t4.t_item)
LEFT OUTER JOIN 
    ttcmcs018900 t5 ON t4.t_csig = t5.t_csig

【问题讨论】:

标记您正在使用的 dbms。 (那里有一些非 ANSI SQL。) 不相关的子查询...您的查询可能会被完全重写。添加一些示例表数据和预期结果 - 以及格式化文本。 查询的其余部分在哪里? 显示正确的数据样本和预期的结果.. 能否也请您发布创建表脚本,它会告诉我们这些列也属于哪个表 【参考方案1】:

使用 INNER JOIN 从 TTCIB 读取所有相关值,并使用 COALESCE() 函数而不是 CASE WHEN ... END

SELECT DISTINCT TOP 15 
         x.t_18 as EC18
        ,COALESCE(x.t_50, x.t_51) AS EC50
        ,RTRIM(t1.t_sern) AS SerialNumber
        ,t1.t_item AS Item
        ,RTRIM(t1.t_desc) AS ItemDesc
        ,t1.t_ofbp AS SoldToBP
        ,RTRIM(t2.t_nama) AS BPName
        ,t1.t_cwte AS Warranty
        ,t1.t_dltm AS  DeliveryTime
        ,t_optm AS InstallationTime
        ,t_clst AS Cluster
        , ISNULL(t3.t_endt,'1970-01-01') AS EndDate
        ,t4.t_csig as ItemSignalCode
        , t5.t_dsca as ItemSignalCodeDesc  
FROM FnGetSerialNos(@SerialNumber, @MasterCompany) t1
    INNER JOIN (
        SELECT   RTRIM(LTRIM(t_EC)) AS t_EC
                ,MAX(CASE WHEN t_comp = '18' THEN t_EC END) AS t_18
                ,MAX(CASE WHEN t_comp = '50' THEN t_EC END) AS t_50
                ,MAX(CASE WHEN t_comp = '51' THEN t_EC END) AS t_51
        FROM TTCIB
        GROUP BY t_EC
    ) x
        ON RTRIM(LTRIM(t1.t_item)) = x.t_EC
    INNER JOIN ttccom100900 t2  
        ON t1.t_ofbp = t2.t_bpid
    LEFT OUTER JOIN ttsctm120900 t3  
        ON t1.t_term = t3.t_term
    Left Outer JOIN ttcibd001900 t4  
        ON ltrim(t1.t_item) = ltrim(t4.t_item)
    Left Outer JOIN ttcmcs018900 t5  
        ON t4.t_csig = t5.t_csig
;

【讨论】:

【参考方案2】:

更快(适用​​于各种数据库)

coalesce((Select t_EC from TTCIB where rtrim(ltrim(t_item)) = rtrim(ltrim(t1.t_item)) and t_comp = '50'),(Select t_EC from TTCIB where rtrim(ltrim(t_item)) = rtrim(ltrim(t1.t_item)) and t_comp = '51')) as EC50

最快(只针对sqlserver,oracle用这种方法比较复杂)

(Select top 1 t_EC from TTCIB where rtrim(ltrim(t_item)) = rtrim(ltrim(t1.t_item)) and t_comp in ('50','51') order by t_comp asc) as EC50

【讨论】:

以上是关于优化SQL,CASE中同一行多次的主要内容,如果未能解决你的问题,请参考以下文章

SQL优化-子查询&case&limit

MySQL--SQL优化案例

SQL里if语句和case语句有啥区别吗?哪个使用更高效?就是查询更优化?

sql查询上一行优化

MySQL单机优化---SQL优化

WebSQL 查询优化帮助 - CASE WHEN 使查询慢 400 毫秒