Cuando se usa una tabla temporal versionada por el sistema (nueva en SQL Server 2016), ¿cuáles son las implicaciones de la creación de consultas y el rendimiento cuando esta característica se usa para manejar Dimensiones que cambian lentamente en un gran almacén de datos relacionales?
Por ejemplo, suponga que tengo una Customer
dimensión de 100.000 filas con una Postal Code
columna y una Sales
tabla de hechos de miles de millones de filas con una CustomerID
columna de clave externa. Y suponga que quiero consultar "Total de ventas de 2014 por código postal del cliente". El DDL simplificado es así (omitiendo muchas columnas para mayor claridad):
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
);
Lo que resulta interesante es que los clientes pueden haberse mudado durante el año, por lo que el mismo cliente puede tener diferentes códigos postales. ¡Y es incluso remotamente posible que un cliente se haya alejado y luego haya regresado, lo que significa que podría haber múltiples registros históricos para el mismo cliente con el mismo código postal! Mi consulta de "ventas por código postal" debería poder calcular los resultados correctos independientemente de cómo cambien los códigos postales de los clientes con el tiempo.
Entiendo cómo usar tablas temporales para consultar solo la dimensión del cliente (por ejemplo SELECT * FROM Customer FOR SYSTEM_TIME FROM '2014-1-1' TO '2015-1-1'
), pero no estoy seguro de cómo unirme de manera más precisa y eficiente a la tabla de hechos.
¿Es así como debería consultarlo?
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
¿Y cuáles son las consideraciones de rendimiento que debo tener en cuenta al hacer consultas como esta?