Lorsque vous utilisez un - Table temporelle de la version à la version (Nouveau dans SQL Server 2016), quelles sont les implications de la création de requêtes et des performances lorsque cette fonctionnalité est utilisée pour gérer les dimensions changeantes lentement dans un grand entrepôt de données relationnel?
Par exemple, supposons que j'ai une dimension de 100 000 rangées Customer
avec un Postal Code
colonne et une rangée de plusieurs milliards Sales
table de fait avec une colonne clé étrangère CustomerID
. Et suppose que je veux interroger "Total des ventes de 2014 par code postal du client". DDL simplifié est comme ceci (omettant de nombreuses colonnes de clarté):
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
);
Là où il est intéressant, c'est que les clients peuvent avoir déplacé au cours de l'année afin que le même client puisse avoir différents codes postaux. Et il est même possible à distance qu'un client s'est éloigné puis replacé, ce qui signifie qu'il pourrait y avoir plusieurs enregistrements d'historique pour le même client avec le même code postal! Ma requête de "Ventes par code postal" devrait être en mesure de calculer les résultats corrects, quels que soient les codes postaux des clients au fil du temps.
Je comprends comment utiliser des tables temporelles pour interroger la dimension du client seul (par exemple, SELECT * FROM Customer FOR SYSTEM_TIME FROM '2014-1-1' TO '2015-1-1'
) Mais je ne suis pas sûr de savoir comment joindre le plus avec précision et efficacement à la table des faits.
Est-ce comment je devrais la questionner?
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
Et quelles sont les considérations de performance que je devrais faire attention lorsque vous faites des questions comme celle-ci?
Je pense que dans votre cas une table dérivée est nécessaire pour isoler le nombre de mutations de codes postaux par client:
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: Étant donné que la requête est censée servir des scénarios DWH/Analytics, l'indexation de colonne est une option de contrôle. J'ai aussi fait des points de repère précédemment pour une table de 10 millions de rangées.