我正在开发一个系统,该系统将定期处理来自外部源的消息并将结果存储在我们的数据库中。特别是,它会收到诸如“患者 X 于 2018 年 10 月 22 日上午 09:45 移至位置 Y...”之类的消息。
理想情况下,我希望能够使用 SQL Server 的时态表来创建“患者去过哪里”的历史轨迹,这样我就可以查询他们在特定时间点的位置。
-- ============================================================
CREATE TABLE [dbo].[Patients] (
[Id] [int] IDENTITY(1,1) NOT NULL,
CONSTRAINT [PK_Patients] PRIMARY KEY([Id]),
-- Data...
);
-- ============================================================
CREATE TABLE [dbo].[Locations] (
[Id] [int] IDENTITY(1,1) NOT NULL,
CONSTRAINT [PK_Locations] PRIMARY KEY([Id]),
-- Data...
);
-- ============================================================
CREATE TABLE [dbo].[PatientLocations] (
[PatientId] [int] NOT NULL,
CONSTRAINT [PK_PatientLocations] PRIMARY KEY([PatientId]),
CONSTRAINT [FK_PatientLocations_PatientId] FOREIGN KEY([PatientId])
REFERENCES [dbo].[Patients] ([Id]),
[LocationId] [int] NOT NULL,
CONSTRAINT [FK_PatientLocations_LocationId] FOREIGN KEY([LocationId])
REFERENCES [dbo].[Locations] ([Id]),
[DateStartedUtc] datetime2 GENERATED ALWAYS AS ROW START NOT NULL,
[DateEndedUtc] datetime2 GENERATED ALWAYS AS ROW END NOT NULL,
PERIOD FOR SYSTEM_TIME ([DateStartedUtc],[DateEndedUtc])
)
WITH ( SYSTEM_VERSIONING = ON ( HISTORY_TABLE = [dbo].[PatientLocations_History] ) );
问题是 [DateStartedUtc] 只能填充当前系统时间,这不一定是患者位置更改的时间。
我的具体问题是: - 有没有一种好方法可以将新数据输入到临时表中,但用特定的开始日期标记它?([DateStartedUtc] 必须是“生成”吗?) - 如果不是,是否还有其他存储可查询历史表的最佳实践?
编辑添加: - Aaron 在评论中提醒我提到关闭和打开系统版本控制。我研究的一个潜在解决方案是关闭系统版本控制,在 dbo.PatientLocations 或 dbo.PatientLocations_History 中插入一个新行,然后重新打开系统版本控制(全部在事务中)。我认为这不是定期更新表格的好解决方案(并且需要我手动维护开始和结束日期),但我愿意被说服。
其他更新
为了添加更多上下文,我最初的解决方案(在我发现 Temporal Tables 之前)是使用触发器维护单个历史表:
-- ============================================================
CREATE TABLE [dbo].[Patients] (
[Id] [int] IDENTITY(1,1) NOT NULL,
CONSTRAINT [PK_Patients] PRIMARY KEY([Id]),
-- Data...
);
-- ============================================================
CREATE TABLE [dbo].[Locations] (
[Id] [int] IDENTITY(1,1) NOT NULL,
CONSTRAINT [PK_Locations] PRIMARY KEY([Id]),
-- Data...
);
-- ============================================================
CREATE TABLE [dbo].[PatientLocations] (
[Id] [int] IDENTITY(1,1) NOT NULL,
CONSTRAINT [PK_PatientLocations] PRIMARY KEY ([Id]),
[PatientId] [int] NOT NULL,
CONSTRAINT [FK_PatientLocations_PatientId] FOREIGN KEY([PatientId])
REFERENCES [dbo].[Patients] ([Id]),
[LocationId] [int] NOT NULL,
CONSTRAINT [FK_PatientLocations_LocationId] FOREIGN KEY([LocationId])
REFERENCES [dbo].[Locations] ([Id]),
-- Timestamps
[LocationStartedAtDateOffset] [datetimeoffset] NOT NULL,
[_LocationCompletedAtDateOffset] [datetimeoffset] NULL,
);
GO
-- ================================================================================
CREATE TRIGGER [dbo].[TRG_PatientLocations_LocationCompletedAtDateOffset]
ON [dbo].[PatientLocations]
AFTER UPDATE, INSERT, DELETE
AS
BEGIN
WITH
-- ========================================
[ModifiedPatientIds] AS (
SELECT DISTINCT [PatientId]
FROM (
SELECT [PatientId] FROM inserted
UNION SELECT [PatientId] FROM deleted
) p
),
-- ========================================
[PList] AS (
SELECT
p.[Id],
p.[PatientId],
p.[LocationStartedAtDateOffset],
LEAD(p.[LocationStartedAtDateOffset], 1)
OVER(PARTITION BY p.[PatientId] ORDER BY p.[LocationStartedAtDateOffset])
AS [LocationCompletedAtDateOffset]
FROM [dbo].[PatientLocations] p
JOIN [ModifiedPatientIds] mp ON p.[PatientId] = mp.[PatientId]
)
-- ========================================
UPDATE p SET
p.[_LocationCompletedAtDateOffset] = pl.[LocationCompletedAtDateOffset]
FROM [dbo].[PatientLocations] p
JOIN [PList] pl ON p.[Id] = pl.[Id]
END;
GO
我最初放弃了这个解决方案,因为触发器通常表现不佳。
我认为Temporal Tables 可以优雅地解决我的问题,但正如评论所提到的,它们实际上只是为系统版本控制而设计的,而不是用户定义的版本控制。
所以我修改后的问题是: - 在数据库中存储和维护用户定义历史的最佳方式是什么?- 有没有比使用触发器更好的解决方案?或者有没有更有效的方法来使用触发器?