假设我有一个名为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秒),但它会使用每个基金的最新价格,这是错误的。
糟糕的是,与我们使用的其他一些表相比,价格表是微不足道的,而且我不清楚它为什么这么慢。我怀疑违规行迫使SQL Server处理笛卡尔积,但我不知道如何避免它。
我一直希望找到一种更有效的方法来做到这一点,但到目前为止它已经逃过了我。有什么想法吗?
答案 0 :(得分:5)
有一种方法可以找到具有最大值或最小值的行,这涉及LEFT JOIN到self,而不是更直观,但也可能更昂贵,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
或其NOT EXISTS对应物:
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
这是一个与NOT EXISTS类似的解决方案:
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 */
)
答案 1 :(得分:4)
您没有指定您正在使用的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
答案 2 :(得分:0)
pricedate
和transactiondate
都被编入索引吗?如果不是,您正在进行表扫描,这可能是导致性能瓶颈的原因。