8

假设我有一个名为 Transaction 的表和另一个名为 Price 的表。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 秒左右),但它会使用每只基金的最新价格,这是错误的。

不好的部分是价格表与我们使用的其他一些表相比是微不足道的,我不清楚为什么它这么慢。我怀疑有问题的行迫使 SQL Server 处理笛卡尔积,但我不知道如何避免它。

我一直希望找到一种更有效的方法来做到这一点,但到目前为止我还没有想到。有任何想法吗?

4

3 回答 3

5

您没有指定您正在使用的 SQL Server 版本,但如果您使用的是支持排名函数和 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 回答
5

有一种查找具有最大值或最小值的行的方法,它涉及到自身的 LEFT JOIN,而不是更直观但可能也更昂贵的 INNER JOIN 到一个自派生的聚合列表。

基本上,该方法使用这种模式:

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 */
)

因此,结果是所有不存在具有相同键且值大于给定值的行的行。

当只有一张表时,上述方法的应用非常简单。但是,当有另一个表时,如何应用它可能并不那么明显,尤其是当,就像在你的情况下,另一个表使实际查询更加复杂,不仅因为它在那里,而且还通过为我们提供额外的过滤对于我们正在寻找的值,即日期的上限。

因此,当应用该方法的 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

这是一个类似的解决方案,不存在:

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 回答
0

两者都pricedatetransactiondate索引了吗?如果不是,您正在执行表扫描,这可能是性能瓶颈的原因。

于 2011-05-23T14:15:37.463 回答