Strategie zapytań przy użyciu tabel czasowych z wersją systemową SQL Server 2016 dla wolno zmieniających się wymiarów

17

W przypadku korzystania z tabeli czasowej z wersją systemową (nowość w SQL Server 2016), jakie są skutki tworzenia zapytań i wydajności, gdy ta funkcja jest używana do obsługi powolnej zmiany wymiarów w dużej relacyjnej hurtowni danych?

Załóżmy na przykład, że mam Customerwymiar 100 000 wierszy z Postal Codekolumną i Salestabelę faktów zawierającą wiele miliardów wierszy z CustomerIDkolumną klucza obcego. I załóżmy, że chcę zapytać „Sprzedaż ogółem 2014 według kodu pocztowego klienta”. Uproszczony DDL wygląda tak (z pominięciem wielu kolumn dla przejrzystości):

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

Ciekawe jest to, że klienci mogli się przeprowadzić w ciągu roku, więc ten sam klient może mieć różne kody pocztowe. Jest nawet możliwe, że klient przeprowadził się, a następnie wycofał, co oznacza, że ​​może istnieć wiele rekordów historii dla tego samego klienta z tym samym kodem pocztowym! Moje zapytanie dotyczące „sprzedaży według kodu pocztowego” powinno być w stanie obliczyć prawidłowe wyniki bez względu na to, jak zmieniają się z czasem kody pocztowe klientów.

Rozumiem, jak korzystać z tabel czasowych do zapytania samego wymiaru klienta (np. SELECT * FROM Customer FOR SYSTEM_TIME FROM '2014-1-1' TO '2015-1-1'), Ale nie jestem pewien, jak najdokładniej i najskuteczniej dołączyć do tabeli faktów.

Czy w ten sposób powinienem zapytać?

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

A jakie są uwagi dotyczące wydajności, na które powinienem uważać, gdy zadajemy takie zapytania?

Justin Grant
źródło

Odpowiedzi:

1

Myślę, że w twoim przypadku potrzebna jest tabela pochodna do wyizolowania kwerendy liczby mutacji kodów pocztowych na klienta:

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: Ponieważ zapytanie ma obsługiwać scenariusze DWH / Analytics, indeksowanie magazynu kolumn jest opcją do sprawdzenia. Wcześniej dokonałem również testów porównawczych dla tabeli 10 milionów wierszy.

Alexandr Volok
źródło
Dlaczego trzeba liczyć liczbę zmian na klienta? Klienci, którzy zmieniają kod pocztowy w ciągu roku, zwiększają złożoność zapytania, ale w rzeczywistości raportowanie tych zmian nie jest wymagane.
Justin Grant
@JustinGrant Liczba zmian ma pokazać, w jaki sposób mutacje te można odzyskać z danych historycznych. Jednak te wiersze, które dodałeś wczoraj: Moje zapytanie „sprzedaż według kodu pocztowego” powinno być w stanie obliczyć prawidłowe wyniki bez względu na to, jak zmieniają się kody pocztowe klientów w czasie. Wyjaśnij żądanie. W takim przypadku SYSTEM_TIME należy ustawić w ten sam sposób dla obu tabel. i są dwa sposoby: 1) Użyj pozbawionych tabel i zastosuj system_time dla obu tabel. 2) Lub po prostu stwórz widok, który zawiera sprzężenie i zastosuj SYSTEM_TIME przy zapytaniu o widok
Alexandr Volok