0

UDTの2つのパラメーターを使用する照会には0.3秒かかりますが、インライン表の値を持つ関数にカプセル化されている場合は3.5+秒かかります。遅いUDFへのSQLの代替

私は(Why is a UDF so much slower than a subquery?)を読んだことがありますが、修正/書き換えには苦労しています。以下@ JasonALongのフィードバックパー

、0.3秒で完了SELECT文の

実行計画:https://www.brentozar.com/pastetheplan/?id=HJnrqC53Z(SQLこのページで利用可能であることに注意)。あなたの質問に言及するように、https://www.brentozar.com/pastetheplan/?id=BJZbqR93b

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 
+2

これはあまりにも曖昧です。あなたのUDFはスカラ関数またはテーブル値である可能性があり、シングルステートメントまたはマルチステートメントかもしれません。それらを相関サブクエリまたは結合サブクラスとして使用することができます。リストは頻繁に起こります。おそらくこれを読んだ後、特定の状況に関連した実際の例を与える必要がありますか? https://stackoverflow.com/help/mcve *(コードの2つのバージョンの実行計画を得て、それらがどのように異なっているかを知ることは悪い考えではありません。 * – MatBailie

+0

fyiに続く読者のために、Option(Recompile)https:// stackoverflowを追加しようとしました。com/questions/20864934/option-recompile-is-always-faster-whyしかし、これは違いはありませんでした。また、.Net(webserver)でSQLクエリを作成し、それを直接実行しようとしましたが、これは関数またはストアドプロシージャを使用するよりもさらに遅いことが判明しました。 –

+1

UDTで実行しても関数でラップされていないとどうなりますか?これは、パフォーマンスの問題(UDTの追加や関数のラッピング)の原因となった変更を分離するのに役立ちます。私はそれが問題であるUDTだと思う。もしそうなら、 'IN()'を使うのではなく、ゲームに参加するようにクエリを書き直してみるか、UDTにインデックスと統計を適用してみてください。 – MatBailie

答えて

0

"IN"句の代わりにジョインを使用すると、大いに役立ちました。 (テーブルvarをテンポラリテーブルに変更しただけでも、これもかなり助けになりました)

2

スカラー関数およびMULIステートメントのテーブル値関数(mTVF)の両方がある:このリンクで以下と実行計画貼り付け3.5秒で完了機能について

コード、 "ブラックボックス"をオプティマイザに送信します。

私は質問を「なぜこれほど悪いのですか?答えは、できるだけ効率的に実行される良い計画を思いつくためには、特定の要件やデータを引き出すテーブルに関する情報を知る必要があります。パフォーマンスにも重大な影響を与えます)。したがって、スカラー関数またはmTVFを使用すると、オプティマイザは、インライン・コードで可能なすべての要件を評価することができません。その応答は、関数が1回だけ実行され、その仮定に従って計画を実行すると仮定することです。

前提が間違っているため、間違った計画が生成され、恐ろしいパフォーマンスに終わります。

解決策は問題の機能を書き換えることです...キーは#1に、「インラインテーブル値関数」(iTVF)として書き直すことです。これらは、オプティマイザがコードを外部クエリ(つまり「インライン」という用語)に直接入力したかのように見える唯一の機能です。あなたがiTVFに慣れていないなら、彼らは2つの要件を持っています... 1彼らはテーブル関数でなければなりません(何らかの理由で、MS STILLは利用可能なスカラーバージョンを持っていません)...そして... 2これは関数本体は単一の文でなければならない(MUST)。

したがって、テーブル値関数が必要ない場合は、スカラー関数が必要ですか?多値関数が単一の(スカラー)値を返すことができないということはまったくありません...なぜ、iTVFとしてのすべての関数をコードする状況を認識しているのでしょうか。

ウェブ上でスカラー値を返すためにコード化されたテーブル関数を使用して、「インラインスカラー関数」を作成することに関する情報が不足していないことをお勧めします。

欲しいものは...

+0

@JasonALongありがとうございます、あなたの情報はより良い研究を指摘しています。しかし、あなたのコメントの下部に関連して、私の問題はスカラではなくテーブルを返すことです。 (また、私はジョインしないデータをサブクエリーに返すことはできないと考えています...) –

+1

具体的なことなしに言うのは難しいですが...ただ混乱はありません...私が言ったとき、 "単一のステートメント"として解釈されるべきではない "単純なステートメント" ... CTE、導出されたテーブルおよびサブクエリは完全に許容可能です。つまり、IFブロックを使用してコントロールフローを追加したり、内部変数を宣言したり設定したりすることはできません。ここで簡単に考えてみましょう... SQL文はセミコロンで終了するはずです...本文に1つ以上のセミコロンを追加できる場合、SQL Serverはそれを複数の値と見なします。 –

+1

SQL Serverが特定の関数を "インライン"と見なしていることを確認するには、単にsys.objectsテーブルを照会します。 SELECT * FROM sys.objects oどこのo.name = N'tfn_SomeFunction '; ...または...一度にすべてのudを見ることができます。SELECT * FROM sys.objects o o.type IN( 'FN'、 'FS'、 'IF'、 'TF'); –