SQL Server 2016:時態表

jopen 10年前發布 | 8K 次閱讀 SQL Server
 

術語“時態數據(temporal data)”是指那些在數據庫中有版本的記錄。任何給定的邏輯記錄都有一個當前版本和零個或多個先前版本。當前版本和任意先前版本在數據庫中都以物理行的形式存在,雖然未必在同一張表中。

使用時態表時要努力保證數據完整性。每次更新一個行,都需要有一種方法可以確保行的當前版本復制到存儲先前版本的表中。這可以通過觸發器或存儲過程實現,但兩種方法都有各自的問題。

同樣,查詢時態數據也是個挑戰。雖然開發人員很容易獲取一條邏輯記錄的當前版本,但查詢特定數據的版本,需要一個復雜而又容易出錯的查詢。這經常導致開發人員寄希望于專門為這種負載類型而設計的數據庫。

SQL Server 2016提供了另外一種選擇——新的 時態表 對象。表面上看,時態表看起來跟普通表一樣。它支持大多數列類型、普通索引、列存儲索引、外鍵等等。CRUD類的操作同使用普通SQL或對象關系映射一樣。實際上,大多數普通表都可以轉換成時態表,而不需要修改使用上述表的存儲過程和應用程序。

從實現上來說,時態表實際上是兩張表。一張表包含當前值,另一張表管理數據的歷史版本。兩張表鏈接在一起,普通表的任何UPDATE或DELETE操作都會自動創建一個相應的歷史行。(INSERT操作不會創建歷史記錄。)

訪問歷史數據

開發人員可以直接查詢歷史表,但由于它不包含當前值,所以不會經常用到它。相反,應該總是使用下面的其中一種操作查詢基表:

  • 時間點: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>)

比如,如果想知道ID為27的客戶在第一年中哪個值是活躍的,可以使用查詢:

… FROM Customer FOR SYSTEM_TIME AS OF '2015-1-1' WHERE CustomerID = 27

如果換個需求,想查看客戶記錄在那天的每個版本,可以使用查詢:

… FROM Customer FOR SYSTEM_TIME BETWEEN '2015-1-1' AND '2015-1-2'WHERE CustomerID = 27

設計原則

  • 時態表需要有一個SysStartTime列和一個SysEndTime列,兩個列均為非空DateTime2類型。這些列可以隨意命名,由SQL Server管理;用戶不能插入或更新這些列的值。
  • 不支持FILESTREAM列類型,因為它在數據庫之外存儲數據。
  • 對于表Foo,歷史表的默認表名為“FooHistory”。該名可以覆寫。
  • 歷史表不能直接修改,只能通過更新或刪除當前表的數據增加它的記錄。
  • 不支持INSTEAD OF觸發器,AFTER觸發器只能用在當前表上。

索引必須手動啟用。關于這一點,微軟給出了一些建議:

為了獲得最優的存儲大小和性能,一個最優的索引策略是,在當前表上創建一個聚簇列存儲索引和/或一個B樹行存儲索引,在歷史表上創建一個聚 簇列存儲索引。如果創建/使用自己的歷史表,那么我們強烈建議創建一個包含當前表主鍵和時間列的索引,以便提升時態數據查詢的速度,以及數據一致性檢查操 作中一部分查詢的速度。如果歷史表是行存儲的,那么我們建議創建一個聚簇行存儲索引。在默認情況下,歷史表上會創建一個聚簇行存儲索引。至少,我們建議創 建一個非聚簇行存儲索引。

模式修改

用戶不能修改時態表的模式。不過,可以在ALTER TABLE語句中使用SET (SYSTEM_VERSIONING = OFF)將時態表轉換成普通表。

這樣做完之后,就可以修改這兩張表,然后使用SET (SYSTEM_VERSIONING = ON)將它們重新轉換成時態表。注意,該語句需要包含歷史表的表名和兩個系統時間列。

更正:本文的上一個版本曾錯誤地將FOR SYSTEM_TIME表達式描述為WHERE子句的一部分,而實際上,它是FROM子句的一部分。

查看英文原文: SQL Server 2016: Temporal Tables

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!