删除多次使用的select语句的子查询以优化性能

时间:2013-09-09 12:07:20

标签: sql sql-server tsql sql-server-2008-r2

我有以下查询, 此查询多次使用select子句中的子查询

SELECT DISTINCT CAST(decCostFactor as decimal(9,4)) 
          FROM tblsubteam WITH (NOLOCK) 
          WHERE intstore = st.intStore 
          AND strsubteam = SUBSTRING(tblDetail.strMiscText,12,4)

我想删除此子查询。我尝试使用group by CAST(decCostFactor as decimal(9,4)),但现在它要求在cluase中包含其他列。

非常感谢任何帮助。

主要查询是

DECLARE @Region int=10006

SELECT 
    st.strRegion,
    st.intStore,
    st.strStoreName,
    tblSticker.intStickerNo ,
    tblSticker.strTeamNo AS strTeam ,
    tblSticker.dtmFixtureStartDate AS dtmStartDate ,
    tblSticker.intAreaNo ,
    SUBSTRING ( tblSticker.strMiscText , 8 , 30 ) AS strSection ,
    tblDetail.intLineNum ,
    tblDetail.strBarcode ,
    tblDetail.intBarcodeLength ,
    tblDetail.intBarcodeType ,
    tblDetail.strBarcodeEntrySw AS strKeyBarcode ,
    tblDetail.fltPrice AS fltPrice ,
    tblDetail.fltQty AS fltQty ,
    tblDetail.strPTCCode4 AS strKeyPrice ,
    tblDetail.strPTCCode5 AS strKeyQty ,
    SUBSTRING ( tblDetail.strMiscText , 1 , 1 ) AS strNOF ,
    SUBSTRING ( tblDetail.strClientText , 1 , 30 ) AS strDesc ,
    SUBSTRING ( tblDetail.strMiscText , 12 , 4 ) AS strSubTeam ,
    SUBSTRING ( tblDetail.strMiscText , 16 , 25 ) AS strSubTeamDesc ,
    SUBSTRING ( tblDetail.strClientText , 34 , 3 ) AS strSubDept ,
    SUBSTRING ( tblDetail.strClientText , 37 , 3 ) AS strClass ,
    SUBSTRING ( tblDetail.strClientText , 40 , 3 ) AS strSubClass ,
    SUBSTRING ( tblDetail.strMiscText , 41 , 7 ) AS strCostFactor ,
    SUBSTRING ( tblDetail.strMiscText , 41 , 1 ) AS strPerishableFlag ,
    tblSubTeam.blnBreakoutCost,
    fltPriceLb = tblDetail.fltClientMisc1,
    fltCostLb = CASE 
      WHEN tblDetail.decCost > 0
          THEN tblDetail.decCost
          ELSE tblDetail.fltClientMisc1 *
        ( SELECT DISTINCT CAST(decCostFactor as decimal(6,4)) 
          FROM tblsubteam WITH (NOLOCK) 
          WHERE intstore = st.intStore 
          AND strsubteam = SUBSTRING(tblDetail.strMiscText,12,4) 
        )
          END,
      strVendor = CASE WHEN tblDetail.strVendor IS NULL THEN 'NA' ELSE tblDetail.strVendor END,

    fltWeightSouth = CASE 
      WHEN tblDetail.fltClientMisc1 > 0
        THEN (tblDetail.fltPrice / tblDetail.fltClientMisc1)
        WHEN SUBSTRING(tblDetail.strMiscText,49,1) <> 'R' 
      AND  tblDetail.fltClientMisc1 = 0 
      THEN cast(SUBSTRING(tblDetail.strClientText,43,6) as float)
        ELSE 0 
      END,

    fltCostSouth = ISNULL(CASE 
      WHEN SUBSTRING(tblDetail.strMiscText,50,1) = 'C' 
      THEN (tblDetail.fltPrice ) 
            ELSE
        -- To calculate cost for Subteam 3100 in the south region -  Thomas - 2/19/2008
                CASE 
        WHEN (SUBSTRING(tblDetail.strMiscText,12,4) = '3100') 
        AND (Substring(tblDetail.strMiscText,1,1) <> 'N')
                THEN fltprice  * 
          ( SELECT DISTINCT CAST(decCostFactor as decimal(6,4)) 
            FROM tblsubteam WITH (NOLOCK) 
            WHERE intstore = st.intStore 
            AND strsubteam = SUBSTRING(tblDetail.strMiscText, 12, 4)
          )
                ELSE CASE 
          WHEN tblDetail.fltClientMisc1 > 0 and tblDetail.decCost > 0
                  -- modified by TA to remedy Extended Retail for weighted items
                  THEN (tblDetail.fltPrice / tblDetail.fltClientMisc1) * tblDetail.decCost 
                  ELSE CASE
                        WHEN (Substring(tblDetail.strMiscText,1,1) = 'N')  
            Or   (tblDetail.strBarcode = '00000000000000')
                        THEN CASE 
              WHEN (fltprice = 0 and tblDetail.decCost > 0) 
              THEN CASE 
                WHEN SUBSTRING(tblDetail.strClientText,43,6) <> '' 
                THEN CASE 
                  WHEN CAST(SUBSTRING(tblDetail.strClientText,43,6) as float) > 0 
                                    THEN CAST(SUBSTRING(tblDetail.strClientText,43,6) as float)*tblDetail.decCost 
                                    ELSE fltTotalUnits*tblDetail.decCost
                                  END
                                ELSE 0 * tblDetail.decCost
                                END
                            ELSE CASE 
                WHEN (Substring(tblDetail.strMiscText,41,7)) = ''
                                THEN fltprice * 0 
                                ELSE fltprice  * Cast(Substring(tblDetail.strMiscText,41,7) as decimal(6,4)) 
                                END
                            END
                        ELSE CASE 
              WHEN (fltprice > 0 and tblDetail.decCost = 0) 
                            THEN  fltprice  * 
                ( SELECT DISTINCT CAST(decCostFactor as decimal(6,4)) 
                  FROM tblsubteam WITH (NOLOCK) 
                  WHERE intstore = st.intStore 
                  AND strsubteam = SUBSTRING(tblDetail.strMiscText,12,4)
                )
                            ELSE CASE 
                WHEN (fltprice = 0 and tblDetail.decCost > 0) 
                THEN CASE 
                  WHEN CAST(SUBSTRING(tblDetail.strClientText,43,6) as float) > 0 
                                    THEN CAST(SUBSTRING(tblDetail.strClientText,43,6) as float) * tblDetail.decCost 
                                    ELSE tblDetail.decCost 
                                    END
                              ELSE tblDetail.decCost 
                                END
                            END
            END
          END
        END
      END,0),

  fltItemCost = ISNULL(CASE
    WHEN ((((Substring(tblDetail.strMiscText,41,7) = '00.0000') 
            or (tblDetail.fltClientMisc1 = 0)) 
            or (tblDetail.decCost = 0)) 
            and Substring(tblDetail.strMiscText,1,1) <> 'N') 
        THEN CASE 
      WHEN (fltprice > 0 and tblDetail.decCost = 0) 
            THEN  fltprice * 
        ( SELECT DISTINCT CAST(decCostFactor as decimal(9,4)) 
          FROM tblsubteam WITH (NOLOCK) 
          WHERE intstore = st.intStore 
          AND strsubteam = SUBSTRING(tblDetail.strMiscText,12,4)
        )   
            ELSE CASE 
        WHEN (fltprice = 0 and tblDetail.decCost > 0) 
                THEN tblDetail.decCost
                ELSE CASE 
          WHEN  (Substring(tblDetail.strMiscText,41,7)) = ''
                    THEN  fltprice * 0
                    ELSE fltprice  * Cast(Substring(tblDetail.strMiscText,41,7) as decimal(9,4)) 
                    END
                END
            END
            ELSE CASE 
        WHEN (Substring(tblDetail.strMiscText,1,1) = 'N') 
        Or   (tblDetail.strBarcode = '00000000000000')
                THEN fltprice * Cast(Substring(tblDetail.strMiscText,41,7) as decimal(9,4)) 
                ELSE CASE 
          WHEN (fltprice > 0 and tblDetail.decCost = 0) 
                    THEN  fltprice  * 
            ( SELECT DISTINCT CAST(decCostFactor as decimal(9,4)) 
              FROM tblsubteam WITH (NOLOCK) 
              WHERE intstore = st.intStore 
              AND strsubteam = SUBSTRING(tblDetail.strMiscText,12,4)
            )   
                    ELSE CASE 
            WHEN (Substring(tblDetail.strMiscText,41,7)) = ''
                        THEN fltprice * 0
                        ELSE fltprice  * Cast(Substring(tblDetail.strMiscText,41,7) as decimal(9,4)) 
                      END
                    END
                END
      END, 0),
      strWeight = SUBSTRING(tblDetail.strClientText, 43, 6)

