如何提高使用OUTER APPLY的查询的性能

时间:2017-08-08 11:06:41

标签: sql sql-server tsql

为了获得实体的“描述”列,我需要连接多个表。内部连接在一开始就足够了,但现在需求已经改变了,我需要从包含“描述”列的表中获取多行,所以我创建了一个表值函数。

问题是查询执行得更糟。使用内部连接时,需要1秒钟,现在需要30秒。

如何优化效果?

我知道使用外部应用会返回比内部连接更多的行。

我尝试在AGREEMENT_LINE表上添加AGR_LINE_NO和Meta_IsCurrent作为聚簇索引,但这只会提高原始查询的性能。

以下是原始查询,新查询,功能和执行计划。

原始查询:

SELECT TOP 10000 
OBJECT_TYPE,
SEQ_NO,
O.AGR_LINE_NO,
SHORT_DESC,
C02,
C03,
C04,
C05,
C07,
C10,
C52,
N05,
N04,
N02,
N19,
N01,
X.[Description]     AS CarConcept,
O.[Timestamp]       AS OBJ_TIMESTAMP,
O.Record_Timestamp  AS OBJ_RECORD_TIMESTAMP,
X.RECORD_TIMESTAMP  AS X_RECORD_TIMESTAMP

  FROM [Archive].[TIA_TIA_OBJECT] O

  INNER JOIN [Archive].[TIA_TIA_AGREEMENT_LINE] A   
  ON A.AGR_LINE_NO = O.Agr_Line_No

  INNER JOIN [Archive].[TIA_TIA_PRODUCT_LINE] PL 
  ON PL.PRODUCT_LINE_ID = A.PRODUCT_LINE_ID AND PL.PRODUCT_LINE_VER_NO = A.PRODUCT_LINE_VER_NO

  INNER JOIN [Archive].[TIA_TIA_TARIFF_STRUCTURE] TS 
  ON PL.TARIFF_TYPE_LIST_VER = TS.[VERSION] AND PL.PRODUCT_LINE_ID = TS.PRODUCT_LINE_ID

  INNER JOIN [Archive].[TIA_TIA_TARIFF_CODES] TC 
  ON TC.PRODUCT_LINE_ID = PL.PRODUCT_LINE_ID AND TC.[TYPE] = TS.[TYPE] AND TC.[VERSION] = TS.TYPE_VERSION  

  INNER JOIN [Archive].[TIA_TIA_XLA_PE_REFERENCE] X
  ON TC.[TYPE] = X.Table_Name AND PL.PRODUCT_LINE_ID = X.ID AND TC.[VERSION] = X.[VERSION] AND TC.CODE = X.[CODE]

  WHERE O.OBJECT_TYPE = 'BIO01'

  AND 
  TC.[TYPE] =    
  CASE WHEN O.C52 IS NOT NULL 
      THEN 'XTARIFTYPE'
      ELSE  'ART'
  END

  AND X.[Language] = 'DK'

  AND 
  X.Code =
  CASE WHEN O.C52 IS NOT NULL 
      THEN O.C52
      ELSE  O.C02
  END

  AND O.Meta_IsCurrent = 1 
  AND A.Meta_IsCurrent = 1
  AND PL.Meta_IsCurrent = 1
  AND TS.Meta_IsCurrent = 1
  AND TC.Meta_IsCurrent = 1
  AND X.Meta_IsCurrent = 1

新查询:

SELECT TOP 10000
OBJECT_TYPE,
SEQ_NO,
O.AGR_LINE_NO,
SHORT_DESC,
C02,
C03,
C04,
C05,
C07,
C10,
C52,
N05,
N04,
N02,
N19,
N01,
carConcept.CodeDescription      AS CarConcept,
O.[Timestamp]       AS OBJ_TIMESTAMP,
O.Record_Timestamp  AS OBJ_RECORD_TIMESTAMP,
carConcept.RECORD_TIMESTAMP AS X_RECORD_TIMESTAMP

  FROM [Archive].[TIA_TIA_OBJECT] O

  INNER JOIN [Archive].[TIA_TIA_AGREEMENT_LINE] A   
  ON A.AGR_LINE_NO = O.Agr_Line_No

  INNER JOIN [Archive].[TIA_TIA_PRODUCT_LINE] PL 
  ON PL.PRODUCT_LINE_ID = A.PRODUCT_LINE_ID AND PL.PRODUCT_LINE_VER_NO = A.PRODUCT_LINE_VER_NO

  OUTER APPLY Staging.ufnGetCodeDescription(PL.PRODUCT_LINE_ID, PL.PRODUCT_LINE_VER_NO, PL.TARIFF_TYPE_LIST_VER,
  CASE WHEN O.C52 IS NOT NULL 
      THEN 'XTARIFTYPE'
      ELSE  'ART'
  END, 
  CASE WHEN O.C52 IS NOT NULL 
      THEN O.C52
      ELSE  O.C02
  END) carConcept

  WHERE O.OBJECT_TYPE = 'BIO01'

  AND O.Meta_IsCurrent = 1 
  AND A.Meta_IsCurrent = 1
  AND PL.Meta_IsCurrent = 1

