复杂TSQL视图的最佳实践

时间:2013-08-01 15:08:20

标签: sql-server tsql

我正在为下面的报告编写一个复杂的视图:

SELECT     TOP (100) PERCENT dbo.tbl_SFCWRK_SQL_NF.ID, dbo.tbl_SFCWRK_SQL_NF.STD_LBR_HRS, dbo.tbl_SFCWRK_SQL_NF.CUSTNAME, 
                  dbo.tbl_SFCWRK_SQL_NF.JOBNBR, CASE WHEN dbo.tbl_SFCWRK_SQL_NF.COMPQTY IS NULL THEN 0 ELSE CONVERT(decimal(10, 5), 
                  dbo.tbl_SFCWRK_SQL_NF.COMPQTY) END AS COMPQTY, CASE WHEN dbo.tbl_SFCWRK_SQL_NF.ORDQTY IS NULL THEN 0 ELSE CONVERT(decimal(10, 5), 
                  dbo.tbl_SFCWRK_SQL_NF.ORDQTY) END AS ORDQTY, dbo.tbl_SFCWRK_SQL_NF.PLAN_CD, dbo.tbl_SFCWRK_SQL_NF.PLANQTY, 
                  dbo.tbl_SFCWRK_SQL_NF.ITEMNBR, dbo.tbl_ITEMMAST_NF.ITEM_NBR, dbo.tbl_SFCWRK_SQL_NF.WODUEDT, tbl_WORK_CENTR_NF.WRK_CENTER_DESC, 
                  tbl_WORK_CENTR_NF.LABOR_CAPACITY, dbo.tbl_SFCWRK_SQL_NF.STD_SETUP_HRS, dbo.tbl_WIPOPER_NF.STD_LBR_HOURS, 
                  dbo.tbl_WIPOPER_NF.SCHED_COMP_DATE, dbo.tbl_WIPOPER_NF.WORK_CENTER_NBR, dbo.tbl_WIPOPER_NF.OPER_STATUS, 
                  dbo.tbl_WIPOPER_NF.OPERATION_NBR, tbl_WORK_CENTR_NF.WAREHOUSE, 
                  CASE WHEN dbo.tbl_WIPOPER_NF.WORK_CENTER_NBR = '20005' THEN 1 WHEN dbo.tbl_WIPOPER_NF.WORK_CENTER_NBR = '20010' THEN 2 WHEN dbo.tbl_WIPOPER_NF.WORK_CENTER_NBR
                   = '20020' THEN 3 WHEN dbo.tbl_WIPOPER_NF.WORK_CENTER_NBR = '20030' THEN 4 WHEN dbo.tbl_WIPOPER_NF.WORK_CENTER_NBR = '20040' THEN 5 WHEN
                   dbo.tbl_WIPOPER_NF.WORK_CENTER_NBR = '20050' THEN 6 WHEN dbo.tbl_WIPOPER_NF.WORK_CENTER_NBR = '20110' THEN 7 ELSE dbo.tbl_WIPOPER_NF.WORK_CENTER_NBR
                   END AS WCOrder, DATEADD(dd, 7 - DATEPART(dw, dbo.tbl_WIPOPER_NF.SCHED_COMP_DATE), dbo.tbl_WIPOPER_NF.SCHED_COMP_DATE) AS WeekEnd, 
                  DATEPART(wk, dbo.tbl_WIPOPER_NF.SCHED_COMP_DATE) AS WkNum, 
                  CASE WHEN dbo.tbl_SFCWRK_SQL_NF.COMPQTY <> 0 THEN ((dbo.tbl_SFCWRK_SQL_NF.STD_LBR_HRS / dbo.tbl_SFCWRK_SQL_NF.ORDQTY) 
                  * (dbo.tbl_SFCWRK_SQL_NF.ORDQTY - dbo.tbl_SFCWRK_SQL_NF.COMPQTY)) 
                  * 2 ELSE ((dbo.tbl_SFCWRK_SQL_NF.STD_LBR_HRS / dbo.tbl_SFCWRK_SQL_NF.ORDQTY) * (dbo.tbl_SFCWRK_SQL_NF.ORDQTY)) * 2 END AS RmnLabor, 
                  CASE WHEN dbo.tbl_SFCWRK_SQL_NF.COMPQTY <> 0 THEN ((dbo.tbl_SFCWRK_SQL_NF.STD_SETUP_HRS / tbl_SFCWRK_SQL_NF.ORDQTY) 
                  * (dbo.tbl_SFCWRK_SQL_NF.ORDQTY - dbo.tbl_SFCWRK_SQL_NF.COMPQTY)) 
                  / 100 ELSE ((dbo.tbl_SFCWRK_SQL_NF.STD_SETUP_HRS / dbo.tbl_SFCWRK_SQL_NF.ORDQTY) * (dbo.tbl_SFCWRK_SQL_NF.ORDQTY)) 
                  / 100 END AS RmnSetup
