查詢系統時態版本表中的資料

適用於: SQL Server 2016 (13.x) 和更新版本 Azure SQL DatabaseAzure SQL 受控執行個體Microsoft Fabric 中的 SQL 資料庫

要取得時間表中資料的最新(目前)狀態,查詢方式與非時間表相同。 如果 PERIOD 資料行未隱藏,其值會出現在 SELECT * 查詢中。 如果你將 PERIOD 欄位指定為 HIDDEN,這些欄位的值就不會出現在 SELECT * 查詢中。 當欄位 PERIOD 被隱藏時,請在子 SELECT 句中特別引用它們以回傳其值。

若要執行基於時間的分析,使用帶有四個時間特定子句的 FOR SYSTEM_TIME 子句來查詢當前與歷史資料表的資料。 如需有關這些子句的詳細資訊,請參閱<時間表格>和<FROM 子句以及 JOIN、APPLY、PIVOT

  • AS OF <date_time>
  • FROM <start_date_time> TO <end_date_time>
  • BETWEEN <start_date_time> AND <end_date_time>
  • CONTAINED IN (<start_date_time>, <end_date_time>)
  • ALL

你可以在查詢中分別為每個資料表指定 FOR SYSTEM_TIME 。 可將其用於常見的資料表表達式、資料表值函式及儲存程序中。 當您搭配時間資料表使用資料表別名時,請在時間資料表名稱與別名之間加入 FOR SYSTEM_TIME 子句(請參閱 使用 AS OF 子子句查詢特定時間 的第二個範例)。

使用 AS OF 次子句查詢特定的時間

使用 AS OF 子條款來重建資料在過去任一特定時間點的狀態。 你可以用欄位定義中PERIOD指定的 datetime2 類型精確度重建資料。

使用 AS OF 帶有常數字量或變數的子子句來動態指定時間條件。 你提供的數值會被解讀為UTC時間。

第一個範例會回傳過去某個特定日期時的 dbo.Department資料表AS OF 狀態。

-- State of entire table AS OF specific date in the past
SELECT [DeptID],
       [DeptName],
       [ValidFrom],
       [ValidTo]
FROM [dbo].[Department] FOR SYSTEM_TIME
    AS OF '2021-09-01 T10:00:00.7230011';

第二個範例針對資料列子集比較兩個時間點之間的值。

DECLARE @ADayAgo AS DATETIME2;

SET @ADayAgo = DATEADD(DAY, -1, SYSUTCDATETIME());

-- Comparison between two points in time for subset of rows
SELECT D_1_Ago.DeptID,
       d.DeptID,
       D_1_Ago.DeptName,
       d.DeptName,
       D_1_Ago.ValidFrom,
       d.ValidFrom,
       D_1_Ago.ValidTo,
       d.ValidTo
FROM dbo.Department FOR SYSTEM_TIME
    AS OF @ADayAgo AS D_1_Ago
    INNER JOIN Department AS d
         ON D_1_Ago.DeptID = d.DeptID
        AND D_1_Ago.DeptID BETWEEN 1 AND 5;

在時間查詢中使用包含 AS OF 次子句的檢視

當你需要進行複雜的特定時間點分析時,視圖會很有用。 一個常見的例子是今天產生包含上個月數值的商業報告。

通常,客戶會擁有一個正規化的資料庫模型,其中包含許多擁有外鍵關係的資料表。 要找出那個正規化模型過去某個時候的資料呈現的樣貌可能很有挑戰性,因為所有表格都會獨立地以自己的節奏變化。

在此情況下,最好的選擇是建立檢視,並套用 AS OF 次子句到整個檢視。 此方法將資料存取層的建模與點時分析分離,因為 SQL Server 會透明地將該AS OF子句套用到所有參與視圖定義的時序資料表。 此外,您可以在相同的檢視中將時態表與非時態表結合,而 AS OF 只會套用至時態表。 如果檢視未參考至少一個時態表,則套用時態性查詢子句至檢視會失敗,並產生錯誤。

下列範例程式碼會建立加入三個時態表的檢視:DepartmentCompanyLocationLocationDepartments

