Временные таблицы SQL Server - Как мне ввести строку, которая не "сейчас"?
Я работаю над системой, которая будет периодически обрабатывать сообщения из внешнего источника и сохранять результаты в нашей базе данных. В частности, он получает сообщения типа "Пациент X переехал в пункт Y в 9:45 утра, 22 октября 2008 года..."
В идеале я хотел бы иметь возможность использовать временные таблицы 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] быть "GENERATED"?) - Если нет, существуют ли другие рекомендации для хранения запрашиваемой таблицы истории?
Отредактировано, чтобы добавить: - Аарон напомнил мне в комментариях, чтобы я упомянул о выключении и включении системы контроля версий. Одно из возможных решений, которое я исследовал, состояло бы в том, чтобы отключить управление версиями системы, вставить новую строку в dbo.PatientLocations или dbo.PatientLocations_History и снова включить управление версиями системы (все в рамках транзакции). Я не думаю, что это хорошее решение для регулярных обновлений таблицы (и это потребовало бы, чтобы я вручную поддерживал даты начала и окончания), но я открыт для убеждения.
Дополнительные обновления
Чтобы добавить больше контекста, мое первоначальное решение (до того, как я обнаружил временные таблицы) состояло в том, чтобы поддерживать одну таблицу истории с триггером:
-- ============================================================
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
Я изначально отошел от этого решения, так как триггеры обычно работают плохо.
Я думал, что временные таблицы элегантно решат мою проблему, но, как уже упоминалось в комментариях, они действительно предназначены только для системного управления версиями, а не для пользовательских версий.
Итак, мой измененный вопрос: - Каков наилучший способ хранить и поддерживать пользовательскую историю в базе данных? - Есть ли лучшее решение, чем использование триггеров? Или есть более эффективный способ использования триггеров?