Estratégias de consulta usando tabelas temporais com versão do sistema do SQL Server 2016 para dimensões de alteração lenta

17

Ao usar uma tabela temporal com versão do sistema (nova no SQL Server 2016), quais são as implicações de autoria e desempenho da consulta quando esse recurso é usado para lidar com dimensões de alteração lenta em um grande armazém de dados relacionais?

Por exemplo, suponha que eu tenha uma Customerdimensão de 100.000 linhas com uma Postal Codecoluna e uma Salestabela de fatos com vários bilhões de linhas com uma CustomerIDcoluna de chave estrangeira. E suponha que eu queira consultar "Total de vendas em 2014 por código postal do cliente". O DDL simplificado é assim (omitindo muitas colunas para maior clareza):

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
);

O que interessa é que os clientes podem ter se mudado durante o ano e, portanto, o mesmo cliente pode ter códigos postais diferentes. E é até remotamente possível que um cliente se afaste e depois volte, o que significa que pode haver vários registros de histórico para o mesmo cliente com o mesmo código postal! Minha consulta de "vendas por código postal" deve poder calcular resultados corretos, independentemente de como os códigos postais dos clientes mudam com o tempo.

Entendo como usar tabelas temporais para consultar apenas a dimensão do cliente (por exemplo SELECT * FROM Customer FOR SYSTEM_TIME FROM '2014-1-1' TO '2015-1-1'), mas não sei como ingressar com mais precisão e eficiência na tabela de fatos.

É assim que eu devo consultá-lo?

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

E quais são as considerações de desempenho que devo observar ao fazer consultas como essa?

Justin Grant
fonte

Respostas:

1

Penso que, no seu caso, é necessária uma tabela derivada para isolar o número de consultas de mutações de códigos postais por cliente:

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: Como a consulta deve servir para cenários DWH / Analytics, a indexação columnstore é uma opção a ser verificada. Também fiz alguns benchmarks anteriormente para uma tabela de 10 milhões de linhas.

Alexandr Volok
fonte
Por que é necessário contar o número de alterações por cliente? Os clientes que alteram o código postal durante o ano aumentam a complexidade da consulta, mas, na verdade, parece não ser necessário informar sobre essas alterações.
23615 Justin Grant
@JustinGrant O número de alterações é mostrar como essas mutações podem ser recuperadas dos dados históricos. No entanto, você adicionou estas linhas ontem: Minha consulta de "vendas por código postal" deve ser capaz de calcular resultados corretos, independentemente de como os códigos postais dos clientes mudam com o tempo. Tornar a solicitação mais clara. Nesse caso, SYSTEM_TIME deve ser definido da mesma maneira para as duas tabelas. e há duas maneiras: 1) Use tabelas privadas e aplique system_time para ambas as tabelas. 2) Ou simplesmente criar uma visão que detêm uma junção e aplicar SYSTEM_TIME em consultando a visão
Alexandr Volok