【问题标题】:T-SQL Update From Order ByT-SQL 更新来自 Order By
【发布时间】:2013-07-17 22:36:11
【问题描述】:

我正在尝试编写一个存储过程,它将在一个日期范围内为我提供审计信息。审计表存储审计日期、列名和旧值。我想在结果集中显示旧值和新值。我需要从下一个最近的审计条目或从实体本身获取新值。存储过程是获取我需要的结果集的多步骤方法。

  1. 使用日期范围内的审计记录创建一个 @results 临时表。
  2. 使用来自实体的当前值创建一个 @currentValues 临时表。
  3. 更新@results 表以存储新值

这是审计表的结构:

  • AuditId 唯一标识符 NEWID()
  • AuditDate 日期时间 GETDATE()
  • UserId 唯一标识符
  • EntityId 唯一标识符
  • 列名 nvarchar(100)
  • 旧值 nvarchar(MAX)

这里是sql:

CREATE PROC GetAuditSummary
    @StartDate datetime = NULL,
    @EndDate datetime = NULL
AS

DECLARE @Results table(
    AuditId uniqueidentifier,
    AuditDate datetime,
    UserId uniqueidentifier,
    EndityId uniqueidentifier,
    ColumnName nvarchar(100),
    OldValue nvarchar(MAX),
    NewValue nvarchar(MAX)
INSERT INTO @Results(AuditId, AuditDate, UserId, EntityId, ColumnName, OldValue)
SELECT AuditId, AuditDate, UserId, EntityId, ColumnName, OldValue
FROM Audit
WHERE (AuditDate >= @StartDate) AND (AuditDate < @EndDate)

DECLARE @CurrentValues table(
    EntityId uniqueidentifier,
    ColumnName nvarchar(100),
    Value nvarchar(MAX)
)
--Lengthy Code to fill @CurrentValues temp table. Assume @CurrentValues is populated

UPDATE @Results
SET NewValue = n.Value
FROM @Results r INNER JOIN
    (SELECT AUditId, AuditDate, EntityId, ColumnName, OldValue AS Value
     FROM Audit
     UNION ALL
     SELECT NULL, GETDATE(), EntityId, ColumnName, Value
     FROM @CurrentValues
     ORDER BY AuditDate DESC
    ) n ON n.EntityId = r.EntityId AND
           n.ColumnNmae = r.ColumName NAD
           n.AuditDate > r.AuditDate

SELECT * FROM @Results ORDER BY AuditDate DESC

现在,如果我错了,请纠正我,当更新语句执行时,NewValue 应该设置为连接结果集中的最后一个匹配行,并且由于我有由 AuditDate 排序的子查询,AuditDate 最接近@Results 中的当前记录应该是设置为 NewValue 的值。我已经尝试过了,但是我收到一个错误消息,告诉我不能在子查询中使用 Order By。还有另一种方法可以做到这一点吗?我愿意接受任何建议,但我需要考虑性能,因为结果中可能会有数千行。

--编辑

这是让它工作的一种方法,但我不确定它的性能是否最好。

UPDATE @Results
SET NewValue = COALESCE(
    (SELECT TOP 1 a.OldValue
     FROM Audit a
     WHERE (a.EntityId = r.EntityId) AND
        (a.ColumnName = r.ColumnName) AND
        (a.AuditDate > r.AuditDate)
     ORDER BY a.AuditDate),
    (SELECT TOP 1 c.Value
     FROM @CurrentValues c
     WHERE (c.EntityId = r.EntityId) AND
        (c.ColumnName = r.ColumnName))
FROM @Results r

【问题讨论】:

  • 您是否期望连接子查询的单行结果?还是不止一个?
  • 超过 1。在更新当前行之后可能有 4 次审计,我需要将 NewValue 更新为最接近正在处理的当前行的审计。我希望这是有道理的。

标签: sql-server tsql


【解决方案1】:

我会使用 Row_Number 或 Rank 函数来获取最后匹配行之后的行。

以下示例应该可以工作,如果您想要匹配日期之后的最新记录,您可能需要将 (order by n.AuditDate) 更改为 (order by n.AuditDate desc)。

  UPDATE @Results
  SET NewValue = n.Value
  FROM @Results r 
     INNER JOIN
               (
                select n.EntityId, n.ColumnName, n.Value, Row_Number() over(partition by n.EntityId, n.ColumnName order by n.AuditDate) RowNumber
                from 
                 @Results ir
                 inner join (
                               SELECT AuditDate, EntityId, ColumnName, OldValue AS Value
                               FROM Audit
                               UNION ALL
                               SELECT GETDATE(), EntityId, ColumnName, Value
                               FROM @CurrentValues
                            ) inn on inn.EntityId = ir.EntityId AND
                                        inn.ColumnNmae = ir.ColumName NAD
                                        inn.AuditDate > ir.AuditDate

               ) n ON n.EntityId = r.EntityId AND
                      n.ColumnNmae = r.ColumName AND
                      n.RowNumber = 1

【讨论】:

  • 查看我在问题中的编辑。这比您的解决方案好得多。知道为什么吗?我原以为你的表现会更好。
  • 这可能是因为没有涉及联合,并且只有在审计表中没有数据时才会获取 @currentvalues。也减少了一个连接。
  • 我编辑的 SQL 现在似乎对我来说是最好的。我还使用 CTE 尝试了您的方法,但产生了相同的性能。
猜你喜欢
  • 2011-03-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-03-21
  • 1970-01-01
相关资源
最近更新 更多