共用方式為


儲存 JSON 文件

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

SQL 資料庫引擎提供原生 JSON 函數,可讓您使用標準 SQL 語言剖析 JSON 文件。 您可以在 SQL Server 或 SQL Database 中儲存 JSON 文件及查詢 JSON 資料,如同在 NoSQL 資料庫中一樣。 本文描述儲存 JSON 文件的選項。

JSON 儲存體格式

第一個儲存體設計決策是如何將 JSON 文件儲存在資料表中。 有兩個可用的選項:

  • LOB 儲存體 - JSON 文件可以依現況儲存於資料行,其資料類型為 jsonnvarchar。 這是快速載入資料和內嵌的最佳方式,因為載入速度與字串資料行的載入速度相符。 如果未針對 JSON 值編製索引,這種方法可能會對查詢/分析時間造成額外的效能影響,因為原始 JSON 文件必須在查詢執行時進行剖析。

  • 關聯式儲存體 - JSON 文件可以在使用 OPENJSONJSON_VALUEJSON_QUERY 函式插入資料表時進行剖析。 輸入 JSON 文件的片段,可以儲存在包含 JSON 子元素且資料類型為 jsonnvarchar 的資料行。 這種方法會增加載入時間,因為載入期間要進行 JSON 剖析;不過,查詢效能與關聯式資料的傳統查詢效能相符。

  • JSON 數據類型目前為 Azure SQL 資料庫 和 Azure SQL 受控執行個體 預覽版(使用 Always-up-to-date 更新原則設定)。

  • 目前在 SQL Server 中,JSON 並非內建資料類型。

傳統的資料表

將 JSON 文件儲存在 SQL Server 或 Azure SQL 資料庫的最簡單方式是建立雙資料行資料表,其中包含文件的識別碼和文件的內容。 例如:

create table WebSite.Logs (
    [_id] bigint primary key identity,
    [log] nvarchar(max)
);

或者,如果支援:

create table WebSite.Logs (
    [_id] bigint primary key identity,
    [log] json
);

這個結構相等於您可以在傳統文件資料庫中找到的集合。 主索引鍵 _id 是一個自動遞增的值,它為每份文件提供唯一的識別碼,而且可供快速查閱。 此結構對於您要依識別碼擷取文件或依識別碼更新儲存文件的傳統 NoSQL 案例而言是不錯的選擇。

  • 使用可用於儲存 JSON 文件的原生 json 資料類型。
  • nvarchar(max) 資料類型可讓您儲存大小高達 2 GB 的 JSON 文件。 不過如果您確定 JSON 文件不大於 8 KB,則基於效能考量,我們建議您使用 nvarchar(4000),不要使用 nvarchar(max)

在上述範例中建立的範例資料表假設,有效的 JSON 文件會儲存在 log 資料行。 如果您想要確定有效的 JSON 會儲存在 log 資料行,您可以新增資料行的 CHECK 條件約束。 例如:

ALTER TABLE WebSite.Logs
    ADD CONSTRAINT [Log record should be formatted as JSON]
                   CHECK (ISJSON([log])=1)

每次有人插入或更新資料表中的文件時,這個條件約束會驗證 JSON 文件已正確格式化。 如果沒有條件約束,資料表最適合插入,因為任何 JSON 文件會直接新增到資料行,而不進行任何處理。

當您將 JSON 文件儲存到資料表中時,您可以使用標準的 Transact-SQL 語言來查詢文件。 例如:

SELECT TOP 100 JSON_VALUE([log], '$.severity'), AVG( CAST( JSON_VALUE([log],'$.duration') as float))
 FROM WebSite.Logs
 WHERE CAST( JSON_VALUE([log],'$.date') as datetime) > @datetime
 GROUP BY JSON_VALUE([log], '$.severity')
 HAVING AVG( CAST( JSON_VALUE([log],'$.duration') as float) ) > 100
 ORDER BY AVG( CAST( JSON_VALUE([log],'$.duration') as float) ) DESC

有一項強大的優點,就是您可以使用「任何」T-SQL 函式和查詢子句來查詢 JSON 文件。 SQL Server 和 SQL Database 不會導入查詢中您可用來分析 JSON 文件的任何條件約束。 您可以使用 JSON_VALUE 函式從 JSON 文件擷取值,並像任何其他值將其用於查詢。

可使用豐富 T-SQL 查詢語法這項功能是 SQL Server 和 SQL Database 與傳統 NoSQL 資料庫之間的主要差異;在 Transact-SQL 中,您可能有處理 JSON 資料所需的任何函式。

索引

如果您發現您的查詢經常以某個屬性來搜尋文件 (例如,JSON 文件中的 severity 屬性),您可以在屬性上新增資料列存放區非叢集索引索引以加速查詢。

您可以建立計算資料行,從指定的路徑 (也就是在路徑 $.severity) 上的 JSON 資料行公開 JSON 值,並在這個計算資料行上建立標準索引。 例如:

create table WebSite.Logs (
    [_id] bigint primary key identity,
    [log] nvarchar(max),
    [severity] AS JSON_VALUE([log], '$.severity'),
    index ix_severity (severity)
);

