8

TransactionというテーブルとPriceという別のテーブルがあるとします。価格は、さまざまな日付での特定のファンドの価格を保持します。各ファンドにはさまざまな日付で価格が追加されますが、すべての可能な日付で価格が設定されるわけではありません。したがって、ファンドXYZの場合、5月1日、5月7日、および5月13日の価格があり、ファンドABCの場合、5月3日、5月9日、および5月11日の価格があります。

だから今、私は取引の日にファンドに優勢だった価格を見ています。取引は5月10日のファンドXYZに対するものでした。私が欲しいのは、その日の最新の既知の価格で、5月7日の価格になります。

コードは次のとおりです。

select d.TransactionID, d.FundCode, d.TransactionDate, v.OfferPrice
from Transaction d
    inner join Price v
        on v.FundCode = d.FundCode
        and v.PriceDate = (
            select max(PriceDate)
            from Price
            where FundCode = v.FundCode
            /* */ and PriceDate < d.TransactionDate 
        )

動作しますが、非常に低速です(実際の使用では数分)。先頭のコメントのある行を削除すると、クエリは非常に高速(2秒程度)になりますが、ファンドごとの最新の価格が使用されます。これは誤りです。

悪い部分は、価格表が私たちが使用する他のいくつかの表と比較して非常に小さいことであり、なぜそれがそれほど遅いのか私にはわかりません。問題のある行がSQLServerにデカルト積を処理させているのではないかと思いますが、それを回避する方法がわかりません。

私はこれを行うためのより効率的な方法を見つけることを望んでいますが、それは今のところ私を逃れています。何か案は?

4

3 に答える 3

5

最大値または最小値を持つ行を検索する方法があります。これには、より直感的ではなく、自己への左結合が含まれますが、おそらくよりコストがかかる、自己派生集約リストへの内部結合も含まれます。

基本的に、このメソッドは次のパターンを使用します。

SELECT t.*
FROM t
  LEFT JOIN t AS t2 ON t.key = t2.key
    AND t2.Value > t.Value  /* ">" is when getting maximums; "<" is for minimums */
WHERE t2.key IS NULL

またはその存在しない対応物:

SELECT *
FROM t
WHERE NOT EXISTS (
  SELECT *
  FROM t AS t2
  WHERE t.key = t2.key
    AND t2.Value > t.Value  /* same as above applies to ">" here as well */
)

したがって、結果は、同じキーと指定された値より大きい行が存在しないすべての行になります。

テーブルが1つしかない場合、上記のメソッドの適用は非常に簡単です。ただし、別のテーブルがある場合、特に他のテーブルが存在するだけでなく、追加のフィルタリングを提供することによって実際のクエリをより複雑にする場合は、それを適用する方法が明確でない場合があります。私たちが探している値、つまり日付の上限について。

したがって、メソッドのLEFT JOINバージョンを適用すると、結果のクエリは次のようになります。

SELECT
  d.TransactionID,
  d.FundCode,
  d.TransactionDate,
  v.OfferPrice
FROM Transaction d
  INNER JOIN Price v ON v.FundCode = d.FundCode
  LEFT JOIN Price v2 ON v2.FundCode = v.FundCode  /* this and */
    AND v2.PriceDate > v.PriceDate                /* this are where we are applying
                                                       the above method; */
    AND v2.PriceDate < d.TransactionDate          /* and this is where we are limiting
                                                       the maximum value */
WHERE v2.FundCode IS NULL

そして、これがNOTEXISTSを使用した同様のソリューションです。

SELECT
  d.TransactionID,
  d.FundCode,
  d.TransactionDate,
  v.OfferPrice
FROM Transaction d
  INNER JOIN Price v ON v.FundCode = d.FundCode
  WHERE NOT EXISTS (
    SELECT *
    FROM Price v2
    WHERE v2.FundCode = v.FundCode           /* this and */
      AND v2.PriceDate > v.PriceDate         /* this are where we are applying
                                                the above method; */
      AND v2.PriceDate < d.TransactionDate   /* and this is where we are limiting
                                                the maximum value */
  )
于 2011-05-23T16:10:16.483 に答える
5

使用しているSQLServerのバージョンを指定しませんが、ランキング関数とCTEクエリをサポートするバージョンを使用している場合は、結合内で相関サブクエリを使用するよりもかなりパフォーマンスが高いと思います。声明。

Andriyのクエリとパフォーマンスが非常に似ているはずです。テーブルの正確なインデックストポグラフィによっては、あるアプローチが別のアプローチよりもわずかに速い場合があります。

結果のコードは(私の意見では)かなり読みやすいので、私はCTEベースのアプローチを好む傾向があります。お役に立てれば!

;WITH set_gen (TransactionID, OfferPrice, Match_val)
AS
(
    SELECT d.TransactionID, v.OfferPrice, ROW_NUMBER() OVER(PARTITION BY d.TransactionID ORDER BY v.PriceDate ASC) AS Match_val
    FROM Transaction d
        INNER JOIN Price v
            ON v.FundCode = d.FundCode
    WHERE v.PriceDate <= d.TransactionDate
)
SELECT sg.TransactionID, d.FundCode, d.TransactionDate, sg.OfferPrice
FROM Transaction d
    INNER JOIN set_gen sg ON d.TransactionID = sg.TransactionID
WHERE sg.Match_val = 1
于 2011-05-23T16:28:37.343 に答える
0

pricedatetransactiondateインデックスの両方がありますか?そうでない場合は、パフォーマンスのボトルネックの原因である可能性が高いテーブルスキャンを実行しています。

于 2011-05-23T14:15:37.463 に答える