首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >减缓UDF的SQL替代方案

减缓UDF的SQL替代方案
EN

Stack Overflow用户
提问于 2017-10-08 00:37:47
回答 2查看 1.7K关注 0票数 0

包含两个UDT参数的查询需要0.3秒,但是当封装在内联表值函数中时,则需要3.5+秒。

我读过(Why is a UDF so much slower than a subquery?),但我正在为如何修复/重写而挣扎。

以下是Per @JasonALong的反馈

在0.3秒内完成的SELECT语句的执行计划:https://www.brentozar.com/pastetheplan/?id=HJnrqC53Z (请注意此页面上的SQL可用)。

函数代码,在3.5秒内完成,粘贴在下面,并在此链接上执行计划:https://www.brentozar.com/pastetheplan/?id=BJZbqR93b

代码语言:javascript
复制
SELECT
SelectedContracts.MeasurableID,
SelectedContracts.EntityID,

EntityName,
EntityAbbrev,
EntityLogoURL,
EntityHex1,
EntityHex2,
EntitySportID,

MeasurableName,
MeasurableOrganizationID,
YearFilter,
SeasonFilter,
CategoryFilter,
ResultFilter,
Logo4Result,
MeasurableSportID,
MouseoverFooter,
ContractRank4Org,
ContractEndUTC,

HighContractPrice4Period,
HighTradeID,
HighTradeUTC,
HighTradeNumberOfContracts,
HighTradeCurrency,

LowContractPrice4Period,
LowTradeID,
LowTradeUTC,
LowTradeNumberOfContracts,
LowTradeCurrency,

LastTradePrice,
LastTradeID,
LastTradeUTC,
LastTradeNumberOfContracts,
LastTradeCurrency,

SecondLastTradePrice,
SecondLastTradeID,
SecondLastTradeUTC,
SecondLastTradeNumberOfContracts,
SecondLastTradeCurrency,

ContractPrice4ChangeCalc,
ContractID4ChangeCalc,
ContractUTC4ChangeCalc,
ContractsNumberTraded4ChangeCalc,
ContractCurrency4ChangeCalc,

HighestBidID,
HighestBidMemberID,
HighestBidPrice,
HighestBidAvailableContracts,
HighestBidCurrency,

LowestAskID,
LowestAskMemberID,
LowestAskPrice,
LowestAskAvailableContracts,
LowestAskCurrency


FROM
(
    SELECT
        dbo.Contracts.MeasurableID,
        dbo.Contracts.EntityID
    FROM
        dbo.Contracts
    WHERE
        dbo.Contracts.MeasurableID IN ((2030),(2017),(2018),(2019),(2020),( 2028),(2024),(2027),(2029),(2022),( 4018),(4019),(4020),(4021))
    GROUP BY
        dbo.Contracts.MeasurableID,
        dbo.Contracts.EntityID
) SelectedContracts


INNER JOIN 
(
    SELECT
        dbo.Entities.ID,
        --dbo.Entities.OrganizationID, -- Get OrganizationID from Measurable since some Entities (European soccer teams) have multiple Orgs
        dbo.Entities.EntityName,
        dbo.Entities.EntityAbbrev,
        dbo.Entities.logoURL AS EntityLogoURL,
        dbo.Entities.Hex1 AS EntityHex1,
        dbo.Entities.Hex2 AS EntityHex2,
        dbo.Entities.SportID AS EntitySportID
    FROM
        dbo.Entities
) SelectedEntities ON SelectedContracts.EntityID = SelectedEntities.ID