此範例中使用的計算資料行,是不會將額外空間新增至資料表的非持續性或虛擬資料行。 它由索引 ix_severity 用來改善查詢的效能,如下列範例所示:

SELECT [log]
FROM Website.Logs
WHERE JSON_VALUE([log], '$.severity') = 'P4'

此索引的一項重要特性是感知定序。 如果原始的 nvarchar 資料行有 COLLATION 屬性 (例如,區分大小寫或日文),則索引組織會根據語言規則或與 NVARCHAR 資料行建立關聯的區分大小寫規則。 如果您正在為需要使用自訂語言規則來處理 JSON 文件的全球市場開發應用程式,這個感知定序可能是一項重要功能。

大型資料表和資料行存放區格式

如果您預期在集合中有大量的 JSON 文件,我們建議在集合上新增叢集資料行存放區索引,如下列範例所示:

create sequence WebSite.LogID as bigint;
go
create table WebSite.Logs (
    [_id] bigint default(next value for WebSite.LogID),
    [log] nvarchar(max),
    INDEX cci CLUSTERED COLUMNSTORE
);

叢集資料行存放區索引能提供高資料壓縮 (最多 25 倍),大幅減少您的儲存空間需求、降低儲存體的成本,以及提升您的工作負載 I/O 效能。 此外,叢集資料行存放區索引針對您 JSON 文件上的資料表掃描和分析而最佳化,因此這種類型的索引可能是記錄分析的最佳選項。

上述範例中使用序列物件將值指派給 _id 資料行。 序列和身分識別都是識別碼資料行的有效選項。

經常變更的文件和經記憶體最佳化的資料表

如果您預期在集合中會有大量的更新、插入和刪除作業,您可以在記憶體最佳化資料表中儲存 JSON 文件。 記憶體最佳化的 JSON 集合一律會將資料保存在記憶體中,因此沒有儲存體 I/O 額外負荷。 此外,記憶體最佳化 JSON 集合完全無鎖定,也就是對文件採取的動作不會封鎖任何其他作業。

要將傳統集合轉換成記憶體最佳化的集合,您唯一要做的是在資料表定義之後指定 WITH (MEMORY_OPTIMIZED=ON) 選項,如下列範例所示。 然後,您便有記憶體最佳化版本的 JSON 集合。

CREATE TABLE WebSite.Logs (
  [_id] bigint IDENTITY PRIMARY KEY NONCLUSTERED,
  [log] nvarchar(max)
) WITH (MEMORY_OPTIMIZED=ON)

記憶體最佳化的資料表是最適合經常變更之文件的選項。 當您考慮記憶體最佳化資料表時,也請考慮效能。 可能的話,請針對記憶體最佳化集合中的 JSON 文件使用 nvarchar(4000),不使用 nvarchar(max),因為它可能會大幅提升效能。 記憶體最佳化的資料表不支援 json 資料類型。

如同傳統的資料表,您可以對使用計算資料行,公開在記憶體最佳化資料表中的欄位,新增索引。 例如:

CREATE TABLE WebSite.Logs (

  [_id] bigint IDENTITY PRIMARY KEY NONCLUSTERED,
  [log] nvarchar(max),

  [severity] AS cast(JSON_VALUE([log], '$.severity') as tinyint) persisted,
  INDEX ix_severity (severity)

) WITH (MEMORY_OPTIMIZED=ON)

為了充分發揮效能,將 JSON 值轉換成可用來保存屬性值的最小可能類型。 在上述範例中,使用了 tinyint

您也可以將更新 JSON 文件的 SQL 查詢,放在預存程序中以善加利用原生編譯。 例如:

CREATE PROCEDURE WebSite.UpdateData(@Id int, @Property nvarchar(100), @Value nvarchar(100))
WITH SCHEMABINDING, NATIVE_COMPILATION

AS BEGIN
    ATOMIC WITH (transaction isolation level = snapshot,  language = N'English')

    UPDATE WebSite.Logs
    SET [log] = JSON_MODIFY([log], @Property, @Value)
    WHERE _id = @Id;

END

此原生編譯的程序會採用查詢,並建立執行查詢的 .DLL 程式碼。 原生編譯的程序是能更快速查詢和更新資料的方法。

結論

SQL Server 和 SQL Database 中的原生 JSON 函式,讓您能像在 NoSQL 資料庫中一樣地處理 JSON 文件。 每個資料庫不論是關聯式還是 NoSQL,都有一些 JSON 資料處理方面的優缺點。 將 JSON 文件儲存在 SQL Server 或 SQL Database 的主要優點是 SQL 語言的完整支援。 您可以使用豐富的 Transact-SQL 語言來處理資料,以及設定各種不同的儲存體選項 (從高壓縮和快速分析用的資料行存放區索引,到無鎖定處理用的經記憶體最佳化的資料表)。 同時,您會受益於成熟的安全性和國際化功能,並且可以輕鬆地重複用於 NoSQL 案例中。 本文中所描述的理由是考慮將 JSON 文件儲存在 SQL Server 或 SQL Database 中的絕佳原因。

深入了解 SQL Server 和 Azure SQL Database 中的 JSON

如需 SQL Server 和 Azure SQL Database 中內建 JSON 支援的觀看式簡介,請參閱下列影片: