使用 TOP 并获得不同的结果集

ret*_*ala 3 sql-server t-sql top

我正在尝试编写一个有效的查询来删除数据块。为此,我希望通过使用主键来获取最旧的记录来避免索引扫描。但是,我看到返回了一些意想不到的结果。

我希望这个

SELECT TOP 15 OrderID FROM [Order]
Run Code Online (Sandbox Code Playgroud)

会给我最旧的 15 条记录,因为我可以依靠主键递增,因此表中的存储顺序将从低到高。

但是,这会返回不同的结果集

SELECT TOP 15 OrderID FROM [Order] ORDER BY DateCreated ASC
Run Code Online (Sandbox Code Playgroud)

这似乎是获得我需要的结果的更准确但更昂贵的方式。

令人费解的是,这

SELECT TOP 15 * FROM [Order]
Run Code Online (Sandbox Code Playgroud)

为此提供一组不同的 OrderID (PK)

SELECT TOP 15 OrderID FROM [Order]
Run Code Online (Sandbox Code Playgroud)

我知道http://msdn.microsoft.com/en-gb/library/ms189463.aspx解释说没有 ORDER BY 子句就不能保证订单,但期望 PK 为我订购并且无法解释两者之间的差异最后两个选择子句。

Aar*_*and 11

看看计划。当您使用SELECT *它时,可能会使用聚集索引,而当您只需要一列时,也许可以使用更薄的索引。

不要“期望”某个顺序。如果您不告诉 SQL Server 如何订购,那么它将使用最有效的方式,这可能会因 20 多个因素而改变。

如果您想要某个订单,请说。请在此处阅读 #3:

此外,Michael Swart 的这篇文章可能很有趣:

如果您希望您的第二个查询更有效,您可以考虑创建一个索引DateCreated(您可能想要包括OrderID- 不确定当前的索引结构)。


对于n一次删除行的实际目标,首先删除最旧的行,并假设OrderID是一IDENTITY列(因此订单创建日期应大致与此一致),为什么不使用这种方法(基于这篇很棒的博客文章,也是由 Michael Swart 撰写的):

-- pick a datetime for the newest row you want to delete
-- let's say you want to delete all orders before Jan 1 2014:

SELECT @MaxOrderID = MAX(OrderID)
  FROM dbo.[Order] -- terrible table name, also always use dbo prefix
  WHERE DateCreated < '20140101';

DECLARE @BatchSize INT = 1000,
        @LargestOrderProcessed INT = -1,
        @NextBatchMax INT,
        @RC INT = 1;

WHILE (@RC > 0)
BEGIN
  SELECT TOP (@BatchSize) @NextBatchMax = OrderID
    FROM dbo.[Order]
    WHERE OrderID > @LargestOrderProcessed
    AND OrderID <= @MaxOrderID
    ORDER BY OrderID;

  DELETE dbo.[Order]
    WHERE OrderID > @LargestOrderProcessed
    AND OrderID <= @NextBatchMax;

  SET @RC = @@ROWCOUNT;
  SET @LargestOrderProcessed = @NextBatchMax;
END
Run Code Online (Sandbox Code Playgroud)

为了尽量减少对日志的影响,您可能希望在其中添加一些额外的逻辑,来自我的博客文章Break large delete operations into chunks。至于dbo前缀,请参阅要踢的坏习惯:避免使用架构前缀