表格值函数:

CREATE FUNCTION [Staging].[ufnGetCodeDescription]
(
    @ProductLineId as nvarchar(20),
    @ProductLineVersion as decimal(10,4),
    @TariffTypeListVer as decimal(10,4),
    @Type as nvarchar(20),
    @Code as nvarchar(20)
)
RETURNS @returntable TABLE
(
    CodeDescription nvarchar(100) NOT NULL, 
    Record_Timestamp datetime2 NOT NULL
)
AS
BEGIN

    DECLARE @CodeDescription as nvarchar(200) 
    DECLARE @Record_Timestamp as datetime2

    SELECT 
    @CodeDescription = X.[Description], 
    @Record_Timestamp = X.RECORD_TIMESTAMP

    FROM  [Archive].[TIA_TIA_TARIFF_STRUCTURE] TS 

    INNER JOIN [Archive].[TIA_TIA_TARIFF_CODES] TC 
    ON TC.PRODUCT_LINE_ID = @ProductLineId AND TC.[TYPE] = TS.[TYPE] AND TC.[VERSION] = TS.TYPE_VERSION  

    INNER JOIN [Archive].[TIA_TIA_XLA_PE_REFERENCE] X
    ON TC.[TYPE] = X.Table_Name AND @ProductLineId = X.ID AND TC.[VERSION] = X.[VERSION] AND TC.CODE = X.[CODE]

    WHERE 

    TS.PRODUCT_LINE_ID = @ProductLineId
    AND
    TS.[VERSION] = @TariffTypeListVer

    AND TS.CLASS = 'CODE'

    AND TC.[TYPE] = @Type
    AND TC.Code = @Code

    AND X.[Language] = 'DK'

    AND TS.Meta_IsCurrent = 1
    AND TC.Meta_IsCurrent = 1
    AND X.Meta_IsCurrent = 1

    IF @CodeDescription IS NOT NULL AND @Record_Timestamp IS NOT NULL
    BEGIN
        INSERT @returntable
        SELECT @CodeDescription, @Record_Timestamp
    END;
    RETURN;
END

执行计划原始查询:

https://pastebin.com/3j9G1rSi

enter image description here

执行计划新查询:

https://pastebin.com/uAADwuU6

enter image description here

2 个答案:

答案 0 :(得分:2)

使用OUTER APPLY实际上为每一行执行函数(以及函数中的SELECT)。

相反,我建议将函数调用移出FROM / WHERE。只需检索所有必要的列/

我们的想法是首先选择必要的列,并将函数调用到相对较小的行集,其中所有过滤器都已应用。

SELECT
    sub.*,
    carConcept.*
FROM (the new query except the OUTER APPLY) sub
  OUTER APPLY Staging.ufnGetCodeDescription(SUB.PRODUCT_LINE_ID, SUB.PRODUCT_LINE_VER_NO, SUB.TARIFF_TYPE_LIST_VER,
  CASE WHEN SUB.C52 IS NOT NULL 
      THEN 'XTARIFTYPE'
      ELSE  'ART'
  END, 
  CASE WHEN SUB.C52 IS NOT NULL 
      THEN SUB.C52
      ELSE  SUB.C02
  END) carConcept

或者您仍然可以使用INNER JOIN。如果INNER JOIN返回多行,则添加GROUP BY逻辑以返回第一个值。

答案 1 :(得分:0)

我在函数中加入的表(TIA_TIA_TARIFF_STRUCTURE,TIA_TIA_TARIFF_CODES,TIA_TIA_XLA_PE_REFERENCE)没有正确的索引。

我认为没有必要,因为它们包含大约3K,17K和22K行。

将聚集索引放在[Staging]。[ufnGetCodeDescription]的表中,将性能从30秒加到5-6秒。