SQL查询以获取最新价格

时间:2020-03-05 18:49:15  来源:igfitidea点击:

我有一个表,其中包含MS SQL 2005表中许多不同"事物"的价格。每天每件事有数百条记录,不同的事物在不同的时间获取价格更新。

ID uniqueidentifier not null,
ThingID int NOT NULL,
PriceDateTime datetime NOT NULL,
Price decimal(18,4) NOT NULL

我需要获取一组物品的最新价格。下面的查询有效,但是我要返回几百行,并且必须循环遍历它们,并且每个ThingID仅提取最新的一行。我如何(例如通过GROUP BY)说我想要每个ThingID最新的?还是我必须使用子查询?

SELECT * 
FROM Thing
WHERE ThingID IN (1,2,3,4,5,6)
  AND PriceDate > cast( convert(varchar(20), getdate(), 106) as DateTime)

更新:为了隐藏复杂性,我将ID列放在一个int中。在现实生活中,它是GUID(而不是顺序类型)。我已经更新了上面的表def以使用uniqueidentifier。

解决方案

回答

我认为表结构的唯一解决方案是使用子查询:

SELECT *
   FROM Thing
   WHERE ID IN (SELECT max(ID) FROM Thing 
                   WHERE ThingID IN (1,2,3,4)
                   GROUP BY ThingID)

(鉴于最高的ID也意味着最新的价格)

但是,我建议我们添加" IsCurrent"列,如果不是最新价格,则该列为0;如果不是最新价格,则为1. 这将增加数据不一致的风险,但是当表变大时(如果它在索引中),它将大大加快整个过程。然后,我们要做的就是...

SELECT *
   FROM Thing
   WHERE ThingID IN (1,2,3,4)
     AND IsCurrent = 1

更新

好的,马库斯(Markus)更新了问题,以显示ID是唯一标识,而不是整数。这使得编写查询变得更加复杂。

SELECT T.* 
   FROM Thing T
   JOIN (SELECT ThingID, max(PriceDateTime)
            WHERE ThingID IN (1,2,3,4)
            GROUP BY ThingID) X ON X.ThingID = T.ThingID 
                                AND X.PriceDateTime = T.PriceDateTime
   WHERE ThingID IN (1,2,3,4)

我真的建议使用" IsCurrent"列,或者与答案中找到的其他建议一起使用,并使用"当前价格"表和单独的"价格历史记录"表(最终会最快,因为它可以保持价格)表本身很小)。

(我知道底部的ThingID是多余的。只需尝试使用或者不使用" WHERE",它会更快。不确定优化器完成工作后哪个版本会更快。)

回答

我会尝试类似以下子查询的操作,而忘记更改数据结构。

SELECT
 *
FROM
 Thing
WHERE 
 (ThingID, PriceDateTime) IN 
 (SELECT 
   ThingID, 
   max(PriceDateTime ) 
  FROM 
   Thing 
  WHERE 
   ThingID IN (1,2,3,4)
  GROUP BY 
   ThingID
 )

编辑上面的是ANSI SQL,我现在猜测在子查询中有多个列不适用于T SQL。 Marius,我无法测试以下内容,请尝试;

SELECT
 p.*
FROM
 Thing p,
 (SELECT ThingID, max(PriceDateTime ) FROM Thing WHERE ThingID IN (1,2,3,4) GROUP BY ThingID) m
WHERE 
 p.ThingId = m.ThingId
 and p.PriceDateTime = m.PriceDateTime

另一种选择是将日期更改为字符串并与ID串联,这样我们只有一列。但是,这有点令人讨厌。

回答

这取决于数据使用方式的性质,但是如果旧价格数据的使用频率不如当前价格数据那么频繁,则此处可能有一个价格历史记录表的参数。这样,随着新价格的到来,可以将非当前数据归档到价格历史记录表中(可能通过触发器)。

正如我所说,根据访问模型,这可能是一个选择。

回答

如果子查询路线太慢,我将把价格更新视为审核日志,并维护ThingPrice表,也许是价格更新表上的触发器:

ThingID int not null,
UpdateID int not null,
PriceDateTime datetime not null,
Price decimal(18,4) not null

主键只是ThingID,而" UpdateID"是原始表中的" ID"。

回答

我正在将uniqueidentifier转换为二进制文件,以便获得最大的标识符。
这应该确保我们不会从具有相同ThingID和PriceDateTimes的多个记录中获得重复项:

SELECT * FROM Thing WHERE CONVERT(BINARY(16),Thing.ID) IN
(
 SELECT MAX(CONVERT(BINARY(16),Thing.ID))
  FROM Thing
  INNER JOIN
   (SELECT ThingID, MAX(PriceDateTime) LatestPriceDateTime FROM Thing
    WHERE PriceDateTime >= CAST(FLOOR(CAST(GETDATE() AS FLOAT)) AS DATETIME)
    GROUP BY ThingID) LatestPrices
  ON Thing.ThingID = LatestPrices.ThingID
   AND Thing.PriceDateTime = LatestPrices.LatestPriceDateTime
 GROUP BY Thing.ThingID, Thing.PriceDateTime
) AND Thing.ThingID IN (1,2,3,4,5,6)

回答

由于ID不是连续的,因此我假设我们在ThingID和PriceDateTime上具有唯一索引,因此对于给定项目,只有一个价格可以是最新价格。

如果今天已定价,此查询将获取列表中的所有项目。如果删除PriceDate的where子句,则无论日期如何,我们都将获得最新价格。

SELECT * 
FROM Thing thi
WHERE thi.ThingID IN (1,2,3,4,5,6)
  AND thi.PriceDateTime =
     (SELECT MAX(maxThi.PriceDateTime)
      FROM Thing maxThi
      WHERE maxThi.PriceDateTime >= CAST( CONVERT(varchar(20), GETDATE(), 106) AS DateTime)
        AND maxThi.ThingID = thi.ThingID)

请注意,我将">"更改为"> =",因为我们可能会在一天开始时就设置价格

回答

由于使用的是SQL Server 2005,因此可以使用新的(CROSS | OUTTER)APPLY子句。 APPLY子句让我们将表值函数连接到表。

要解决该问题,首先定义一个表值函数,以从Thing检索特定ID,日期排序的前n行:

CREATE FUNCTION dbo.fn_GetTopThings(@ThingID AS GUID, @n AS INT)
  RETURNS TABLE
AS
RETURN
  SELECT TOP(@n) *
  FROM Things
  WHERE ThingID= @ThingID
  ORDER BY PriceDateTime DESC
GO

然后使用该函数检索查询中的前1条记录:

SELECT *
   FROM Thing t
CROSS APPLY dbo.fn_GetTopThings(t.ThingID, 1)
WHERE t.ThingID IN (1,2,3,4,5,6)

魔术是通过APPLY子句完成的,该子句将函数应用于左侧结果集中的每一行,然后与函数返回的结果集合并,然后重新调整最终结果集。 (注意:要像应用一样进行左联接,请使用OUTTER APPLY从左侧返回所有行,而CROSS APPLY仅返回在右侧具有匹配项的行)

BlaM:
因为我还不能发表评论(由于低代表数),甚至无法发表自己的回答^^,所以我将在邮件正文中回答:
-APPLY子句,即使它使用表值函数,它也会由SQL Server在内部进行优化,其方式是不为左侧结果集中的每一行调用该函数,而是从该函数中获取内部sql并将其与其余查询一起转换为join子句,因此其性能与使用子查询的查询的性能相当或者更好(如果计划是由sql server选择的,并且可以进行进一步的优化),并且我的个人经验对数据库正确建立索引并且统计信息是最新的时,APPLY没有性能问题(就像带有子查询的普通查询在这种情况下一样)

回答

尝试此操作(前提是我们只需要最新价格,而不需要该价格的标识符或者日期时间)

SELECT ThingID, (SELECT TOP 1 Price FROM Thing WHERE ThingID = T.ThingID ORDER BY PriceDateTime DESC) Price
FROM Thing T
WHERE ThingID IN (1,2,3,4) AND DATEDIFF(D, PriceDateTime, GETDATE()) = 0
GROUP BY ThingID