INNER JOIN 
(
    SELECT
        dbo.Measurables.ID AS MeasurableID,
        dbo.Measurables.Name AS MeasurableName,
        dbo.Measurables.OrganizationID AS MeasurableOrganizationID,
        dbo.Measurables.[Year] AS YearFilter,
        dbo.Measurables.Season AS SeasonFilter,
        dbo.Measurables.Category AS CategoryFilter,
        dbo.Measurables.Result AS ResultFilter,
        dbo.Measurables.Logo4Result,
        dbo.Measurables.SportID AS MeasurableSportID,
        dbo.Measurables.MouseoverFooter,
        dbo.Measurables.ContractRank4Org,
        dbo.Measurables.EndUTC AS ContractEndUTC
    FROM
        dbo.Measurables
) MEASURABLES_table ON SelectedContracts.MeasurableID = MEASURABLES_table.MeasurableID


LEFT JOIN 
(
    SELECT
        MeasurableID,
        EntityID,
        ContractPrice AS HighContractPrice4Period,
        ID AS HighTradeID,
        UTCMatched AS HighTradeUTC,
        NumberOfContracts AS HighTradeNumberOfContracts,
        CurrencyCode AS HighTradeCurrency
    FROM
                (
                    SELECT
                        *, ROW_NUMBER () OVER (
                            PARTITION BY MeasurableID,
                            EntityID
                        ORDER BY
                            ContractPrice DESC,
                            ID DESC
                        ) RowNumber -- ID DESC means most recent trade of ties
                    FROM
                        Contracts
                    WHERE
                        MeasurableID IN ((2030),(2017),(2018),(2019),(2020),( 2028),(2024),(2027),(2029),(2022),( 4018),(4019),(4020),(4021))
                        AND dbo.Contracts.UTCmatched < DATEADD(DAY, -30, SYSDATETIME())
                        AND (           CurrencyCode IN (('GBP'), ('CAD'), ('INR'), ('BRL'), ('MXN'), ('CHF'), ('RUB')) 
                                )   
                ) AS InnerSelect4HighTrade

    WHERE   
        InnerSelect4HighTrade.RowNumber = 1

) HighTrades ON SelectedContracts.MeasurableID = HighTrades.MeasurableID AND SelectedContracts.EntityID = HighTrades.EntityID


LEFT JOIN 
(
    SELECT
        MeasurableID,
        EntityID,
        ContractPrice AS LowContractPrice4Period,
        ID AS LowTradeID,
        UTCMatched AS LowTradeUTC,
        NumberOfContracts AS LowTradeNumberOfContracts,
        CurrencyCode AS LowTradeCurrency
    FROM
        (
            SELECT
                    *, ROW_NUMBER () OVER (
                    PARTITION BY MeasurableID,
                    EntityID
                ORDER BY
                    ContractPrice ASC,
                    ID DESC
                ) RowNumber -- ID DESC means most recent trade of ties
            FROM
                Contracts
            WHERE
                MeasurableID IN ((2030),(2017),(2018),(2019),(2020),( 2028),(2024),(2027),(2029),(2022),( 4018),(4019),(4020),(4021))
                AND dbo.Contracts.UTCmatched < DATEADD(DAY, -30, SYSDATETIME())
                AND (           CurrencyCode IN (('GBP'), ('CAD'), ('INR'), ('BRL'), ('MXN'), ('CHF'), ('RUB')) 
                        )           
        ) AS InnerSelect4LowTrade

    WHERE       InnerSelect4LowTrade.RowNumber = 1

) LowTrades ON SelectedContracts.MeasurableID = LowTrades.MeasurableID AND SelectedContracts.EntityID = LowTrades.EntityID


