确定最新版本记录的最有效方法是什么?

时间:2016-08-23 13:37:15

标签: sql sql-server performance

我正在查询每个记录有多个修订版的表,但不存储每条记录的版本号。我必须从“触及”的日期推导出来。

我想只检索最新版本。

我在两种方法之间胡扯:ROW_NUMBER()LEFT OUTER JOIN但我不确定哪种方法会表现得更好。也许这取决于背景,但我不确定要做出哪些考虑。我不知道这些表的索引。

这两种方法在性能上是否存在显着差异?

ROW_NUMBER()

SELECT *
FROM
(
    SELECT  h.header_id,
            h.touched_on,
            ROW_NUMBER() OVER (PARTITION BY h.header_id ORDER BY h.touched_on DESC) AS revision
    FROM header h
    JOIN event e ON h.serial_no = e.serial_no
    WHERE h.touched_on BETWEEN @startDate AND @endDate
) noms
WHERE noms.revision = 1

LEFT JOIN

SELECT  h.header_id,
        h.touched_on,
FROM header h
LEFT OUTER JOIN header h2 ON h2.header_id = h.header_id AND h2.touched_on > h.touched_on
JOIN event e ON h.serial_no = e.serial_no
WHERE h.touched_on BETWEEN @startDate AND @endDate
AND h2.header_id IS NULL

3 个答案:

答案 0 :(得分:1)

如果您想要检索一个特定密钥的当前(最新)版本,那么很容易:

New-Object : Cannot validate argument on parameter 'Property'. The argument is null or empty. Supply an argument that is not null or empty and then try the command again.
At C:\scripts\HideDC.ps1:9 char:37
+        New-Object psobject -Property <<<<  $_info     
    + CategoryInfo          : InvalidData: (:) [New-Object], 
ParameterBindingValidationException
    + FullyQualifiedErrorId : ParameterArgumentValidationError,Microsoft.PowerShell.Commands.NewObjectCommand

Cannot index into a null array.
At C:\scripts\HideDC.ps1:5 char:55
+            "Operation" = [string] $_.Properties.Value[ <<<< 0]
    + CategoryInfo          : InvalidOperation: (0:Int32) [], RuntimeException
    + FullyQualifiedErrorId : NullArray

为了提高效率,您的表格应按SELECT TOP(1) ... FROM <table> WHERE key = @key ORDER BY touched_on DESC; 进行群集。

但是,要检索 set 行(或所有)的当前(最新)版本,那么任务很难,至少可以说。 很多更好的设计是将表拆分为两个,一个包含当前行,另一个包含所有历史记录。这正是SQL Server 2016中的Temporal Tables所做的,这是what PostgreSQL does,这就是DB2所做的。基本上没有实现通过在同一个表中存储行的所有版本来提供时间旅行,因为它使查询当前数据非常昂贵

你知道,你的设计已知有问题且效率低下。在根本原因上解决问题要好得多,并将数据拆分为(key, touched_on)<table_current>

答案 1 :(得分:0)

不知道什么会向您显示您的LEFT JOIN查询,请考虑将其替换为CROSS APPLY:

SELECT  DISTINCT 
            h2.header_id,
            h2.touched_on
FROM header h
CROSS APPLY (
    SELECT TOP 1 * 
    FROM header 
    WHERE header_id = h.header_id 
        and touched_on BETWEEN @startDate AND @endDate
    ORDER BY touched_on DESC) as h2
JOIN [event] e ON h2.serial_no = e.serial_no
WHERE h.touched_on BETWEEN @startDate AND @endDate

答案 2 :(得分:0)

我的偏好是使用Common Table Expression: -

SET STATISTICS IO ON;
SET STATISTICS TIME ON;
WITH HeaderEvent
     AS (SELECT h.header_id,
                h.touched_on,
                RowNumber = ROW_NUMBER() OVER(PARTITION BY h.header_id ORDER BY h.touched_on DESC)
         FROM header h
              JOIN event e ON h.serial_no = e.serial_no
         WHERE h.touched_on BETWEEN @startDate AND @endDate)
     SELECT header_id,
            touched_on
     FROM HeaderEvent
     WHERE RowNumber = 1;

要比较性能,请在运行每个查询以比较指标之前包括以下内容: -

SET STATISTICS IO ON;
SET STATISTICS TIME ON;

您还可以调查Temporal Tables功能(仅限SQL Server 2016),以便有效地存储数据的版本历史记录。