CREATE VIEW [dbo].[vw_GetOrgChart]
AS
SELECT [CompanyLocation].LocID,
       [CompanyLocation].LocName,
       [CompanyLocation].City,
       [Department].DeptID,
       [Department].DeptName
FROM [dbo].[CompanyLocation]
    LEFT OUTER JOIN [dbo].[LocationDepartments]
        ON [CompanyLocation].LocID = LocationDepartments.LocID
    LEFT OUTER JOIN [dbo].[Department]
        ON LocationDepartments.DeptID = [Department].DeptID;
GO

您可以使用 AS OF 次子句和 datetime2 常值來查詢檢視:

/* Query view AS OF */
SELECT *
FROM [vw_GetOrgChart] FOR SYSTEM_TIME
    AS OF '2021-09-01 T10:00:00.7230011';

查詢特定資料列的時間變更

時間子句 FROM ... TOBETWEEN ... ANDCONTAINED IN 在您需要取得目前資料表中特定資料列的所有歷史變更時非常有用(也稱為資料稽核)。

前兩個子句回傳與特定期間重疊的列版本(即在該期間之前開始、結束於該期間的版本),而 CONTAINED IN 僅回傳存在於指定期間範圍內的版本。

如果你只搜尋非目前的列版本,請直接查詢歷史表以獲得最佳查詢效能。 可自由 ALL 查詢當前及過去資料,無需任何限制。

/* Query using BETWEEN...AND sub-clause*/
SELECT [DeptID],
       [DeptName],
       [ValidFrom],
       [ValidTo],
       IIF (YEAR(ValidTo) = 9999, 1, 0) AS IsActual
FROM [dbo].[Department] FOR SYSTEM_TIME
    BETWEEN '2021-01-01' AND '2021-12-31'
WHERE DeptId = 1
ORDER BY ValidFrom DESC;

/* Query using CONTAINED IN sub-clause */
SELECT [DeptID],
       [DeptName],
       [ValidFrom],
       [ValidTo]
FROM [dbo].[Department] FOR SYSTEM_TIME
    CONTAINED IN ('2021-04-01', '2021-09-25')
WHERE DeptId = 1
ORDER BY ValidFrom DESC;

/* Query using ALL sub-clause */
SELECT [DeptID],
       [DeptName],
       [ValidFrom],
       [ValidTo],
       IIF (YEAR(ValidTo) = 9999, 1, 0) AS IsActual
FROM [dbo].[Department] FOR SYSTEM_TIME ALL
ORDER BY [DeptID], [ValidFrom] DESC;

要總結資料在某一時期的表現,結合FOR SYSTEM_TIME BETWEEN ... ANDGROUP BY並彙總函數。 使用此技術用於描述性統計與趨勢分析。 它會彙整在該時間窗口內處於有效狀態的每個資料列版本,而不是重建某個單一時間點的狀態。

以下範例計算員工薪資在某一時間窗口內的描述性統計,並依部門分組。 它使用在 時態表時態表使用案例中定義的 Employee 系統版本控制資料表。 欄位 AnnualSalary 是一個數值測度,非常適合用於 AVGMINMAXSTDEV 函數。

FOR SYSTEM_TIME BETWEEN ... AND 子句會回傳每一個與該時期重疊的行版本。 因此,總量包含了該期間生效的所有薪資。 當員工薪資變動時,查詢會回傳該員工的多個版本,每個版本都會貢獻到彙整資料中。

DECLARE @periodStart AS DATETIME2 = '2021-01-01';
DECLARE @periodEnd AS DATETIME2 = '2021-12-31';

SELECT [Department],
       AVG([AnnualSalary]) AS AvgSalary,
       MIN([AnnualSalary]) AS MinSalary,
       MAX([AnnualSalary]) AS MaxSalary,
       STDEV([AnnualSalary]) AS SalaryStdDev,
       COUNT(*) AS RowVersions
FROM [dbo].[Employee] FOR SYSTEM_TIME
    BETWEEN @periodStart AND @periodEnd
GROUP BY [Department]
ORDER BY AvgSalary DESC;