Стратегии запросов с использованием системных версий SQL Server 2016 с временными таблицами для медленно меняющихся измерений

17

При использовании системной версии темпоральной таблицы (впервые в SQL Server 2016), как влияют на разработку запросов и производительность, когда эта функция используется для обработки медленно меняющихся измерений в большом реляционном хранилище данных?

Например, предположим, что у меня есть Customerизмерение из 100 000 строк со Postal Codeстолбцом и Salesтаблица фактов из нескольких миллиардов строк со CustomerIDстолбцом внешнего ключа. И предположим, что я хочу запросить «Общий объем продаж в 2014 году по почтовому индексу клиента». Упрощенный DDL выглядит следующим образом (для ясности опущено множество столбцов):

CREATE TABLE Customer
(
    CustomerID int identity (1,1) NOT NULL PRIMARY KEY CLUSTERED, 
    PostalCode varchar(50) NOT NULL,
    SysStartTime datetime2 GENERATED ALWAYS AS ROW START NOT NULL, 
    SysEndTime datetime2 GENERATED ALWAYS AS ROW END NOT NULL,   
    PERIOD FOR SYSTEM_TIME (SysStartTime, SysEndTime) 
)
WITH (SYSTEM_VERSIONING = ON);

CREATE TABLE Sale
(
    SaleId int identity(1,1) NOT NULL PRIMARY KEY CLUSTERED,
    SaleDateTime datetime2 NOT NULL,
    CustomerId int NOT NULL FOREIGN KEY REFERENCES Customer(CustomerID),
    SaleAmount decimal(10,2) NOT NULL
);

Интересно, что клиенты могли переехать в течение года, поэтому у одного и того же клиента могут быть разные почтовые индексы. И даже отдаленно возможно, что клиент отошел, а затем отошел назад, что означает, что может быть несколько записей истории для одного и того же клиента с одним и тем же почтовым индексом! Мой запрос "продажи по почтовому индексу" должен быть в состоянии рассчитать правильные результаты независимо от того, как почтовые индексы клиентов меняются со временем.

Я понимаю, как использовать временные таблицы для запроса только измерения клиента (например SELECT * FROM Customer FOR SYSTEM_TIME FROM '2014-1-1' TO '2015-1-1'), но я не уверен, как наиболее точно и эффективно присоединиться к таблице фактов.

Это как я должен запросить это?

SELECT c.PostalCode, sum(s.SaleAmount) SaleAmount
FROM Customer c FOR SYSTEM_TIME FROM '2014-1-1' TO '2015-1-1'
    JOIN Sale s ON s.CustomerId = c.CustomerId
WHERE s.SaleDateTime >= '2014-1-1' AND s.SaleDateTime < '2015-1-1'
    AND c.SysStartTime >= s.SaleDateTime
    AND c.SysEndTime < s.SaleDateTime
GROUP BY c.PostalCode

И какие соображения производительности следует учитывать при выполнении подобных запросов?

Джастин Грант
источник

Ответы:

1

Я думаю, что в вашем случае производная таблица необходима для выделения запрашиваемого количества мутаций почтовых индексов на одного клиента:

SELECT c.postalcode 
, sum(s.SaleAmount) SaleAmount
, count(postcode_mutations.customerid) as CntCustomerChangedPostCode   
FROM dbo.Sale s
JOIN dbo.Customer c on s.customerid = c.customerid

LEFT JOIN (
SELECT 
    CustomerID
FROM [dbo].[Customer]
FOR SYSTEM_TIME FROM '20140101' TO '20150101'
GROUP BY CustomerID
HAVING COUNT(DISTINCT PostalCode) > 1
) postcode_mutations on s.customerid = postcode_mutations.customerid

WHERE s.SaleDateTime >= '2014-1-1' AND s.SaleDateTime < '2015-1-1'
GROUP BY c.PostalCode

upd: Так как запрос должен обслуживать сценарии DWH / Analytics, индексирование columnstore является опцией для проверки. Ранее я также сделал несколько тестов для таблицы с 10 миллионами строк.

Александр волок
источник
Почему необходимо подсчитывать количество изменений на одного клиента? Клиенты, которые изменяют почтовый индекс в течение года, действительно усложняют запрос, но на самом деле отчеты об этих изменениях не требуются.
Джастин Грант
@JustinGrant Количество изменений показывает, как эти мутации могут быть получены из исторических данных. Однако эти строки вы добавили вчера: мой запрос «Продажи по почтовому индексу» должен быть в состоянии рассчитать правильные результаты независимо от того, как почтовые индексы клиентов меняются со временем. Сделайте запрос более понятным. В этом случае SYSTEM_TIME должен быть установлен одинаково для обеих таблиц. и есть два способа: 1) Использовать лишенные таблицы и применять system_time для обеих таблиц. 2) Или просто создайте представление, которое удерживает соединение, и примените SYSTEM_TIME при запросе представления
Александр Волок