17

我需要在以下时态表中为我的本地开发目的播种数据,开始日期应该是旧的。给定的表架构是

CREATE TABLE [dbo].[Contact](
    [ContactID] [uniqueidentifier] NOT NULL,
    [ContactNumber] [nvarchar](50) NOT NULL,
    [SequenceID] [int] IDENTITY(1,1) NOT NULL,
    [SysStartTime] [datetime2](0) GENERATED ALWAYS AS ROW START NOT NULL,
    [SysEndTime] [datetime2](0) GENERATED ALWAYS AS ROW END NOT NULL,
 CONSTRAINT [PK_Contact] PRIMARY KEY NONCLUSTERED 
(
    [ContactID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY],
    PERIOD FOR SYSTEM_TIME ([SysStartTime], [SysEndTime])
) ON [PRIMARY]
WITH
(
    SYSTEM_VERSIONING = ON (HISTORY_TABLE = [dbo].[ContactHistory] , DATA_CONSISTENCY_CHECK = ON )
)

我需要在这个表中插入一些旧的日期数据。

INSERT INTO dbo.Contact
(
    ContactID,
    ContactNumber,
    --SequenceID - this column value is auto-generated
    SysStartTime,
    SysEndTime
)
VALUES
(
    NEWID(), -- ContactID - uniqueidentifier
    N'9999912345', -- ContactNumber - nvarchar
    -- SequenceID - int
    '2017-09-01 06:26:59', -- SysStartTime - datetime2
    NULL -- SysEndTime - datetime2
)

我收到以下错误。

无法将显式值插入到表“DevDB.dbo.Contact”的 GENERATED ALWAYS 列中。将 INSERT 与列列表一起使用以排除 GENERATED ALWAYS 列,或将 DEFAULT 插入 GENERATED ALWAYS 列。

请帮助我如何将旧数据数据添加或更新到此临时表中

4

4 回答 4

30

最后我找到了解决方案

步骤#1:需要将其关闭SYSTEM_VERSIONING

ALTER TABLE dbo.Contact SET (SYSTEM_VERSIONING = OFF);

步骤#2:需要删除PERIOD FOR SYSTEM_TIME

ALTER TABLE dbo.Contact DROP PERIOD FOR SYSTEM_TIME

步骤 #3 : 插入过去日期的所需记录

INSERT INTO dbo.Contact
(
    ContactID,
    ContactNumber,
    SysStartTime,
    SysEndTime
)
VALUES
(
    NEWID(), -- ContactID - uniqueidentifier
    N'1234567890', -- ContactNumber - nvarchar
    '2014-09-13 00:00:00', -- SysStartTime - datetime2
    '9999-12-31 23:59:59' -- SysEndTime - datetime2
)

步骤#4:需要添加PERIOD FOR SYSTEM_TIME

ALTER TABLE dbo.Contact
ADD PERIOD FOR SYSTEM_TIME ([SysStartTime], [SysEndTime])

步骤#5:需要将其打开SYSTEM_VERSIONING

ALTER TABLE dbo.[Contact] SET (SYSTEM_VERSIONING = ON
 (HISTORY_TABLE=dbo.[ContactHistory],DATA_CONSISTENCY_CHECK=ON)
);

而已...

于 2017-09-13T11:12:14.317 回答
0

你想达到什么目的?GENERATED ALWAYS 列是技术列,如果您将它们设置为无法更新它们,它们会自动更新。要跟踪更改,您有 ContactHistory 表。

通常你应该使用以下插入:

INSERT INTO dbo.Contact
(
    ContactID, --NEWID()
    ContactNumber
)
VALUES
(
    '045ABA61-1C64-4FE4-B079-18A9A50335D5', -- ContactID - uniqueidentifier
    N'9999912345'
);

然后在第一次插入之后,这就是你所拥有的:

select * from dbo.Contact
select * from dbo.ContactHistory

在此处输入图像描述 您在历史记录中还没有任何记录,因为它不存储实际记录。现在,如果您想更改数据,那么您可以使用普通的 UPDATE 语句忽略这些 GENERATED ALWAYS 列:

UPDATE dbo.Contact SET ContactNumber = '123456789' WHERE ContactId = '045ABA61-1C64-4FE4-B079-18A9A50335D5'

让我们再次检查数据:

select * from dbo.Contact
select * from dbo.ContactHistory

现在你的情况有点不同:

在此处输入图像描述

如您所见,实际数据按预期更新,并且历史表具有“关闭”结束时间的旧记录。

因此,如果您想获得对 SCD 的本机支持,那么 Sql Server 会为您做所有事情,只是不要碰这些列。如果出于某些特殊原因需要更新这些列,则不要使用 GENERATED ALWAYS 列并为此使用 DEFAULT 约束。

于 2017-09-12T11:00:15.700 回答
0

可以办到

可以使用现有数据(包括日期)初始化 SQL 临时(系统版本化)表和历史记录。它只涉及跳过一堆愚蠢的箍。希望微软将来能给我们一个更好的方法来用现有的历史数据初始化这些表。

将现有数据放入具有特定 SYSTEM_TIME 日期的这些表中的技巧的简短版本是:

  1. 将系统版本控制添加到表中。
  2. 收集数据以添加到系统版本化表,其中包含用于填充目标表中的 SYSTEM_TIME 列的列。请注意,SYSTEM_TIME 值将位于 UTC 时区,因此可能需要执行 AT TIME ZONE 'UTC' 之类的操作来获取正确的日期。
  3. 关闭系统版本控制。
  4. 为 CRUD 操作创建动态 SQL。
  5. 重新打开系统版本控制。

这是该过程的简化示例:

-- System versioned table: dbo.ManagerList
-- Primary Key: ManagerName
-- History table: dbo.ManagerList_History
-- SYSTEM_TIME columns: _PeriodStart, _PeriodEnd
-- Table with Data to Import: #SourceData

DECLARE @Script varchar(max)

-- Disable system versioning
ALTER TABLE dbo.ManagerList SET (SYSTEM_VERSIONING = OFF);

ALTER TABLE dbo.ManagerList 
DROP PERIOD FOR SYSTEM_TIME;

-- Prepare source data (in temporary table #SourceData)
UPDATE  A
SET     _PeriodEnd = B.PeriodEnd
FROM    #SourceData as A
        INNER JOIN 
        (
            SELECT  ManagerName,
                    _PeriodStart,
                    _PeriodEnd = 
                        LEAD(_PeriodStart, 1, datetime2fromparts(9999,12,31,23,59,59,9999999,7)) OVER
                        (
                            PARTITION BY ManagerName
                            ORDER BY _PeriodStart
                        )
        ) as B
            ON  A.ManagerName = B.ManagerName
            AND A._PeriodStart = B._PeriodStart

-- DELETE from System-Versioned table
DELETE  A
FROM    dbo.ManagerList as A
WHERE   NOT EXISTS
        (
            SELECT  1
            FROM    #SourceData
            WHERE   ManagerName = A.ManagerName
        )

-- UPDATE script for System-Versioned table
SET @Script = 
        'UPDATE A ' +
        'SET    FavoriteColor   = B.FavoriteColor, ' + 
        '       _PeriodStart    = B._PeriodStart ' +
        'FROM   dbo.ManagerList as A ' +
        '       INNER JOIN #SourceData as B ' + 
        '           ON A.ManagerName = B.ManagerName ' + 
        'WHERE  B._PeriodEnd > datefromparts(9999,12,31) ' + 
        '       ( ' +
        '           A.ManagerName       != B.ManagerName ' +
        '           OR A._PeriodStart   != B.PeriodStart ' +
        '       )'
EXEC (@Script)
    
-- UPDATE script for System-Versioned table
SET @Script = 
        'UPDATE A ' +
        'SET    FavoriteColor       = B.FavoriteColor, ' + 
        '       _PeriodStart    = B._PeriodStart ' +
        'FROM   dbo.ManagerList ' +
        '       INNER JOIN #SourceData as B ' + 
        '           ON A.ManagerName = B.ManagerName ' + 
        'WHERE  B._PeriodEnd > datefromparts(9999,12,31) ' + 
        '   AND (' +
        '           A.FavoriteColor     != B.FavoriteColor ' +
        '           OR A._PeriodStart   != B.PeriodStart ' +
        '       )'
EXEC (@Script)

-- INSERT script for System-Versioned table
SET @Script = 
        'INSERT dbo.ManagerList ' + 
        '( ' +
        '   ManagerName, ' +
        '   FavoriteColor, ' +
        '   _PeriodStart ' +
        ') ' +
        'SELECT ManagerName, ' +
        '       FavoriteColor, ' +
        '       _PeriodStart ' +
        'FROM   #SourceData as A ' +
        'WHERE  _PeriodEnd > datefromparts(9999,12,31) ' + 
        '   AND NOT EXISTS ' +
        '       ( ' +
        '           SELECT  1 ' +
        '           FROM    dbo.ManagerList ' +
        '           WHERE   ManagerName = A.ManagerName ' +
        '       )'
EXEC (@Script)

-- DELETE script for History table
SET @Script = 
        'DELETE A ' +
        'FROM   dbo.ManagerList_History as A ' +
        'WHERE  NOT EXISTS ' +
        '       ( ' +
        '           SELECT  1 ' +
        '           FROM    #SourceData ' +
        '           WHERE   ManagerName = A.ManagerName ' +
        '               AND _PeriodEnd < datefromparts(9999,12,31) ' +
        '       )'
EXEC (@Script)

-- UPDATE script for History table
SET @Script = 
        'UPDATE A ' +
        'SET    FavoriteColor   = B.FavoriteColor ' +
        'FROM   dbo.ManagerList_History as A' +
        '       INNER JOIN #SourceData as B ' + 
        '           ON  A.ManagerName = B.ManagerName ' + 
        '           AND A._PeriodStart = B._PeriodStart ' + 
        'WHERE  B._PeriodEnd < datefromparts(9999,12,31) ' + 
        '   AND (' +
        '           A.FavoriteColor     != B.FavoriteColor' +
        '       )'
EXEC (@Script)
    
-- INSERT script for History table
SET @Script = 
        'INSERT dbo.ManagerList ' + 
        '( ' +
        '   ManagerName, ' +
        '   FavoriteColor, ' +
        '   _PeriodStart ' +
        ') ' +
        'SELECT ManagerName, ' +
        '       FavoriteColor, ' +
        '       _PeriodStart ' +
        'FROM   #SourceData as A ' +
        'WHERE  _PeriodEnd < datefromparts(9999,12,31) ' + 
        '   AND NOT EXISTS ' +
        '       ( ' +
        '           SELECT  1 ' +
        '           FROM    dbo.ManagerList_History ' +
        '           WHERE   ManagerName = A.ManagerName ' +
        '               AND _PeriodStart = A._PeriodStart ' + 
        '       )'
EXEC (@Script)

-- Re-enabling system versioning
-- Note: Making this dynamic SQL solves compiler error
SET @Script = 
    'ALTER TABLE dbo.ManagerList ' +
    'ADD    PERIOD FOR SYSTEM_TIME (_PeriodStart, _PeriodEnd)';
EXEC (@Script)

ALTER TABLE dbo.ManagerList 
SET (SYSTEM_VERSIONING = ON  (HISTORY_TABLE = dbo.ManagerList_History));

主要问题似乎是编译器并不总是能够识别系统版本表上的东西何时关闭,并且不会让非动态 SQL 编译或运行。不知道为什么可以使用动态 SQL,但我很高兴它可以做到。

在最初从现有表中填充表之后,所有关于不修改 SYSTEM_TIME 日期或删除历史记录中的记录的限制在世界范围内都是有意义的。

希望这可以帮助某人。

于 2021-01-04T15:44:21.750 回答
-1

注意 Sql server 2016 仅从System-Versioned Temporal TablesANSI 标准 2011 引入,该标准定义了 3 种临时表

截至 2011 年 12 月,ISO/IEC 9075,数据库语言 SQL:2011 第 2 部分:SQL/Foundation 在表定义中包含子句,用于定义“应用程序时间段表”(有效时间表)、“系统版本表”(事务时间表)和“系统版本化的应用程序时间段表”(双时态表)。 https://en.wikipedia.org/wiki/Temporal_database

System-Versioned Temporal Tables 不支持更改已注册的历史记录。因此,要创建测试时态数据集,您需要按顺序重新执行所有 hystory INSERT。

于 2017-09-12T11:23:54.570 回答