FROM         dbo.tbl_ITEMMAST_NF INNER JOIN
                  dbo.tbl_SFCWRK_SQL_NF ON dbo.tbl_ITEMMAST_NF.CPN =  dbo.tbl_SFCWRK_SQL_NF.CPN_NO INNER JOIN
                  dbo.tbl_WORK_CENTR_NF AS tbl_WORK_CENTR_NF ON dbo.tbl_SFCWRK_SQL_NF.WC_NBR = tbl_WORK_CENTR_NF.ID INNER JOIN
                  dbo.tbl_WIPOPER_NF ON dbo.tbl_SFCWRK_SQL_NF.ID = dbo.tbl_WIPOPER_NF.ID
WHERE     (dbo.tbl_WIPOPER_NF.SCHED_COMP_DATE <= DATEADD(m, 2, GETDATE())) AND (dbo.tbl_WIPOPER_NF.OPER_STATUS <> 'C' OR
                  dbo.tbl_WIPOPER_NF.OPER_STATUS IS NULL OR
                  dbo.tbl_WIPOPER_NF.OPER_STATUS = '') AND (tbl_WORK_CENTR_NF.WAREHOUSE = '02') AND (dbo.tbl_WIPOPER_NF.WORK_CENTER_NBR IN ('20005', '20010', 
                  '20020', '20030', '20040', '20060', '20110')) AND (NOT (dbo.tbl_ITEMMAST_NF.ITEM_NBR LIKE 'A%')) AND (CASE WHEN tbl_SFCWRK_SQL_NF.ORDQTY IS NULL 
                  THEN 0 ELSE CONVERT(decimal(10, 5), tbl_SFCWRK_SQL_NF.ORDQTY) END <> 0) AND (CASE WHEN tbl_SFCWRK_SQL_NF.ORDQTY IS NULL 
                  THEN 0 ELSE CONVERT(decimal(10, 5), tbl_SFCWRK_SQL_NF.ORDQTY) END <> 0)
ORDER BY dbo.tbl_SFCWRK_SQL_NF.WODUEDT DESC

我想让这次运行更有效率。 我也很好奇,如果我正在做的是写View的正确方法,我是否应该重构它???

赖安

3 个答案:

答案 0 :(得分:2)

这对我来说没什么意义

where      
(CASE 
      WHEN tbl_SFCWRK_SQL_NF.ORDQTY IS NULL THEN 0 
      ELSE CONVERT(decimal(10, 5), tbl_SFCWRK_SQL_NF.ORDQTY) END 
       <> 0)

如果为null则返回false 但任何与null的比较都会返回false。

必须是转换为十进制才能成功的数字类型 使用任何数字类型&lt;&gt; 0是一样的。

非常确定可以缩减为

where tbl_SFCWRK_SQL_NF.ORDQTY <> 0

使用您的语法可以终止在该列上使用索引

另外&lt;&gt; 'c'或=''是多余的

看到tbl_SFCWRK_SQL_NF.ORDQTY是varchar的评论 那只是搞砸了。
如果任何varchar值不是十进制,则转换将失败 我只是假设它必须是某种类型的数字。

答案 1 :(得分:1)