LEFT JOIN 
(
    SELECT
        MeasurableID,
        EntityID,
        ContractPrice AS LastTradePrice,
        ID AS LastTradeID,
        UTCMatched AS LastTradeUTC,
        NumberOfContracts AS LastTradeNumberOfContracts,
        CurrencyCode AS LastTradeCurrency
    FROM
        (
            SELECT
                *, ROW_NUMBER () OVER (
                    PARTITION BY MeasurableID,
                    EntityID
                ORDER BY
                    ID DESC
                ) RowNumber -- ID DESC means most recent trade of ties
            FROM
                Contracts
            WHERE
                MeasurableID IN ((2030),(2017),(2018),(2019),(2020),( 2028),(2024),(2027),(2029),(2022),( 4018),(4019),(4020),(4021))
                AND (           CurrencyCode IN (('GBP'), ('CAD'), ('INR'), ('BRL'), ('MXN'), ('CHF'), ('RUB')) 
                        )   
        ) AS InnerSelect4LastTrade

    WHERE   InnerSelect4LastTrade.RowNumber = 1

) LastTrades ON SelectedContracts.MeasurableID = LastTrades.MeasurableID AND SelectedContracts.EntityID = LastTrades.EntityID


LEFT JOIN 
(
    SELECT
        MeasurableID,
        EntityID,
        ContractPrice AS SecondLastTradePrice,
        ID AS SecondLastTradeID,
        UTCMatched AS SecondLastTradeUTC,
        NumberOfContracts AS SecondLastTradeNumberOfContracts,
        CurrencyCode AS SecondLastTradeCurrency
    FROM
        (
            SELECT
                *, ROW_NUMBER () OVER (
                    PARTITION BY MeasurableID,
                    EntityID
                ORDER BY
                    ID DESC
                ) RowNumber -- ID DESC means most recent trade of ties
            FROM
                Contracts
            WHERE
                MeasurableID IN ((2030),(2017),(2018),(2019),(2020),( 2028),(2024),(2027),(2029),(2022),( 4018),(4019),(4020),(4021))
                AND (           CurrencyCode IN (('GBP'), ('CAD'), ('INR'), ('BRL'), ('MXN'), ('CHF'), ('RUB')) 
                        )   
--need time filter???
        ) AS InnerSelect4SecondToLastTrade

    WHERE InnerSelect4SecondToLastTrade.RowNumber = 2

) SecondToLastTrade ON SelectedContracts.MeasurableID = SecondToLastTrade.MeasurableID AND SelectedContracts.EntityID = SecondToLastTrade.EntityID


LEFT JOIN 
(
    SELECT
        MeasurableID,
        EntityID,
        ContractPrice AS ContractPrice4ChangeCalc,
        ID AS ContractID4ChangeCalc,
        UTCMatched AS ContractUTC4ChangeCalc,
        NumberOfContracts AS ContractsNumberTraded4ChangeCalc,
        CurrencyCode AS ContractCurrency4ChangeCalc
    FROM
        (
            SELECT
                *, ROW_NUMBER () OVER (
                    PARTITION BY MeasurableID,
                    EntityID
                ORDER BY
                    ID DESC  -- ID DESC equals the most recent trade if ties
                ) RowNumber 
            FROM
                Contracts
            WHERE
                MeasurableID IN ((2030),(2017),(2018),(2019),(2020),( 2028),(2024),(2027),(2029),(2022),( 4018),(4019),(4020),(4021))
                AND (           CurrencyCode IN (('GBP'), ('CAD'), ('INR'), ('BRL'), ('MXN'), ('CHF'), ('RUB')) 
                        )   
            AND dbo.Contracts.UTCmatched < DATEADD(Day ,-30, SYSDATETIME())
        ) AS InnerSelect4ChangeCalcPerPeriod

    WHERE   InnerSelect4ChangeCalcPerPeriod.RowNumber = 1

) Trade4ChangeCalcPerPeriod ON SelectedContracts.MeasurableID = Trade4ChangeCalcPerPeriod.MeasurableID AND SelectedContracts.EntityID = Trade4ChangeCalcPerPeriod.EntityID


LEFT JOIN 
(
    SELECT
        MeasurableID,
        EntityID,
        ID AS HighestBidID,
        MemberID AS HighestBidMemberID,
        BidPrice AS HighestBidPrice,
        AvailableContracts AS HighestBidAvailableContracts,
        CurrencyCode AS HighestBidCurrency
    FROM
        (
            SELECT
                *, ROW_NUMBER () OVER (
                    PARTITION BY MeasurableID,
                    EntityID
                ORDER BY
                    BidPrice DESC,
                    ID DESC
                ) RowNumber
            FROM
                dbo.Interest2Buy
            WHERE
                MeasurableID IN ((2030),(2017),(2018),(2019),(2020),( 2028),(2024),(2027),(2029),(2022),( 4018),(4019),(4020),(4021))
            AND AvailableContracts > 0
                AND (           CurrencyCode IN (('GBP'), ('CAD'), ('INR'), ('BRL'), ('MXN'), ('CHF'), ('RUB')) 
                        )   
        ) AS InnerSelect4HighestBid

    WHERE   InnerSelect4HighestBid.RowNumber = 1

) HighestBids ON SelectedContracts.MeasurableID = HighestBids.MeasurableID AND SelectedContracts.EntityID = HighestBids.EntityID


LEFT JOIN 
(
    SELECT
        MeasurableID,
        EntityID,
        ID AS LowestAskID,
        MemberID AS LowestAskMemberID,
        AskPrice AS LowestAskPrice,
        AvailableContracts AS LowestAskAvailableContracts,
        CurrencyCode AS LowestAskCurrency
    FROM
        (
            SELECT
                *, ROW_NUMBER () OVER (
                    PARTITION BY MeasurableID,
                    EntityID
                ORDER BY
                    AskPrice ASC,
                    ID DESC
                ) RowNumber
            FROM
                dbo.Interest2Sell
            WHERE
                MeasurableID IN ((2030),(2017),(2018),(2019),(2020),( 2028),(2024),(2027),(2029),(2022),( 4018),(4019),(4020),(4021))
                AND AvailableContracts > 0
                AND (           CurrencyCode IN (('GBP'), ('CAD'), ('INR'), ('BRL'), ('MXN'), ('CHF'), ('RUB')) 
                        )   
        ) AS InnerSelect4BestAsk

    WHERE   InnerSelect4BestAsk.RowNumber = 1

) BestAsks ON SelectedContracts.MeasurableID = BestAsks.MeasurableID AND SelectedContracts.EntityID = BestAsks.EntityID
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2017-10-15 22:09:59

使用联接而不是"IN“子句非常有帮助。(虽然我也将表var更改为临时表,但这也有很大帮助。)

票数 0
EN

Stack Overflow用户

发布于 2017-10-08 01:44:37

在您的问题中,标量函数和多语句表值函数(mTVF)都是优化器的“黑匣子”.

所以,我认为这个问题是:“为什么这件事这么糟?”答案是,为了想出一个尽可能高效地执行的好计划,它需要知道关于具体需求的某些细节,以及它将从其中提取数据的表的信息(这就是为什么过时的静力学也会严重影响性能)。所以..。当您使用标量函数或mTVF时,优化器不能像使用内联代码那样评估所有的需求。它的反应是简单地假设函数只执行一次,并根据这个假设制定计划。

因为假设是错误的,错误的计划就会产生,你就会以糟糕的表现告终。

解决办法是重写冒犯的函数..。关键在于#1,确保将它们重写为“内联表值函数”(iTVF)。这些是优化器将看到的唯一函数,就好像它们的代码被直接输入到外部查询中一样(因此“内联”一词)。如果你不熟悉iTVF,他们有两个要求.它们必须是表函数(不管出于什么原因,MS仍然没有提供标量版本).还有.2这是最重要的..。函数体必须是一个语句。

那么,如果不需要表值函数,那么需要标量函数呢?多值函数不能返回单个(标量)值.这就是为什么那些知道使用代码的情况的人,他们所有的函数都是iTVF。

好的是,web上并不缺少关于创建“内联标量函数”的信息,使用编码的表函数在web上返回标量值。

希望这能帮上忙。

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/46626476

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档