使用示例在sql server中使用timestamp列的实际用途是什么?

sat*_*jit 35 sql-server

我在sql server中遇到了timestamp数据类型.具有示例的sql server中timestamp列的实际用途是什么?

Bog*_*ean 31

我使用了TIMESTAMP数据类型(ROWVERSION,SQL2005 +)来避免丢失更新问题:

丢失的更新问题:第二个事务在第一个并发事务写入的第一个值之上写入数据项(datum)的第二个值,并且第一个值丢失到其他需要按时优先运行的事务,读取第一个值.读取错误值的事务以不正确的结果结束.

示例lost update::

t  : User 1 read payment order (PO) #1 (amount 1000)
t+1: User 2 read payment order (PO) #1 (amount 1000)

t+2: User 1 change the amount for PO #1 to 1005
t+3: User 2 change the amount for PO #1 to 1009 (change make by User 1 is lost because is overwritten by change make by User 2)
t+4: The amount is **1009**.
Run Code Online (Sandbox Code Playgroud)

示例:如何防止lost update:

t  : User 1 read payment order (PO) #1 (amount 1000, timestamp 0x00000000000007D1)
t+1: User 2 read payment order (PO) #1 (amount 1000, timestamp 0x00000000000007D1)

t+2: User 1 change the amount for PO #1 to 1005 and it checks if row has the same `timestamp` (column `RW` in this case; 0x00000000000007D1). The check succeeds and the change is `COMMIT`ed. This will change, also, the timestamp (column 'RW'). The new timestamp is 0x00000000000007D4.
t+3: User 2 change the amount for PO #1 to 1009 and it checks if row has the same `timestamp` (column `RW` in this case; 0x00000000000007D4). The checks fails because the initial timestamp (@rw=0x00000000000007D1) is <> than current timestamp (column `RW`=0x00000000000007D4). An error is raised the catch block "intercepts" the error and this transaction is cancelled (`ROLLBACK`).
t+4: The amount {remains|is} **1005**.
Run Code Online (Sandbox Code Playgroud)

示例:T-SQL脚本How to prevent the lost update(警告:您必须使用两个SSMS窗口/两个会话)

CREATE DATABASE TestRowVersion;
GO
USE TestRowVersion;
GO

CREATE TABLE dbo.PaymentOrder(
    PaymentOrderID INT IDENTITY(1,1) PRIMARY KEY,
    PaymentOrderDate DATE NOT NULL,
    Amount NUMERIC(18,2) NOT NULL,
    CreateDate DATETIME NOT NULL DEFAULT (GETDATE()),
    UpdateDate DATETIME NULL,
    RW ROWVERSION NOT NULL -- R[ow] V[ersion]
);
GO

INSERT  dbo.PaymentOrder (PaymentOrderDate,Amount)
VALUES  ('2013-07-21',1000);
INSERT  dbo.PaymentOrder (PaymentOrderDate,Amount)
VALUES  ('2013-07-22',2000);
INSERT  dbo.PaymentOrder (PaymentOrderDate,Amount)
VALUES  ('2013-07-23',3000);
GO

SELECT * FROM dbo.PaymentOrder;
/*
PaymentOrderID PaymentOrderDate Amount  CreateDate              UpdateDate RW
-------------- ---------------- ------- ----------------------- ---------- ------------------
1              2013-07-21       1000.00 2013-07-21 09:35:38.750 NULL       0x00000000000007D1
2              2013-07-22       2000.00 2013-07-21 09:35:38.750 NULL       0x00000000000007D2
3              2013-07-23       3000.00 2013-07-21 09:35:38.750 NULL       0x00000000000007D3
*/
GO