首先,views上的最佳做法不包含ORDER BY。所以摆脱TOP (100) PERCENTORDER BY dbo.tbl_SFCWRK_SQL_NF.WODUEDT DESC语句。接下来格式化您的查询,以便它易于阅读。我在下面做了这个,但如果你有不同的格式,你更喜欢使用它。这一切都是为了让你和你的队友/最终替换它可读。

格式化后,我可以告诉您应该检查以下索引。

  • tbl_ITEMMAST_NF(CPN,ITEM_NBR)
  • tbl_SFCWRK_SQL_NF(CPN_NO,WC_NBR,ID,ORDQTY)
  • tbl_WIPOPER_NF(ID,SCHED_COMP_DATE,OPER_STATUS)

这些是基于您在JOINWHERE子句中使用的列的起始位置。它们可能不是最优的,但可能总比没有好。您还可以检查执行计划,看看优化器是否提出了建议。如果它确实检查了您现有的索引并查看它是否适合它们,如果确实如此,则添加它。如果没有,则可以修改您现有的一个索引以涵盖它所要求的内容。

例如,假设您创建了上面的索引,但优化程序需要以下索引。

tbl_WIPOPER_NF(ID, SCHED_COMP_DATE, OPER_STATUS) INCLUDE WORK_CENTER_NBR

不要只添加新索引。修改旧的列以包含INCLUDE ed列。或者优化器可能希望您更改索引中列的顺序。如果这是你使用该索引的唯一地方(因为我建议你添加它),然后修改它。不要添加新的。与您已有的索引相比,我的建议也是如此。

就查询本身而言。看起来很好。还有一些其他方法可以处理事情,例如使用ISNULL而不是CASE子句中的WHERE语句。但你拥有它的方式应该工作得很好。

SELECT --TOP (100) PERCENT 
    dbo.tbl_SFCWRK_SQL_NF.ID, dbo.tbl_SFCWRK_SQL_NF.STD_LBR_HRS, 
    dbo.tbl_SFCWRK_SQL_NF.CUSTNAME, dbo.tbl_SFCWRK_SQL_NF.JOBNBR, 
    CASE WHEN dbo.tbl_SFCWRK_SQL_NF.COMPQTY IS NULL THEN 0 
        ELSE CONVERT(decimal(10, 5), dbo.tbl_SFCWRK_SQL_NF.COMPQTY) END AS COMPQTY, 
    CASE WHEN dbo.tbl_SFCWRK_SQL_NF.ORDQTY IS NULL THEN 0 
        ELSE CONVERT(decimal(10, 5), dbo.tbl_SFCWRK_SQL_NF.ORDQTY) END AS ORDQTY, 
    dbo.tbl_SFCWRK_SQL_NF.PLAN_CD, dbo.tbl_SFCWRK_SQL_NF.PLANQTY, 
    dbo.tbl_SFCWRK_SQL_NF.ITEMNBR, dbo.tbl_ITEMMAST_NF.ITEM_NBR, 
    dbo.tbl_SFCWRK_SQL_NF.WODUEDT, tbl_WORK_CENTR_NF.WRK_CENTER_DESC, 
    tbl_WORK_CENTR_NF.LABOR_CAPACITY, dbo.tbl_SFCWRK_SQL_NF.STD_SETUP_HRS, 
    dbo.tbl_WIPOPER_NF.STD_LBR_HOURS, dbo.tbl_WIPOPER_NF.SCHED_COMP_DATE, 
    dbo.tbl_WIPOPER_NF.WORK_CENTER_NBR, dbo.tbl_WIPOPER_NF.OPER_STATUS, 
    dbo.tbl_WIPOPER_NF.OPERATION_NBR, tbl_WORK_CENTR_NF.WAREHOUSE, 
    CASE WHEN dbo.tbl_WIPOPER_NF.WORK_CENTER_NBR = '20005' THEN 1 
        WHEN dbo.tbl_WIPOPER_NF.WORK_CENTER_NBR = '20010' THEN 2 
        WHEN dbo.tbl_WIPOPER_NF.WORK_CENTER_NBR = '20020' THEN 3 
        WHEN dbo.tbl_WIPOPER_NF.WORK_CENTER_NBR = '20030' THEN 4 
        WHEN dbo.tbl_WIPOPER_NF.WORK_CENTER_NBR = '20040' THEN 5 
        WHEN dbo.tbl_WIPOPER_NF.WORK_CENTER_NBR = '20050' THEN 6 
        WHEN dbo.tbl_WIPOPER_NF.WORK_CENTER_NBR = '20110' THEN 7 
        ELSE dbo.tbl_WIPOPER_NF.WORK_CENTER_NBR END AS WCOrder, 
    DATEADD(dd, 7 - DATEPART(dw, dbo.tbl_WIPOPER_NF.SCHED_COMP_DATE), 
    dbo.tbl_WIPOPER_NF.SCHED_COMP_DATE) AS WeekEnd, DATEPART(wk, dbo.tbl_WIPOPER_NF.SCHED_COMP_DATE) AS WkNum, 
    CASE WHEN dbo.tbl_SFCWRK_SQL_NF.COMPQTY <> 0 
        THEN ((dbo.tbl_SFCWRK_SQL_NF.STD_LBR_HRS / dbo.tbl_SFCWRK_SQL_NF.ORDQTY) *
            (dbo.tbl_SFCWRK_SQL_NF.ORDQTY - dbo.tbl_SFCWRK_SQL_NF.COMPQTY)) * 2 
        ELSE ((dbo.tbl_SFCWRK_SQL_NF.STD_LBR_HRS / dbo.tbl_SFCWRK_SQL_NF.ORDQTY) * 
            (dbo.tbl_SFCWRK_SQL_NF.ORDQTY)) * 2 END AS RmnLabor, 
    CASE WHEN dbo.tbl_SFCWRK_SQL_NF.COMPQTY <> 0 
        THEN ((dbo.tbl_SFCWRK_SQL_NF.STD_SETUP_HRS / tbl_SFCWRK_SQL_NF.ORDQTY) * 
            (dbo.tbl_SFCWRK_SQL_NF.ORDQTY - dbo.tbl_SFCWRK_SQL_NF.COMPQTY)) / 100 
        ELSE ((dbo.tbl_SFCWRK_SQL_NF.STD_SETUP_HRS / dbo.tbl_SFCWRK_SQL_NF.ORDQTY) * 
            (dbo.tbl_SFCWRK_SQL_NF.ORDQTY)) / 100 END AS RmnSetup