FROM
tblSticker WITH ( NOLOCK )
INNER JOIN tblDetail WITH ( NOLOCK )
ON tblSticker.intStore = tblDetail.intStore
   AND tblSticker.intStickerNo = tblDetail.intStickerNo
   AND tblSticker.dtmStickerDate = tblDetail.dtmStickerDate
LEFT OUTER JOIN tblsubteam
ON tblDetail.intStore = tblSubTeam.intStore
   AND SUBSTRING ( tblDetail.strMiscText , 12 , 4 ) = tblSubTeam.strSubTeam
inner join tblStore st on st.intStore=tblDetail.intStore
--fix add join to store.  Kevin 2/8/07

-- AND SUBSTRING ( tblDetail.strMiscText , 12 , 4 ) = tblSubTeam.strSubTeam
--fix fix move join to store outside of compound conditional.  Eddie 9/2/11
WHERE
   ((@Region is not null and st.intRegion=@Region) or @Region is  null)
    AND st.intStore < 90000
    AND ( tblSticker.intStickerNo NOT BETWEEN 334717100
          AND 334717299 )
    AND tblSticker.strRescanSW = 'N'
    AND ( tblSticker.strEmptyStatus = ' '
          OR tblSticker.strEmptyStatus = '*' )
ORDER BY st.strStoreName,
    tblSticker.intStickerNo ,
    tblDetail.intLineNum