-- User 1 (SQL Server Management Studio/SSMS window #1)
    -- [t] Client app, user 1: it loads first PO
    SET NOCOUNT ON;
    GO
    DECLARE @PaymentOrderID INT=1;  -- parameter

    SELECT  po.PaymentOrderID,
            po.PaymentOrderDate,
            po.Amount,
            po.RW 
    FROM    dbo.PaymentOrder po 
    WHERE   po.PaymentOrderID=@PaymentOrderID;

    -- Client app, user 1: during 15 seconds it edit the amount from 1000.00 to 1005.00
    WAITFOR DELAY '00:00:15';
    GO

    -- [t+2] Client app, user 1: it sends this change (new amount) from client app to database server 
    -- with the old row version value
    DECLARE @PaymentOrderID INT=1;              -- parameter
    DECLARE @rw BINARY(8)=0x00000000000007D1;   -- parameter
    DECLARE @NewAmount NUMERIC(18,2)=1005.00;   -- parameter

    BEGIN TRY
        BEGIN TRANSACTION
            UPDATE  dbo.PaymentOrder
            SET     Amount=@NewAmount
            WHERE   PaymentOrderID=@PaymentOrderID
            AND     RW=@rw; -- it checks the timestamp (current timestamp versus original timestamp)
            DECLARE @rowcount INT=@@ROWCOUNT; -- How many rows were affected by the last statement (UPDATE in this case) ?
            SELECT @rowcount AS [@@ROWCOUNT];
            IF @rowcount<>1
                RAISERROR('Lost update or row deleted.', 16, 1);
        COMMIT TRANSACTION
        PRINT 'UPDATE succeded';
    END TRY
    BEGIN CATCH
        IF @@TRANCOUNT>0
            ROLLBACK;

        DECLARE @ErrMsg NVARCHAR(2002);
        SET @ErrMsg=ERROR_MESSAGE();
        RAISERROR(@ErrMsg,16,1);
    END CATCH;
    GO

    -- [t+4] Client app, user 1: it reloads first PO
    DECLARE @PaymentOrderID INT=1;  -- parameter

    SELECT  po.PaymentOrderID,
            po.PaymentOrderDate,
            po.Amount,
            po.RW 
    FROM    dbo.PaymentOrder po 
    WHERE   po.PaymentOrderID=@PaymentOrderID;  
    GO

-- User 2 (warning: run this script in another SQL Server Management Studio window: File > New Database Engine Query !; SSMS window #2)
    -- [t+1] Client app, user 1: it loads first PO
    SET NOCOUNT ON;
    GO
    DECLARE @PaymentOrderID INT=1;  -- parameter

    SELECT  po.PaymentOrderID,
            po.PaymentOrderDate,
            po.Amount,
            po.RW 
    FROM    dbo.PaymentOrder po 
    WHERE   po.PaymentOrderID=@PaymentOrderID;

    -- Client app, user 1: during 20 seconds it edit the amount from 1000.00 to 1005.00
    WAITFOR DELAY '00:00:20';
    GO

    -- [t+4] Client app, user 1: it sends this change (new amout) from client app to database server 
    -- with the old row version value
    DECLARE @PaymentOrderID INT=1;              -- parameter
    DECLARE @rw BINARY(8)=0x00000000000007D1;   -- parameter
    DECLARE @NewAmount NUMERIC(18,2)=1009.00;   -- parameter

    BEGIN TRY
        BEGIN TRANSACTION
            UPDATE  dbo.PaymentOrder
            SET     Amount=@NewAmount
            WHERE   PaymentOrderID=@PaymentOrderID
            AND     RW=@rw; -- it checks the timestamp (current timestamp versus original timestamp)
            DECLARE @rowcount INT=@@ROWCOUNT; -- How many rows were affected by the last statement (UPDATE in this case) ?
            SELECT @rowcount AS [@@ROWCOUNT];
            IF @rowcount<>1
                RAISERROR('Lost update or row deleted.', 16, 1);
        COMMIT TRANSACTION
        PRINT 'UPDATE succeded';
    END TRY
    BEGIN CATCH
        IF @@TRANCOUNT>0
            ROLLBACK;

        DECLARE @ErrMsg NVARCHAR(2002);
        SET @ErrMsg=ERROR_MESSAGE();
        RAISERROR(@ErrMsg,16,1);
    END CATCH;
    GO

    -- [t+4] Client app, user 1: it reloads first PO
    DECLARE @PaymentOrderID INT=1;  -- parameter

    SELECT  po.PaymentOrderID,
            po.PaymentOrderDate,
            po.Amount,
            po.RW 
    FROM    dbo.PaymentOrder po 
    WHERE   po.PaymentOrderID=@PaymentOrderID;  
    GO
Run Code Online (Sandbox Code Playgroud)

用户1的结果(金额1000 - > 1005):

PaymentOrderID PaymentOrderDate Amount                                  RW
-------------- ---------------- --------------------------------------- ------------------
1              2013-07-21       1000.00                                 0x00000000000007D1

@@ROWCOUNT <- Timestamp check succeeds 
-----------
1

UPDATE succeded
PaymentOrderID PaymentOrderDate Amount                                  RW
-------------- ---------------- --------------------------------------- ------------------
1              2013-07-21       1005.00                                 0x00000000000007D4
Run Code Online (Sandbox Code Playgroud)

用户2的结果(金额1000 - > 1009):

PaymentOrderID PaymentOrderDate Amount                                  RW
-------------- ---------------- --------------------------------------- ------------------
1              2013-07-21       1000.00                                 0x00000000000007D1

@@ROWCOUNT <- Timestamp check fails 
-----------
0

Msg 50000, Level 16, State 1, Line 27
Lost update.
PaymentOrderID PaymentOrderDate Amount                                  RW
-------------- ---------------- --------------------------------------- ------------------
1              2013-07-21       1005.00                                 0x00000000000007D4
Run Code Online (Sandbox Code Playgroud)

注意:将错误消息更改为RAISERROR('Lost update or row deleted.', 16, 1);

  • @Imad:这可能是一个解决方案,但开发人员应谨慎更新此列(“ [修改日期]”)。“ timestamp” /“ rowversion”列具有+号,当连接执行“ UPDATE”语句时,该列会由SQL Server数据库引擎自动更新 (2认同)

zed*_*xus 27

让我们以销售订单表为例来说明时间戳的作用.

create table saleorder (ordernumber int, amount int, timestamp);
insert into saleorder (ordernumber, amount) values (1, 100), (2, 100), (3, 200);
select * from saleorder
Run Code Online (Sandbox Code Playgroud)

注意timestamp列中的数据.时间戳文档(SQL Server 2005)说:这(即时间戳)跟踪数据库中的相对时间,而不是可以与时钟关联的实际时间...每次修改或插入带有时间戳列的行时,递增的数据库时间戳值将插入timestamp列中.

我们来看看数据的样子:

ordernumber amount  timestamp
1           100     0x00000000000007D1
2           100     0x00000000000007D2
3           200     0x00000000000007D3
Run Code Online (Sandbox Code Playgroud)

好的.首先添加订单1,最后输入订单3.如果我们要更新订单1的金额会怎样?

update saleorder set amount = 200 where ordernumber = 1
select * from saleorder
Run Code Online (Sandbox Code Playgroud)

啊,请注意,订单1的时间戳现在是0x7D4(Decimal 2004).与其他行相关,我们知道订单1最近更新了.但是,更重要的是,时间戳的值出现在并发写入发生时.

ordernumber amount  timestamp
1           200     0x00000000000007D4
2           100     0x00000000000007D2
3           200     0x00000000000007D3
Run Code Online (Sandbox Code Playgroud)

假设John和Mary都使用在.NET中开发的Web应用程序销售第3个订单.约翰拉起订单并做出改变.约翰还没有保存数据.玛丽拉相同的顺序并改变它.约翰先保存.玛丽试图保存数据..NET应用程序可以首先查看Mary提取的时间戳是否仍然与数据库对订单3的时间戳相同.

如果使用订单3获取的时间戳Mary现在不同(因为John保存的数据和时间戳自动更改),.NET应用程序可以提醒Mary并让她刷新屏幕上的记录以查看最新更改(或者可能突出显示在屏幕上改变).

将时间戳视为行版本.有趣的是,SQL Server的最新版本使用rowversion数据类型,它与timestamp数据类型同义.rowversion文档(SQL Server 2012)有一些有趣的例子.


Ste*_*man 7

我已经使用timestamp列来跟踪数据何时更改,特别是需要同步到一个或多个移动应用程序的数据.您可以使用timestamp列来返回自某个时间点以来已更改的行(通过提供上一个时间戳).