繁体   English   中英

为什么从视图中加入选择的前N个要比仅从视图加入要快得多?

[英]Why is joining on select top N from view much faster than joining on just the view?

我有一个视图,该视图的未索引列已在另一个查询中加入。 我们无法在视图中创建索引,因为它使用外部联接。

外部查询本质上是:

select * from SomeTable AS T INNER JOIN
v_SomeView AS V ON T.Column = V.Column

查询非常慢,通过一些实验,我们发现将查询更改为:

select * from SomeTable AS T INNER JOIN
(select TOP 10000000 * from v_SomeView) AS V ON T.Column = V.Column

加快查询速度的一个重要因素(10分钟到大约5s)。

该视图仅包含约1000行。

有人可以解释造成这种巨大差异的情况吗?

执行计划:

http://dropcanvas.com/011il

这是实际的视图v_SecurityClassificationResult

SELECT        rc.SecurityId, rc.Status, rc.ExpiryDate, rc.StartDate, rc.ClassificationValue AS RiskClass, sg.ClassificationValue AS SecurityGroup, 
                         br.ClassificationValue AS BondRating, cur.ClassificationValue AS Currency, cod.ClassificationValue AS Country, reg.ClassificationValue AS Region
FROM            dbo.SecurityClassificationResult AS rc LEFT OUTER JOIN
                         dbo.SecurityClassificationResult AS sg ON rc.SecurityId = sg.SecurityId AND rc.ExpiryDate = sg.ExpiryDate AND sg.SecurityClassificationFieldId =
                             (SELECT        SecurityClassificationFieldId
                               FROM            dbo.SecurityClassificationField
                               WHERE        (ClassificationField = 'SecurityGroup')) LEFT OUTER JOIN
                         dbo.SecurityClassificationResult AS br ON rc.SecurityId = br.SecurityId AND sg.ExpiryDate = br.ExpiryDate AND br.SecurityClassificationFieldId =
                             (SELECT        SecurityClassificationFieldId
                               FROM            dbo.SecurityClassificationField
                               WHERE        (ClassificationField = 'BondRating')) LEFT OUTER JOIN
                         dbo.SecurityClassificationResult AS cur ON rc.SecurityId = cur.SecurityId AND br.ExpiryDate = cur.ExpiryDate AND cur.SecurityClassificationFieldId =
                             (SELECT        SecurityClassificationFieldId
                               FROM            dbo.SecurityClassificationField
                               WHERE        (ClassificationField = 'Currency')) LEFT OUTER JOIN
                         dbo.SecurityClassificationResult AS cod ON rc.SecurityId = cod.SecurityId AND cur.ExpiryDate = cod.ExpiryDate AND cod.SecurityClassificationFieldId =
                             (SELECT        SecurityClassificationFieldId
                               FROM            dbo.SecurityClassificationField
                               WHERE        (ClassificationField = 'CountryOfDomicile')) LEFT OUTER JOIN
                         dbo.SecurityClassificationResult AS reg ON rc.SecurityId = reg.SecurityId AND cod.ExpiryDate = reg.ExpiryDate AND reg.SecurityClassificationFieldId =
                             (SELECT        SecurityClassificationFieldId
                               FROM            dbo.SecurityClassificationField
                               WHERE        (ClassificationField = 'Region'))
WHERE        (rc.SecurityClassificationFieldId =
                             (SELECT        SecurityClassificationFieldId
                               FROM            dbo.SecurityClassificationField
                               WHERE        (ClassificationField = 'RiskClass')))

以及从中选择的另一个查询:

SELECT        count(*)
FROM            dbo.Fund_RelevantSecurity AS FRS INNER JOIN
                         dbo.SourceSystemImportLog AS SSIL ON FRS.SourceSystemImporLogtId = SSIL.SourceSystemImporLogtId INNER JOIN
                         dbo.FundInstanceHolding AS FIH ON FRS.FundInstanceHoldingId = FIH.FundInstanceHoldingId INNER JOIN
                         dbo.FundInstance AS FI ON FIH.FundInstanceId = FI.FundInstanceId INNER JOIN
                         dbo.SourceSystemImportLog AS SSILOrig ON SSILOrig.SourceSystemImporLogtId = FI.SourceSystemImportLogId INNER JOIN
                         dbo.Security ON FRS.SecurityId = dbo.Security.SecurityId INNER JOIN
                         dbo.Fund ON FRS.FundId = dbo.Fund.FundId INNER JOIN
                         dbo.BusinessUnitFund ON dbo.Fund.FundId = dbo.BusinessUnitFund.FundId INNER JOIN
                         dbo.BusinessUnit ON dbo.BusinessUnit.BusinessUnitId = dbo.BusinessUnitFund.BusinessUnitId RIGHT OUTER JOIN
                         (select top 2147483647 * from dbo.v_SecurityClassificationResult) AS classificationResult ON FRS.SecurityId = classificationResult.SecurityId AND GETDATE() 
                         <= classificationResult.ExpiryDate AND GETDATE() >= classificationResult.StartDate
WHERE        (SSIL.ImportStatusId <> 5)

我建议您重建索引并刷新统计信息。 通过执行TOP N,您的优化器将采取另一种计划,并且看来这一计划要好得多。 尝试检查预期的记录数和实际记录数,以查找错误的计划。 另一件事是,如果重写视图,则可能会获得更好的性能。 您可以执行类似的操作(由于没有提供结构和数据,因此无法检查语法和逻辑):

SELECT
    rc.SecurityId ,
    rc.Status ,
    rc.ExpiryDate ,
    rc.StartDate ,
    s.ClassificationValue AS RiskClass ,
    MIN(CASE WHEN s.ClassificationValue = 'SecurityGroup' THEN s.ClassificationValue END) AS SecurityGroup ,
    MIN(CASE WHEN s.ClassificationValue = 'BondRating' THEN s.ClassificationValue END) AS BondRating ,
    MIN(CASE WHEN s.ClassificationValue = 'Currency' THEN s.ClassificationValue END) AS Currency ,
    MIN(CASE WHEN s.ClassificationValue = 'CountryOfDomicile' THEN s.ClassificationValue END) AS Country ,
    MIN(CASE WHEN s.ClassificationValue = 'Region' THEN s.ClassificationValue END) AS Region
FROM
    dbo.SecurityClassificationResult AS rc
    LEFT OUTER JOIN
    (
    SELECT s.ClassificationValue,
            sf.ClassificationField
    FROM  dbo.SecurityClassificationResult AS s
    JOIN dbo.SecurityClassificationField AS sf
      ON sf.SecurityClassificationFieldId = s.SecurityClassificationFieldId
    WHERE sf.ClassificationField IN ( 'SecurityGroup', 'BondRating', 'Currency', 'CountryOfDomicile' 'Region')

    ) s
    ON s.SecurityId = rc.SecurityId
WHERE
    ( rc.SecurityClassificationFieldId = (
                                           SELECT
                                            SecurityClassificationFieldId
                                           FROM
                                            dbo.SecurityClassificationField
                                           WHERE
                                            ( ClassificationField = 'RiskClass' )
                                         ) )
GROUP BY     rc.SecurityId ,
    rc.Status ,
    rc.ExpiryDate ,
    rc.StartDate ,
    s.ClassificationValue       

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM