【问题标题】:SQL Server row date last modifiedSQL Server 行日期上次修改
【发布时间】:2020-03-28 20:34:32
【问题描述】:

我的一位同事说,SQL Server 将上次修改的日期和时间保存在每条记录的“隐藏列”中。我很确定他说错了什么。你能向我确认一下吗?

【问题讨论】:

  • 您的同事可能对 Oracle 的 SCN_TO_TIMESTAMP 感到困惑,这是提交数据的大致时间。

标签: sql sql-server


【解决方案1】:

正如其他人所暗示的那样,您的同事一定是在胡言乱语,或者指的是别的东西。记录或页面的磁盘结构不包含对上次更新时间的任何引用。虽然您可以在对象级别找到有关上次更新的信息,但在记录/行级别没有此类信息。

【讨论】:

  • 我感谢 Damien_The_Unbeliever(并给他 +1)和 Mark S. Rasmussen 的回应。只选择了标记响应,因为先来。谢谢!
【解决方案2】:

没有按行维护的隐藏列,其中包含上次修改的日期/时间。也没有一列包含执行此类更改的用户的身份,也没有一列标识最后执行的更改是什么(插入或更新)。

如果您想要这些功能中的任何一个,就必须实现它们。对于某些用户来说,最后一点开销对他们来说都可能很重要,因此拥有隐藏功能(具有隐藏成本)是不可接受的。

【讨论】:

  • 我特别喜欢关于隐藏功能 == 隐藏成本的观点。这是一个我还无法向同事解释的概念。
【解决方案3】:

来自Pinal Dave,您可以使用 DMV 获取此信息。您的同事可能会在 TIMESTAMP 中引用修改后的行,但据我所知,只有当您通过显式添加该类型的列来要求它时,它才会保持这一点。

SELECT OBJECT_NAME(OBJECT_ID) AS DatabaseName, last_user_update,*
FROM sys.dm_db_index_usage_stats
WHERE database_id = DB_ID( 'AdventureWorks')
AND OBJECT_ID=OBJECT_ID('test');

【讨论】:

  • 所以你向我确认没有“每条记录中的隐藏字段”
  • @Bugeo - 不,不是每行。有TIMESTAMPROWVERSION,但你必须自己实现。这将为对该行所做的特定更新记录一个唯一 ID。除此之外,SQL Server 仅维护每个表最后一次被触摸的日期和时间。
  • 不可靠。第一个链接使其几乎无效。第二个有更好的,可能是sqlblogcasts.com/blogs/grumpyolddba/archive/2010/08/16/…stackoverflow.com/questions/4345603/…
  • @gbn - 我想我可以澄清一下,但是 DMV 可靠的吗?他们可以并且确实会一直在版本之间进行更改。
【解决方案4】:

如果您想知道特定行的最后更新时间,您应该在表中维护一个“LastUpdated”时间戳列,并在每次 UPDATE 时更新它(例如通过触发器自动)

【讨论】:

  • (尽管这是一篇旧文章):我倾向于将我的时间戳更新作为我准备好的语句(存储过程)的一部分。
  • @Paul Ada 说的和你说的有矛盾吗?
  • @Ahmed - 哇!这是一个旧的!有点儿。只是偏好更新发生的位置。 AdaTheDev 使用触发器来插入时间戳,而我觉得如果使用一个或多个存储过程会有更多的控制,因为您使用的任何准备好的语句都可以实现时间戳。 (基本上触发是粗略的方法)。
【解决方案5】:

您可以尝试使用 PageID 读取 SQL Server 事务日志。

这不是一种保证的方式,但它在很多情况下都有效,并且比查询索引统计信息更准确一些

/* This is example how to query datetime of data changes using SQL Server 
   transaction log, if audit routine wasn't set */

USE [AdventureWorksDW2017]
GO

UPDATE dbo.FactFinance
SET Date = GETDATE(), Amount = Amount + 1
WHERE FinanceKey = 1
GO

WITH main AS
(   
    SELECT 
        replace(replace(sys.fn_PhysLocFormatter (%%physloc%%), ')', ''), '(', '') page,
        FinanceKey, Date, Amount
    FROM 
        dbo.FactFinance (NOLOCK)   
    WHERE 
        FinanceKey = 1
),
tlog AS
( 
    SELECT 
        page, l2.[Begin Time], L2.[End Time],
        l1.AllocUnitName l1_uname, l2.[Transaction Name] l2_tname,
        l1.[Transaction ID]
    FROM 
        main
    JOIN
        sys.fn_dblog(NULL, NULL) l1 ON PATINDEX('%'+main.page+'%', l1.[Lock Information]) > 0
    LEFT JOIN 
        sys.fn_dblog(NULL, NULL) l2 ON l2.[Transaction ID] = l1.[Transaction ID]  
)
SELECT 
    PAGE, [Transaction ID], 
    MIN([Begin Time]), MIN([End Time]),
    MIN(l1_uname), MIN(l2_tname)
FROM
    tlog
GROUP BY 
    [Transaction ID], page

【讨论】:

    【解决方案6】:

    记录上次更新的时间和更新者可以通过添加一个默认值为 GetDate() 的 last_updated 列来维护。上次更新用户名很棘手,因为您的应用很可能使用公共数据库连接。可以通过将用户名作为自定义添加到所有插入和更新过程并传递 AD、SSO 或登录凭据来填充用户名——只要对您的身份验证方法有意义即可。

    最后一个更新列非常有用和必要,它应该作为数据库或至少表的设置选项包含在内。为什么?输入事实后,尝试在没有它的情况下获取此信息(提示,如果您有身份字段 - 它“可能”有帮助,但可能不可靠)。

    【讨论】:

    • 这将记录创建记录的日期/时间,而不是修改时间
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-06
    • 2017-11-19
    • 1970-01-01
    • 1970-01-01
    • 2016-11-10
    • 2016-11-14
    相关资源
    最近更新 更多