FROM dbo.tbl_ITEMMAST_NF 
INNER JOIN dbo.tbl_SFCWRK_SQL_NF 
    ON dbo.tbl_ITEMMAST_NF.CPN =  dbo.tbl_SFCWRK_SQL_NF.CPN_NO 
INNER JOIN dbo.tbl_WORK_CENTR_NF AS tbl_WORK_CENTR_NF 
    ON dbo.tbl_SFCWRK_SQL_NF.WC_NBR = tbl_WORK_CENTR_NF.ID 
INNER JOIN dbo.tbl_WIPOPER_NF 
    ON dbo.tbl_SFCWRK_SQL_NF.ID = dbo.tbl_WIPOPER_NF.ID
WHERE (dbo.tbl_WIPOPER_NF.SCHED_COMP_DATE <= DATEADD(m, 2, GETDATE())) 
  AND (dbo.tbl_WIPOPER_NF.OPER_STATUS <> 'C' OR
        dbo.tbl_WIPOPER_NF.OPER_STATUS IS NULL OR
        dbo.tbl_WIPOPER_NF.OPER_STATUS = '') 
  AND (tbl_WORK_CENTR_NF.WAREHOUSE = '02') 
  AND (dbo.tbl_WIPOPER_NF.WORK_CENTER_NBR IN ('20005', '20010', '20020', '20030', 
                                                '20040', '20060', '20110')) 
  AND (NOT (dbo.tbl_ITEMMAST_NF.ITEM_NBR LIKE 'A%')) 
  AND (CASE WHEN tbl_SFCWRK_SQL_NF.ORDQTY IS NULL THEN 0 
        ELSE CONVERT(decimal(10, 5), tbl_SFCWRK_SQL_NF.ORDQTY) END <> 0) 
  AND (CASE WHEN tbl_SFCWRK_SQL_NF.ORDQTY IS NULL THEN 0 
        ELSE CONVERT(decimal(10, 5), tbl_SFCWRK_SQL_NF.ORDQTY) END <> 0)
--ORDER BY dbo.tbl_SFCWRK_SQL_NF.WODUEDT DESC

答案 2 :(得分:1)

我对查询做了一些小的更新和格式化,可能会有所帮助;使用IS NULL代替COALESCE语句检查CASE并清空字符串。请参阅下文了解详情。我不确定这些是否会影响性能,但你可以尝试一下。

请注意,您的上一个条件已列出两次。我不确定这是否是故意的,或者它是否会对性能产生影响。让我知道。如果没有,那么我将删除这个答案。

SELECT
    dbo.tbl_SFCWRK_SQL_NF.ID,
    dbo.tbl_SFCWRK_SQL_NF.STD_LBR_HRS,
    dbo.tbl_SFCWRK_SQL_NF.CUSTNAME,
    dbo.tbl_SFCWRK_SQL_NF.JOBNBR,
    --CASE WHEN dbo.tbl_SFCWRK_SQL_NF.COMPQTY IS NULL THEN 0 ELSE CONVERT(decimal(10,   5), dbo.tbl_SFCWRK_SQL_NF.COMPQTY) END AS COMPQTY,
    COALESCE(CONVERT(decimal(10, 5), dbo.tbl_SFCWRK_SQL_NF.COMPQTY), 0) AS COMPQTY
    --CASE WHEN dbo.tbl_SFCWRK_SQL_NF.ORDQTY IS NULL THEN 0 ELSE CONVERT(decimal(10,    5), dbo.tbl_SFCWRK_SQL_NF.ORDQTY) END AS ORDQTY,
    COALESCE(CONVERT(decimal(10, 5), dbo.tbl_SFCWRK_SQL_NF.ORDQTY), 0) AS ORDQTY
    dbo.tbl_SFCWRK_SQL_NF.PLAN_CD,
    dbo.tbl_SFCWRK_SQL_NF.PLANQTY,
    dbo.tbl_SFCWRK_SQL_NF.ITEMNBR,
    dbo.tbl_ITEMMAST_NF.ITEM_NBR,
    dbo.tbl_SFCWRK_SQL_NF.WODUEDT,
    tbl_WORK_CENTR_NF.WRK_CENTER_DESC,
    tbl_WORK_CENTR_NF.LABOR_CAPACITY,
    dbo.tbl_SFCWRK_SQL_NF.STD_SETUP_HRS,
    dbo.tbl_WIPOPER_NF.STD_LBR_HOURS,
    dbo.tbl_WIPOPER_NF.SCHED_COMP_DATE,
    dbo.tbl_WIPOPER_NF.WORK_CENTER_NBR,
    dbo.tbl_WIPOPER_NF.OPER_STATUS,
    dbo.tbl_WIPOPER_NF.OPERATION_NBR,
    tbl_WORK_CENTR_NF.WAREHOUSE,
    CASE
        WHEN
            dbo.tbl_WIPOPER_NF.WORK_CENTER_NBR = '20005'
        THEN
            1
        WHEN
            dbo.tbl_WIPOPER_NF.WORK_CENTER_NBR = '20010'
        THEN
            2
        WHEN
            dbo.tbl_WIPOPER_NF.WORK_CENTER_NBR  = '20020'
        THEN
            3
        WHEN
            dbo.tbl_WIPOPER_NF.WORK_CENTER_NBR = '20030'
        THEN
            4
        WHEN
            dbo.tbl_WIPOPER_NF.WORK_CENTER_NBR = '20040'
        THEN
            5
        WHEN
            dbo.tbl_WIPOPER_NF.WORK_CENTER_NBR = '20050'
        THEN
            6
        WHEN
            dbo.tbl_WIPOPER_NF.WORK_CENTER_NBR = '20110'
        THEN
            7
        ELSE
            dbo.tbl_WIPOPER_NF.WORK_CENTER_NBR
        END
            AS WCOrder,
    DATEADD(dd, 7 - DATEPART(dw, dbo.tbl_WIPOPER_NF.SCHED_COMP_DATE), dbo.tbl_WIPOPER_NF.SCHED_COMP_DATE) AS WeekEnd,
    DATEPART(wk, dbo.tbl_WIPOPER_NF.SCHED_COMP_DATE) AS WkNum,
    CASE
        WHEN
            dbo.tbl_SFCWRK_SQL_NF.COMPQTY <> 0
        THEN
            ((dbo.tbl_SFCWRK_SQL_NF.STD_LBR_HRS / dbo.tbl_SFCWRK_SQL_NF.ORDQTY) * (dbo.tbl_SFCWRK_SQL_NF.ORDQTY - dbo.tbl_SFCWRK_SQL_NF.COMPQTY)) * 2
        ELSE
            ((dbo.tbl_SFCWRK_SQL_NF.STD_LBR_HRS / dbo.tbl_SFCWRK_SQL_NF.ORDQTY) * (dbo.tbl_SFCWRK_SQL_NF.ORDQTY)) * 2
        END AS RmnLabor,
    CASE
        WHEN
            dbo.tbl_SFCWRK_SQL_NF.COMPQTY <> 0
        THEN
            ((dbo.tbl_SFCWRK_SQL_NF.STD_SETUP_HRS / tbl_SFCWRK_SQL_NF.ORDQTY) * (dbo.tbl_SFCWRK_SQL_NF.ORDQTY - dbo.tbl_SFCWRK_SQL_NF.COMPQTY)) / 100
        ELSE
            ((dbo.tbl_SFCWRK_SQL_NF.STD_SETUP_HRS / dbo.tbl_SFCWRK_SQL_NF.ORDQTY) * (dbo.tbl_SFCWRK_SQL_NF.ORDQTY)) / 100
        END AS RmnSetup