2 个答案:

答案 0 :(得分:4)

您可以将查询移至APPLY,这可确保只执行一次。

我认为你的例子会变成:

FROM    tblSticker WITH ( NOLOCK )
        INNER JOIN tblDetail WITH ( NOLOCK )
            ON tblSticker.intStore = tblDetail.intStore
            AND tblSticker.intStickerNo = tblDetail.intStickerNo
            AND tblSticker.dtmStickerDate = tblDetail.dtmStickerDate
        LEFT OUTER JOIN tblsubteam
            ON tblDetail.intStore = tblSubTeam.intStore
            AND SUBSTRING ( tblDetail.strMiscText , 12 , 4 ) = tblSubTeam.strSubTeam
        INNER JOIN tblStore st 
            ON st.intStore=tblDetail.intStore
        OUTER APPLY
        (   SELECT  DeCostFactor = CAST(decCostFactor AS DECIMAL(9,4)) 
            FROM    tblsubteam WITH (NOLOCK) 
            WHERE   intstore = st.intStore 
            AND     strsubteam = SUBSTRING(tblDetail.strMiscText,12,4)
        ) dcf

然后,您可以简单地引用dcf.DeCostFactor

,而不是您的子查询

作为一般测试,我使用了以下内容,与单个APPLY相比,它只执行相关子查询4次

SET STATISTICS IO ON;

WITH T AS
(   SELECT  A = Number, B = Number + 1
    FROM    Master..spt_values
    WHERE   Type = 'P'
)
SELECT  T.A,
        T.B,
        SubQuery = (SELECT T2.B FROM T T2 WHERE T2.A = T.B),
        SubQuery2 = (SELECT T2.B FROM T T2 WHERE T2.A = T.B),
        SubQuery3 = (SELECT T2.B FROM T T2 WHERE T2.A = T.B),
        SubQuery4 =  (SELECT T2.B FROM T T2 WHERE T2.A = T.B)
FROM    T;


-- USING OUTER APPLY
WITH T AS
(   SELECT  A = Number, B = Number + 1
    FROM    Master..spt_values
    WHERE   Type = 'P'
)
SELECT  T.A,
        T.B,
        SubQuery = SubQuery.B,
        SubQuery2 = SubQuery.B,
        SubQuery3 = SubQuery.B,
        SubQuery4 = SubQuery.B
FROM    T
        OUTER APPLY
        (   SELECT  T2.B
            FROM    T T2
            WHERE   T2.A = T.B
        ) SubQuery;

IO统计数据不言自明:

多个子查询

Table 'spt_values'. Scan count 8193, logical reads 24625, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

外部申请

Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'spt_values'. Scan count 2, logical reads 18, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

修改

我认为你根本不需要子查询,你已经按照相同的标准加入了表tblSubteam

LEFT OUTER JOIN tblsubteam
    ON tblDetail.intStore = tblSubTeam.intStore
    AND SUBSTRING ( tblDetail.strMiscText , 12 , 4 ) = tblSubTeam.strSubTeam

为什么不能只使用tblsubteam.DeCostFactor而不是子查询?

答案 1 :(得分:1)

您将此作为select子句中的子查询。因此,它必须返回一个值。因此,您可以将其替换为:

    ( SELECT top 1 CAST(decCostFactor as decimal(6,4)) 
      FROM tblsubteam WITH (NOLOCK) 
      WHERE intstore = st.intStore 
      AND strsubteam = SUBSTRING(tblDetail.strMiscText,12,4) 
    )

SQL Server非常适合优化。但是,distinct可能会造成问题。删除distinct并且只取任意值可能会改善优化。

但是,检查的方法是查看执行计划。

通过在tblsubteam(instore, strsubteam, decCostFactor)上建立索引,可能会提高效果。