FROM
    dbo.tbl_ITEMMAST_NF INNER JOIN dbo.tbl_SFCWRK_SQL_NF
        ON
            dbo.tbl_ITEMMAST_NF.CPN = dbo.tbl_SFCWRK_SQL_NF.CPN_NO INNER JOIN dbo.tbl_WORK_CENTR_NF AS tbl_WORK_CENTR_NF
                ON
                    dbo.tbl_SFCWRK_SQL_NF.WC_NBR = tbl_WORK_CENTR_NF.ID INNER JOIN dbo.tbl_WIPOPER_NF
                        ON
                            dbo.tbl_SFCWRK_SQL_NF.ID = dbo.tbl_WIPOPER_NF.ID
WHERE
    dbo.tbl_WIPOPER_NF.SCHED_COMP_DATE <= DATEADD(m, 2, GETDATE())
        AND
    (
        dbo.tbl_WIPOPER_NF.OPER_STATUS <> 'C'
            OR
        --dbo.tbl_WIPOPER_NF.OPER_STATUS IS NULL OR dbo.tbl_WIPOPER_NF.OPER_STATUS = ''
        --Replace the above statement with the following...
        COALESCE(dbo.tbl_WIPOPER_NF.OPER_STATUS, N'') = N''
    )
        AND
    tbl_WORK_CENTR_NF.WAREHOUSE = '02'
        AND
    dbo.tbl_WIPOPER_NF.WORK_CENTER_NBR IN ('20005', '20010', '20020', '20030', '20040', '20060','20110')
        AND
    dbo.tbl_ITEMMAST_NF.ITEM_NBR NOT LIKE 'A%'
        AND
    --CASE WHEN tbl_SFCWRK_SQL_NF.ORDQTY IS NULL THEN 0 ELSE CONVERT(decimal(10, 5), tbl_SFCWRK_SQL_NF.ORDQTY) END <> 0
    --I would change the above statement to this...
    COALESCE(tbl_SFCWRK_SQL_NF.ORDQTY, 0) <> 0

        --AND
    --CASE WHEN tbl_SFCWRK_SQL_NF.ORDQTY IS NULL THEN 0 ELSE CONVERT(decimal(10, 5), tbl_SFCWRK_SQL_NF.ORDQTY) END <> 0
    --The above statement is a duplicate!
ORDER BY
    dbo.tbl_SFCWRK_SQL_NF.WODUEDT DESC