SQL Server 2017 JSON



Microsoftが長幎にわたっお極端から極端ぞず熱狂しおいたずき、少しず぀あなたはそれに慣れ始め、特定の懐疑心を持っお新しいものを埅っおいたす。 時間が経぀に぀れお、この感情は匷くなり、無意識のうちに良いものは期埅したせん。

しかし、時にはすべおが正反察になるこずがありたす。 マむクロ゜フトは 、すべおの確立された人生のステレオタむプを打ち砎る完党に機胜する機胜を箱から出しおいたす。 あなたは新しい機胜から次のレヌキを期埅しおいたすが、毎分、これがたさにあなたが長幎欠いおいたものであるこずにたすたす気づきたす。

長い間、 Microsoft ConnectでSQL Server䞊のJSONを操䜜するサポヌトが最も䞀般的な機胜の1぀であったため、このパトスの導入にはいく぀かの理由がありたす。 数幎が経ち、予想倖に、この機胜はSQL Server 2016のリリヌスず共に実装されたした。 将来的には非垞にうたくいったず蚀いたすが、 Microsoftはそこで止たらず、 SQL Server 2017では既に高速なJSONパヌサヌのパフォヌマンスを倧幅に改善したした。

内容


1. デヌタ型
2. ストレヌゞ
3. 圧瞮/解凍
4. 圧瞮
5. ColumnStore
6. JSONを䜜成する
7. JSONを確認する
8. JsonValue
9. OpenJson
10. 文字列の分割
11. 緩い
12. 倉曎
13. 暗黙の倉換
14. むンデックス
15. パヌサヌのパフォヌマンス
ビデオ

1.デヌタ型


SQL Serverでの JSONサポヌトは、最初はすべおの゚ディションで利甚できたす。 同時に、 XMLの堎合のように、 Microsoftは個別のデヌタ型を提䟛したせんでした。 SQL Server䞊のJSONのデヌタはプレヌンテキストずしお保存されたす Unicode  NVARCHAR / NCHAR たたはANSI  VARCHAR / CHAR 圢匏。

DECLARE @JSON_ANSI VARCHAR(MAX) = '[{"Nąme":"Lenōvo モデ460"}]' , @JSON_Unicode NVARCHAR(MAX) = N'[{"Nąme":"Lenōvo モデ460"}]' SELECT DATALENGTH(@JSON_ANSI), @JSON_ANSI UNION ALL SELECT DATALENGTH(@JSON_Unicode), @JSON_Unicode 

留意すべき䞻なこずは、このデヌタ型たたはそのデヌタ型がどれだけのスペヌスを䜿甚するかです Unicodeずしおデヌタを保存する堎合は1文字あたり2バむト、 ANSI文字列の堎合は1バむト。 たた、 Unicode定数の前に「 N 」を眮くこずを忘れないでください。 そうでなければ、あなたはたくさんの楜しい状況に出くわすこずができたす

 --- ---------------------------- 25 [{"Name":"Lenovo ??460"}] 50 [{"Nąme":"Lenōvo モデ460"}] 

すべおがシンプルに思えたすが、違いたす。 さらに、遞択したデヌタ型がサむズだけでなく、解析速床にも圱響するこずがわかりたす。

さらに、 非掚奚のデヌタ型-NTEXT / TEXTを䜿甚しないこずを匷くお勧めしたす。 習慣のためにただそれらを䜿甚する人のために、私たちは小さな調査実隓を行いたす

 DROP TABLE IF EXISTS #varchar DROP TABLE IF EXISTS #nvarchar DROP TABLE IF EXISTS #ntext GO CREATE TABLE #varchar (x VARCHAR(MAX)) CREATE TABLE #nvarchar (x NVARCHAR(MAX)) CREATE TABLE #ntext (x NTEXT) GO DECLARE @json NVARCHAR(MAX) = N'[{"Manufacturer":"Lenovo","Model":"ThinkPad E460","Availability":1}]' SET STATISTICS IO, TIME ON INSERT INTO #varchar SELECT TOP(50000) @json FROM [master].dbo.spt_values s1 CROSS JOIN [master].dbo.spt_values s2 OPTION(MAXDOP 1) INSERT INTO #nvarchar SELECT TOP(50000) @json FROM [master].dbo.spt_values s1 CROSS JOIN [master].dbo.spt_values s2 OPTION(MAXDOP 1) INSERT INTO #ntext SELECT TOP(50000) @json FROM [master].dbo.spt_values s1 CROSS JOIN [master].dbo.spt_values s2 OPTION(MAXDOP 1) SET STATISTICS IO, TIME OFF 

埌者の堎合の挿入速床は倧きく異なりたす。

 varchar: CPU time = 32 ms, elapsed time = 28 ms nvarchar: CPU time = 31 ms, elapsed time = 30 ms ntext: CPU time = 172 ms, elapsed time = 190 ms 

たた、 NTEXT / TEXTは垞にLOBペヌゞに保存されるこずに泚意しおください。

 SELECT obj_name = OBJECT_NAME(p.[object_id]) , a.[type_desc] , a.total_pages , total_mb = a.total_pages * 8 / 1024. FROM sys.allocation_units a JOIN sys.partitions p ON p.[partition_id] = a.container_id WHERE p.[object_id] IN ( OBJECT_ID('#nvarchar'), OBJECT_ID('#ntext'), OBJECT_ID('#varchar') ) 

 obj_name type_desc total_pages total_mb ------------- -------------- ------------ ----------- varchar IN_ROW_DATA 516 4.031250 varchar LOB_DATA 0 0.000000 nvarchar IN_ROW_DATA 932 7.281250 nvarchar LOB_DATA 0 0.000000 ntext IN_ROW_DATA 188 1.468750 ntext LOB_DATA 1668 13.031250 

参照甚に、可倉長タむプのSQL Server 2005から、「デヌタを保存するペヌゞ」ルヌルが倉曎されたした。 䞀般に、サむズが8060バむトを超える堎合、デヌタはLOBペヌゞに配眮され、そうでない堎合はIN_ROWに栌玍されたす 。 この堎合、 SQL Serverがペヌゞ䞊のデヌタのストレヌゞを最適化するこずは明らかです。

そしお、 NTEXT / TEXTを䜿甚しない最埌の理由は、非掚奚のデヌタ型を含むすべおのJSON関数が面癜くないずいう事実です。

 SELECT TOP(1) 1 FROM #ntext WHERE ISJSON(x) = 1 

 Msg 8116, Level 16, State 1, Line 63 Argument data type ntext is invalid for argument 1 of isjson function. 

2.ストレヌゞ


XMLずしお提瀺される同様のデヌタず比范しお、 JSONをNVARCHAR / VARCHARずしお保存するこずの利点を芋おみたしょう。 さらに、 XMLをネむティブ圢匏で保存し、文字列ずしお衚瀺しようずしたす。

 DECLARE @XML_Unicode NVARCHAR(MAX) = N' <Manufacturer Name="Lenovo"> <Product Name="ThinkPad E460"> <Model Name="20ETS03100"> <CPU>i7-6500U</CPU> <Memory>16</Memory> <SSD>256</SSD> </Model> <Model Name="20ETS02W00"> <CPU>i5-6200U</CPU> <Memory>8</Memory> <HDD>1000</HDD> </Model> <Model Name="20ETS02V00"> <CPU>i5-6200U</CPU> <Memory>4</Memory> <HDD>500</HDD> </Model> </Product> </Manufacturer>' DECLARE @JSON_Unicode NVARCHAR(MAX) = N' [ { "Manufacturer": { "Name": "Lenovo", "Product": { "Name": "ThinkPad E460", "Model": [ { "Name": "20ETS03100", "CPU": "Intel Core i7-6500U", "Memory": 16, "SSD": "256" }, { "Name": "20ETS02W00", "CPU": "Intel Core i5-6200U", "Memory": 8, "HDD": "1000" }, { "Name": "20ETS02V00", "CPU": "Intel Core i5-6200U", "Memory": 4, "HDD": "500" } ] } } } ]' DECLARE @XML_Unicode_D NVARCHAR(MAX) = N'<Manufacturer Name="Lenovo"><Product Name="ThinkPad E460"><Model Name="20ETS03100"><CPU>i7-6500U</CPU><Memory>16</Memory><SSD>256</SSD></Model><Model Name="20ETS02W00"><CPU>i5-6200U</CPU><Memory>8</Memory><HDD>1000</HDD></Model><Model Name="20ETS02V00"><CPU>i5-6200U</CPU><Memory>4</Memory><HDD>500</HDD></Model></Product></Manufacturer>' , @JSON_Unicode_D NVARCHAR(MAX) = N'[{"Manufacturer":{"Name":"Lenovo","Product":{"Name":"ThinkPad E460","Model":[{"Name":"20ETS03100","CPU":"Intel Core i7-6500U","Memory":16,"SSD":"256"},{"Name":"20ETS02W00","CPU":"Intel Core i5-6200U","Memory":8,"HDD":"1000"},{"Name":"20ETS02V00","CPU":"Intel Core i5-6200U","Memory":4,"HDD":"500"}]}}}]' DECLARE @XML XML = @XML_Unicode , @XML_ANSI VARCHAR(MAX) = @XML_Unicode , @XML_D XML = @XML_Unicode_D , @XML_ANSI_D VARCHAR(MAX) = @XML_Unicode_D , @JSON_ANSI VARCHAR(MAX) = @JSON_Unicode , @JSON_ANSI_D VARCHAR(MAX) = @JSON_Unicode_D SELECT * FROM ( VALUES ('XML Unicode', DATALENGTH(@XML_Unicode), DATALENGTH(@XML_Unicode_D)) , ('XML ANSI', DATALENGTH(@XML_ANSI), DATALENGTH(@XML_ANSI_D)) , ('XML', DATALENGTH(@XML), DATALENGTH(@XML_D)) , ('JSON Unicode', DATALENGTH(@JSON_Unicode), DATALENGTH(@JSON_Unicode_D)) , ('JSON ANSI', DATALENGTH(@JSON_ANSI), DATALENGTH(@JSON_ANSI_D)) ) t(DataType, Delimeters, NoDelimeters) 

実行するず、次の結果が埗られたす。

 DataType Delimeters NoDelimeters ------------ ----------- -------------- XML Unicode 914 674 XML ANSI 457 337 XML 398 398 JSON Unicode 1274 604 JSON ANSI 637 302 

最適なオプションはネむティブXMLのように思えるかもしれたせん。 これは郚分的には真実ですが、埮劙な違いがありたす。 XMLは垞にUnicodeずしお保存されたす 。 さらに、 SQL Serverはこのデヌタの保存にバむナリ圢匏を䜿甚しおいるため、すべおがポむンタヌを䜿甚しお暙準化された蟞曞に圧瞮されたす。 そのため、XML内の曞匏蚭定はデヌタの最終サむズに圱響したせん。

文字列では、すべおが異なるため、フォヌマットされたJSONを保存するこずはお勧めしたせん。 最良のオプションは、クラむアントで既にオンデマンドでデヌタを保存およびフォヌマットするずきに䜙分な文字をすべお切り取るこずです。

JSONデヌタのサむズをさらに削枛したい堎合は、いく぀かのオプションがありたす。

3.圧瞮/解凍


SQL Server 2016では、 GZIP圧瞮のサポヌトを远加する新しいCOMPRESS / DECOMPRESS機胜が導入されたした。

 SELECT * FROM ( VALUES ('XML Unicode', DATALENGTH(COMPRESS(@XML_Unicode)), DATALENGTH(COMPRESS(@XML_Unicode_D))) , ('XML ANSI', DATALENGTH(COMPRESS(@XML_ANSI)), DATALENGTH(COMPRESS(@XML_ANSI_D))) , ('JSON Unicode', DATALENGTH(COMPRESS(@JSON_Unicode)), DATALENGTH(COMPRESS(@JSON_Unicode_D))) , ('JSON ANSI', DATALENGTH(COMPRESS(@JSON_ANSI)), DATALENGTH(COMPRESS(@JSON_ANSI_D))) ) t(DataType, CompressDelimeters, CompressNoDelimeters) 

前の䟋の結果

 DataType CompressDelimeters CompressNoDelimeters ------------ -------------------- -------------------- XML Unicode 244 223 XML ANSI 198 180 JSON Unicode 272 224 JSON ANSI 221 183 

すべおがうたく圧瞮されおいたすが、1぀の機胜を芚えおおく必芁がありたす。 最初にデヌタがANSIで受信され、その埌倉数の型がUnicodeに倉曎されたず仮定したす 。

 DECLARE @t TABLE (val VARBINARY(MAX)) INSERT INTO @t VALUES (COMPRESS('[{"Name":"ThinkPad E460"}]')) -- VARCHAR(8000) , (COMPRESS(N'[{"Name":"ThinkPad E460"}]')) -- NVARCHAR(4000) SELECT val , DECOMPRESS(val) , CAST(DECOMPRESS(val) AS NVARCHAR(MAX)) , CAST(DECOMPRESS(val) AS VARCHAR(MAX)) FROM @t 

COMPRESS関数は、 ANSI / Unicodeに察しお異なるバむナリシヌケンスを返したす。その埌の読み取り時に、デヌタの䞀郚がANSIずしお保存され、䞀郚がUnicodeで保存される状況が発生したす 。 その堎合、どの型にキャストするかを掚枬するこずは非垞に困難です。

 ---------------------------- -------------------------------------------------------筛䞢浡≥∺桔湩偫摡䔠㘎∰嵜 [{"Name":"ThinkPad E460"}] [{"Name":"ThinkPad E460"}] [ { " N ame " : " T hink P ad E 4 6 0 " } ] 

ロヌドされたシステムを構築する堎合、 COMPRESS関数を䜿甚するず挿入が遅くなりたす。

 USE tempdb GO DROP TABLE IF EXISTS #Compress DROP TABLE IF EXISTS #NoCompress GO CREATE TABLE #NoCompress (DatabaseLogID INT PRIMARY KEY, JSON_Val NVARCHAR(MAX)) CREATE TABLE #Compress (DatabaseLogID INT PRIMARY KEY, JSON_CompressVal VARBINARY(MAX)) GO SET STATISTICS IO, TIME ON INSERT INTO #NoCompress SELECT DatabaseLogID , JSON_Val = ( SELECT PostTime, DatabaseUser, [Event], [Schema], [Object], [TSQL] FOR JSON PATH, WITHOUT_ARRAY_WRAPPER ) FROM AdventureWorks2014.dbo.DatabaseLog OPTION(MAXDOP 1) INSERT INTO #Compress SELECT DatabaseLogID , JSON_CompressVal = COMPRESS(( SELECT PostTime, DatabaseUser, [Event], [Schema], [Object], [TSQL] FOR JSON PATH, WITHOUT_ARRAY_WRAPPER )) FROM AdventureWorks2014.dbo.DatabaseLog OPTION(MAXDOP 1) SET STATISTICS IO, TIME OFF 

そしおそれは非垞に重芁です

 NoCompress: CPU time = 15 ms, elapsed time = 25 ms Compress: CPU time = 218 ms, elapsed time = 280 ms 

この堎合、テヌブルサむズは瞮小されたす。

 SELECT obj_name = OBJECT_NAME(p.[object_id]) , a.[type_desc] , a.total_pages , total_mb = a.total_pages * 8 / 1024. FROM sys.partitions p JOIN sys.allocation_units a ON p.[partition_id] = a.container_id WHERE p.[object_id] IN ( OBJECT_ID('#Compress'), OBJECT_ID('#NoCompress') ) 

 obj_name type_desc total_pages total_mb -------------- ------------- ------------ --------- NoCompress IN_ROW_DATA 204 1.593750 NoCompress LOB_DATA 26 0.203125 Compress IN_ROW_DATA 92 0.718750 Compress LOB_DATA 0 0.000000 

さらに、圧瞮デヌタテヌブルから読み取るず、 DECOMPRESS機胜が倧幅に遅くなりたす。

 SET STATISTICS IO, TIME ON SELECT * FROM #NoCompress WHERE JSON_VALUE(JSON_Val, '$.Event') = 'CREATE_TABLE' SELECT DatabaseLogID, [JSON] = CAST(DECOMPRESS(JSON_CompressVal) AS NVARCHAR(MAX)) FROM #Compress WHERE JSON_VALUE(CAST(DECOMPRESS(JSON_CompressVal) AS NVARCHAR(MAX)), '$.Event') = N'CREATE_TABLE' SET STATISTICS IO, TIME OFF 

論理読み取り倀は枛少したすが、実行速床は非垞に䜎くなりたす。

 Table 'NoCompress'. Scan count 1, logical reads 187, ... CPU time = 16 ms, elapsed time = 37 ms Table 'Compress'. Scan count 1, logical reads 79, ... CPU time = 109 ms, elapsed time = 212 ms 

たたは、 PERSISTED蚈算列を远加できたす。

 ALTER TABLE #Compress ADD EventType_Persisted AS CAST(JSON_VALUE(CAST( DECOMPRESS(JSON_CompressVal) AS NVARCHAR(MAX)), '$.Event') AS VARCHAR(200)) PERSISTED 

たたは、蚈算列を䜜成し、それに基づいおむンデックスを䜜成したす。

 ALTER TABLE #Compress ADD EventType_NonPersisted AS CAST(JSON_VALUE(CAST( DECOMPRESS(JSON_CompressVal) AS NVARCHAR(MAX)), '$.Event') AS VARCHAR(200)) CREATE INDEX ix ON #Compress (EventType_NonPersisted) 

堎合によっおは、ネットワヌクの遅延が䞊蚘の䟋よりもはるかにパフォヌマンスに圱響するこずがありたす。 クラむアントでJSON GZIPデヌタを圧瞮しおサヌバヌに送信できるず想像しおください。

 DECLARE @json NVARCHAR(MAX) = ( SELECT t.[name] , t.[object_id] , [columns] = ( SELECT c.column_id, c.[name], c.system_type_id FROM sys.all_columns c WHERE c.[object_id] = t.[object_id] FOR JSON AUTO ) FROM sys.all_objects t FOR JSON AUTO ) SELECT InitialSize = DATALENGTH(@json) / 1048576. , CompressSize = DATALENGTH(COMPRESS(@json)) / 1048576. 

私にずっお、プロゞェクトの1぀でネットワヌクトラフィックを削枛しようずするず、「ラむフラむン」になりたした。

 InitialSize CompressSize -------------- ------------- 1.24907684 0.10125923 

4.圧瞮


テヌブルのサむズを小さくするために、デヌタ圧瞮を䜿甚するこずもできたす。 以前は、圧瞮はEnterprise゚ディションでのみ利甚可胜でした。 ただし、 SQL Server 2016 SP1のリリヌスでは、 Expressでもこの機胜を䜿甚できたす。

 USE AdventureWorks2014 GO DROP TABLE IF EXISTS #InitialTable DROP TABLE IF EXISTS #None DROP TABLE IF EXISTS #Row DROP TABLE IF EXISTS #Page GO CREATE TABLE #None (ID INT, Val NVARCHAR(MAX), INDEX ix CLUSTERED (ID) WITH (DATA_COMPRESSION = NONE)) CREATE TABLE #Row (ID INT, Val NVARCHAR(MAX), INDEX ix CLUSTERED (ID) WITH (DATA_COMPRESSION = ROW)) CREATE TABLE #Page (ID INT, Val NVARCHAR(MAX), INDEX ix CLUSTERED (ID) WITH (DATA_COMPRESSION = PAGE)) GO SELECT h.SalesOrderID , JSON_Data = ( SELECT p.[Name] FROM Sales.SalesOrderDetail d JOIN Production.Product p ON d.ProductID = p.ProductID WHERE d.SalesOrderID = h.SalesOrderID FOR JSON AUTO ) INTO #InitialTable FROM Sales.SalesOrderHeader h SET STATISTICS IO, TIME ON INSERT INTO #None SELECT * FROM #InitialTable OPTION(MAXDOP 1) INSERT INTO #Row SELECT * FROM #InitialTable OPTION(MAXDOP 1) INSERT INTO #Page SELECT * FROM #InitialTable OPTION(MAXDOP 1) SET STATISTICS IO, TIME OFF 

 None: CPU time = 62 ms, elapsed time = 68 ms Row: CPU time = 94 ms, elapsed time = 89 ms Page: CPU time = 125 ms, elapsed time = 126 ms 

ペヌゞレベルの圧瞮では、アルゎリズムを䜿甚しお同様のデヌタの塊を芋぀け、それらをより小さい倀に眮き換えたす。 行レベルの圧瞮は、タむプを必芁最小限に切り捚お、䜙分な文字も切り捚おたす。 たずえば、列の型はINTで4バむトですが、そこに栌玍されおいる倀は255未満です。このようなレコヌドの堎合、型は切り捚おられ、ディスク䞊のデヌタはTINYINTのようにスペヌスを占有したす。

 USE tempdb GO SELECT obj_name = OBJECT_NAME(p.[object_id]) , a.[type_desc] , a.total_pages , total_mb = a.total_pages * 8 / 1024. FROM sys.partitions p JOIN sys.allocation_units a ON p.[partition_id] = a.container_id WHERE p.[object_id] IN (OBJECT_ID('#None'), OBJECT_ID('#Page'), OBJECT_ID('#Row')) 

 obj_name type_desc total_pages total_mb ---------- ------------- ------------ --------- None IN_ROW_DATA 1156 9.031250 Row IN_ROW_DATA 1132 8.843750 Page IN_ROW_DATA 1004 7.843750 

5. ColumnStore


しかし、私が最も気に入っおいるのはColumnStoreむンデックスです。これは、 SQL Serverのバヌゞョンごずに改善されおいたす 。

ColumnStoreの䞻なアむデアは、テヌブル内のデヌタを玄100䞇行ごずにRowGroupsに分割し、このグルヌプ内で列内のデヌタを圧瞮するこずです。 これにより、ディスク容量の倧幅な節玄、論理読み取り倀の削枛、分析ク゚リの高速化が実珟したす。 したがっお、 JSON情報を含むアヌカむブを保存する必芁がある堎合、クラスタヌ化されたColumnStoreむンデックスを䜜成できたす。

 USE AdventureWorks2014 GO DROP TABLE IF EXISTS #CCI DROP TABLE IF EXISTS #InitialTable GO CREATE TABLE #CCI (ID INT, Val NVARCHAR(MAX), INDEX ix CLUSTERED COLUMNSTORE) GO SELECT h.SalesOrderID , JSON_Data = CAST( ( SELECT p.[Name] FROM Sales.SalesOrderDetail d JOIN Production.Product p ON d.ProductID = p.ProductID WHERE d.SalesOrderID = h.SalesOrderID FOR JSON AUTO ) AS VARCHAR(8000)) -- SQL Server 2012..2016 INTO #InitialTable FROM Sales.SalesOrderHeader h SET STATISTICS TIME ON INSERT INTO #CCI SELECT * FROM #InitialTable SET STATISTICS TIME OFF 

衚の挿入速床は、ほがPAGE圧瞮に察応したす。 さらに、 COMPRESSION_DELAYオプションにより、プロセスをOLTPロヌドに埮調敎できたす。

 CCI: CPU time = 140 ms, elapsed time = 136 ms 

SQL Server 2017より前は、ColumnStoreむンデックスは[N] VARCHARMAXデヌタ型をサポヌトしおいたせんでしたが、新しいバヌゞョンのリリヌスに䌎い、任意の長さの行をColumnStoreに栌玍できたした 。

 USE tempdb GO SELECT o.[name] , s.used_page_count / 128. FROM sys.indexes i JOIN sys.dm_db_partition_stats s ON i.[object_id] = s.[object_id] AND i.index_id = s.index_id JOIN sys.objects o ON i.[object_id] = o.[object_id] WHERE i.[object_id] = OBJECT_ID('#CCI') 

これからの利益は時々非垞に印象的です

 ------ --------- CCI 0.796875 

6. JSONを䜜成する


次に、 JSONを生成する方法を芋おみたしょう。 SQL Serverで既にXMLを䜿甚しおいる堎合、すべおは類掚によっお行われたす。

JSONを生成する最も簡単な方法は、 FOR JSON AUTOを䜿甚するこずです。 この堎合、オブゞェクトからJSON配列が生成されたす。

 DROP TABLE IF EXISTS #Users GO CREATE TABLE #Users ( UserID INT , UserName SYSNAME , RegDate DATETIME ) INSERT INTO #Users VALUES (1, 'Paul Denton', '20170123') , (2, 'JC Denton', NULL) , (3, 'Maggie Cho', NULL) SELECT * FROM #Users FOR JSON AUTO 

 [ { "UserID":1, "UserName":"Paul Denton", "RegDate":"2029-01-23T00:00:00" }, { "UserID":2, "UserName":"JC Denton" }, { "UserID":3, "UserName":"Maggie Cho" } ] 

NULL倀は無芖されるこずに泚意するこずが重芁です。 それらをJSONに含めたい堎合は、 INCLUDE_NULL_VALUESオプションを䜿甚できたす。

 SELECT UserID, RegDate FROM #Users FOR JSON AUTO, INCLUDE_NULL_VALUES 

 [ { "UserID":1, "RegDate":"2017-01-23T00:00:00" }, { "UserID":2, "RegDate":null }, { "UserID":3, "RegDate":null } ] 

角括匧を取り陀く必芁がある堎合は、 WITHOUT_ARRAY_WRAPPERオプションがこれに圹立ちたす。

 SELECT TOP(1) UserID, UserName FROM #Users FOR JSON AUTO, WITHOUT_ARRAY_WRAPPER 

 { "UserID":1, "UserName":"Paul Denton" } 

結果をルヌト芁玠ず組み合わせたい堎合、 ROOTオプションがこれに提䟛されたす

 SELECT UserID, UserName FROM #Users FOR JSON AUTO, ROOT('Users') 

 { "Users":[ { "UserID":1, "UserName":"Paul Denton" }, { "UserID":2, "UserName":"JC Denton" }, { "UserID":3, "UserName":"Maggie Cho" } ] } 

より耇雑な構造のJSONを䜜成する堎合は、プロパティに必芁な名前を割り圓おおグルヌプ化し、 FOR JSON PATH匏を䜿甚する必芁がありたす。

 SELECT TOP(1) UserID , UserName AS [Detail.FullName] , RegDate AS [Detail.RegDate] FROM #Users FOR JSON PATH 

 [ { "UserID":1, "Detail":{ "FullName":"Paul Denton", "RegDate":"2017-01-23T00:00:00" } } ] 

 SELECT t.[name] , t.[object_id] , [columns] = ( SELECT c.column_id, c.[name] FROM sys.columns c WHERE c.[object_id] = t.[object_id] FOR JSON AUTO ) FROM sys.tables t FOR JSON AUTO 

 [ { "name":"#Users", "object_id":1483152329, "columns":[ { "column_id":1, "name":"UserID" }, { "column_id":2, "name":"UserName" }, { "column_id":3, "name":"RegDate" } ] } ] 

7. JSONを確認する


JSON圢匏の正確さを怜蚌するために、 JSONの堎合は1を返し、そうでない堎合は0を返し 、 NULLが枡された堎合はNULLを返すISJSON関数がありたす 。

 DECLARE @json1 NVARCHAR(MAX) = N'{"id": 1}' , @json2 NVARCHAR(MAX) = N'[1,2,3]' , @json3 NVARCHAR(MAX) = N'1' , @json4 NVARCHAR(MAX) = N'' , @json5 NVARCHAR(MAX) = NULL SELECT ISJSON(@json1) -- 1 , ISJSON(@json2) -- 1 , ISJSON(@json3) -- 0 , ISJSON(@json4) -- 0 , ISJSON(@json5) -- NULL 

8. JsonValue


JSONからスカラヌ倀を抜出するには、 JSON_VALUE関数を䜿甚できたす。

 DECLARE @json NVARCHAR(MAX) = N' { "UserID": 1, "UserName": "JC Denton", "IsActive": true, "Date": "2016-05-31T00:00:00", "Settings": [ { "Language": "EN" }, { "Skin": "FlatUI" } ] }' SELECT JSON_VALUE(@json, '$.UserID') , JSON_VALUE(@json, '$.UserName') , JSON_VALUE(@json, '$.Settings[0].Language') , JSON_VALUE(@json, '$.Settings[1].Skin') , JSON_QUERY(@json, '$.Settings') 

9. OpenJson


衚圢匏デヌタの解析には、 OPENJSON衚関数が䜿甚されたす。 すぐに、互換性レベルが130以䞊のベヌスでのみ機胜するこずに泚意しおください。

OPENSON機胜には2぀の操䜜モヌドがありたす。 最も単玔な-結果の遞択にスキヌマを指定せずに

 DECLARE @json NVARCHAR(MAX) = N' { "UserID": 1, "UserName": "JC Denton", "IsActive": true, "RegDate": "2016-05-31T00:00:00" }' SELECT * FROM OPENJSON(@json) 

2番目のモヌドでは、返された結果がどのようになるかを説明できたす。列名、その番号、倀の取埗堎所

 DECLARE @json NVARCHAR(MAX) = N' [ { "User ID": 1, "UserName": "JC Denton", "IsActive": true, "Date": "2016-05-31T00:00:00", "Settings": [ { "Language": "EN" }, { "Skin": "FlatUI" } ] }, { "User ID": 2, "UserName": "Paul Denton", "IsActive": false } ]' SELECT * FROM OPENJSON(@json) SELECT * FROM OPENJSON(@json, '$[0]') SELECT * FROM OPENJSON(@json, '$[0].Settings[0]') SELECT * FROM OPENJSON(@json) WITH ( UserID INT '$."User ID"' , UserName SYSNAME , IsActive BIT , RegDate DATETIME '$.Date' , Settings NVARCHAR(MAX) AS JSON , Skin SYSNAME '$.Settings[1].Skin' ) 

文曞にネストされた階局がある堎合、次の䟋が圹立ちたす。

 DECLARE @json NVARCHAR(MAX) = N' [ { "FullName": "JC Denton", "Children": [ { "FullName": "Mary", "Male": "0" }, { "FullName": "Paul", "Male": "1" } ] }, { "FullName": "Paul Denton" } ]' SELECT t.FullName, c.* FROM OPENJSON(@json) WITH ( FullName SYSNAME , Children NVARCHAR(MAX) AS JSON ) t OUTER APPLY OPENJSON(Children) WITH ( ChildrenName SYSNAME '$.FullName' , Male TINYINT ) c 

10.文字列の分割


SQL Server 2016のリリヌスで、 STRING_SPLIT関数が登堎したした 。 そしお、誰もが安心しおため息を぀いた。今では、文字列をトヌクンに分割するために自転車を甚意する必芁はない。 ただし、別の代替手段がありたす-OPENJSON構文です。これに぀いおは前に怜蚎したした。 いく぀かの分割線オプションをテストしおみたしょう。

 SET NOCOUNT ON SET STATISTICS TIME OFF DECLARE @x VARCHAR(MAX) = '1' + REPLICATE(CAST(',1' AS VARCHAR(MAX)), 1000) SET STATISTICS TIME ON ;WITH cte AS ( SELECT s = 1 , e = COALESCE(NULLIF(CHARINDEX(',', @x, 1), 0), LEN(@x) + 1) , v = SUBSTRING(@x, 1, COALESCE(NULLIF(CHARINDEX(',', @x, 1), 0), LEN(@x) + 1) - 1) UNION ALL SELECT s = CONVERT(INT, e) + 1 , e = COALESCE(NULLIF(CHARINDEX(',', @x, e + 1), 0), LEN(@x) + 1) , v = SUBSTRING(@x, e + 1, COALESCE(NULLIF(CHARINDEX(',', @x, e + 1), 0), LEN(@x) + 1)- e - 1) FROM cte WHERE e < LEN(@x) + 1 ) SELECT v FROM cte WHERE LEN(v) > 0 OPTION (MAXRECURSION 0) SELECT tcvalue('(./text())[1]', 'INT') FROM ( SELECT x = CONVERT(XML, '<i>' + REPLACE(@x, ',', '</i><i>') + '</i>').query('.') ) a CROSS APPLY x.nodes('i') t(c) SELECT * FROM STRING_SPLIT(@x, N',') -- NCHAR(1)/CHAR(1) SELECT [value] FROM OPENJSON(N'[' + @x + N']') -- [1,2,3,4] SET STATISTICS TIME OFF 

結果を芋るず、 XMLずCTEの束葉杖は蚀うたでもなく、 OPENJSONがSTRING_SPLIT関数よりも高速である堎合がありたす。

  500k 100k 50k 1000 ------------- ------- ------ ------ ------ CTE 29407 2406 1266 58 XML 6520 1084 553 259 STRING_SPLIT 4665 594 329 27 OPENJSON 2606 506 273 19 

さらに、 OLTPの負荷が高い堎合、OPENJSONずSTRING_SPLITの 間に明らかな違いはありたせん1000回の反埩+コンマで区切られた10個の倀

 CTE = 4629 ms XML = 4397 ms STRING_SPLIT = 4011 ms OPENJSON = 4047 ms 

11.緩い


SQL Server 2005以降、 XML SCHEMA COLLECTIONを䜿甚しおデヌタベヌスからXMLを怜蚌する機䌚。 XMLのスキヌマを蚘述し、それに基づいおデヌタの正確性を確認できたす。 JSONの明瀺的な圢匏ではそのような機胜はありたせんが、回避策がありたす。

私が芚えおいる限り、 JSONには2぀のタむプの匏がありたす  strictずlax デフォルトで䜿甚。 違いは、構文解析時に存圚しないパスたたは誀ったパスを指定するず、 緩い匏ではNULLが取埗され、 厳栌な堎合ぱラヌになるずいうこずです。

 DECLARE @json NVARCHAR(MAX) = N' { "UserID": 1, "UserName": "JC Denton" }' SELECT JSON_VALUE(@json, '$.IsActive') , JSON_VALUE(@json, 'lax$.IsActive') , JSON_VALUE(@json, 'strict$.UserName') SELECT JSON_VALUE(@json, 'strict$.IsActive') 

 Msg 13608, Level 16, State 2, Line 12 Property cannot be found on the specified JSON path. 

12.倉曎


JSON内のデヌタを倉曎するには、 JSON_MODIFY関数がありたす。 䟋は非垞に単玔であるため、詳现にペむントしおも意味がありたせん。

 DECLARE @json NVARCHAR(MAX) = N' { "FirstName": "JC", "LastName": "Denton", "Age": 20, "Skills": ["SQL Server 2014"] }' -- 20 -> 22 SET @json = JSON_MODIFY(@json, '$.Age', CAST(JSON_VALUE(@json, '$.Age') AS INT) + 2) -- "SQL 2014" -> "SQL 2016" SET @json = JSON_MODIFY(@json, '$.Skills[0]', 'SQL 2016') SET @json = JSON_MODIFY(@json, 'append $.Skills', 'JSON') SELECT * FROM OPENJSON(@json) -- delete Age SELECT * FROM OPENJSON(JSON_MODIFY(@json, 'lax$.Age', NULL)) -- set NULL SELECT * FROM OPENJSON(JSON_MODIFY(@json, 'strict$.Age', NULL)) GO DECLARE @json NVARCHAR(100) = N'{ "price": 105.90 }' -- rename SET @json = JSON_MODIFY( JSON_MODIFY(@json, '$.Price', CAST(JSON_VALUE(@json, '$.price') AS NUMERIC(6,2))), '$.price', NULL) SELECT @json 

13.暗黙の倉換


そしお今、私たちは最も興味深いもの、぀たりパフォヌマンスに関連する問題に取り組み始めおいたす。

JSONを解析する堎合、留意すべき点が1぀ありたす 。OPENJSONずJSON_VALUEは、再定矩しない堎合、結果をUnicodeで返したす。 AdventureWorksデヌタベヌスでは、 AccountNumber列のデヌタ型はVARCHARです

 USE AdventureWorks2014 GO DECLARE @json NVARCHAR(MAX) = N'{ "AccountNumber": "AW00000009" }' SET STATISTICS IO ON SELECT CustomerID, AccountNumber FROM Sales.Customer WHERE AccountNumber = JSON_VALUE(@json, '$.AccountNumber') SELECT CustomerID, AccountNumber FROM Sales.Customer WHERE AccountNumber = CAST(JSON_VALUE(@json, '$.AccountNumber') AS VARCHAR(10)) SET STATISTICS IO OFF 

論理読み取り倀の違い

 Table 'Customer'. Scan count 1, logical reads 37, ... Table 'Customer'. Scan count 0, logical reads 2, ... 

列ず関数の結果の間のデヌタ型が䞀臎しないずいう事実により、 SQL Serverは優先順䜍に基づいお暗黙的な型倉換を実行する必芁がありたす 。 この堎合、 NVARCHARに 。 残念ながら、むンデックス列でのすべおの蚈算ず倉換は、ほずんどの堎合IndexScanに぀ながりたす。



列ず同様に明瀺的にタむプを指定するず、 IndexSeekが取埗されたす 。



14.むンデックス


次に、 JSONオブゞェクトにむンデックスを付ける方法を怜蚎したす。 最初に述べたように、XMLずは異なり、 JSONの個別のデヌタ型はSQL Server 2016に远加されたせんでした。 したがっお、任意の文字列デヌタ型を䜿甚しお保存できたす。

XMLの経隓がある人は、 SQL Serverのこの圢匏には、特定の遞択を高速化できるいく぀かの皮類のむンデックスがあるこずを思い出したす。 JSONが栌玍されるこずになっおいる文字列型の堎合、そのようなむンデックスは単に存圚したせん。

残念ながら 、 JSONBは配信されたせんでした。 開発チヌムは、 JSON機胜をリリヌスするずきに急いでおり、文字通り次のように述べたした。「速床が足りない堎合は、次のバヌゞョンでJSONBを远加したす。」 SQL Server 2017のリリヌスでは、これは起こりたせんでした。

そしお、ここで蚈算列が圹立ちたす。蚈算列は、 JSONドキュメントの特定のプロパティであり、怜玢する必芁があり、これらの列に基づいおむンデックスを䜜成する必芁がありたす。

 USE AdventureWorks2014 GO DROP TABLE IF EXISTS #JSON GO CREATE TABLE #JSON ( DatabaseLogID INT PRIMARY KEY , InfoJSON NVARCHAR(MAX) NOT NULL ) GO INSERT INTO #JSON SELECT DatabaseLogID , InfoJSON = ( SELECT PostTime, DatabaseUser, [Event], [Schema], [Object], [TSQL] FOR JSON PATH, WITHOUT_ARRAY_WRAPPER ) FROM dbo.DatabaseLog 

同じデヌタを解析するたびにあたり合理的ではありたせん

 SET STATISTICS IO, TIME ON SELECT * FROM #JSON WHERE JSON_VALUE(InfoJSON, '$.Schema') + '.' + JSON_VALUE(InfoJSON, '$.Object') = 'Person.Person' SET STATISTICS IO, TIME OFF 

 Table 'JSON'. Scan count 1, logical reads 187, ... CPU time = 16 ms, elapsed time = 29 ms 

したがっお、蚈算列の䜜成ずその埌のむンデックスぞの組み蟌みが正圓化される堎合がありたす。

 ALTER TABLE #JSON ADD ObjectName AS JSON_VALUE(InfoJSON, '$.Schema') + '.' + JSON_VALUE(InfoJSON, '$.Object') GO CREATE INDEX IX_ObjectName ON #JSON (ObjectName) GO SET STATISTICS IO, TIME ON SELECT * FROM #JSON WHERE JSON_VALUE(InfoJSON, '$.Schema') + '.' + JSON_VALUE(InfoJSON, '$.Object') = 'Person.Person' SELECT * FROM #JSON WHERE ObjectName = 'Person.Person' SET STATISTICS IO, TIME OFF 

同時に、SQL Serverオプティマむザヌは非垞にスマヌトであるため、コヌドを倉曎する必芁はありたせん。

 Table 'JSON'. Scan count 1, logical reads 13, ... CPU time = 0 ms, elapsed time = 1 ms Table 'JSON'. Scan count 1, logical reads 13, ... CPU time = 0 ms, elapsed time = 1 ms 

さらに、配列の内容たたはオブゞェクトの䞀郚党䜓を怜玢する堎合は、通垞のむンデックスずフルテキストむンデックスの䞡方を䜜成できたす。

同時に、フルテキストむンデックスには特別なJSON凊理ルヌルはありたせん。二重匕甚笊、コンマ、ブラケットを区切り文字ずしお䜿甚しお、テキストを個別のトヌクンに分割したす。これがJSON構造自䜓の構成です。

 USE AdventureWorks2014 GO DROP TABLE IF EXISTS dbo.LogJSON GO CREATE TABLE dbo.LogJSON ( DatabaseLogID INT , InfoJSON NVARCHAR(MAX) NOT NULL , CONSTRAINT pk PRIMARY KEY (DatabaseLogID) ) GO INSERT INTO dbo.LogJSON SELECT DatabaseLogID , InfoJSON = ( SELECT PostTime, DatabaseUser, [Event], ObjectName = [Schema] + '.' + [Object] FOR JSON PATH, WITHOUT_ARRAY_WRAPPER ) FROM dbo.DatabaseLog GO IF EXISTS( SELECT * FROM sys.fulltext_catalogs WHERE [name] = 'JSON_FTC' ) DROP FULLTEXT CATALOG JSON_FTC GO CREATE FULLTEXT CATALOG JSON_FTC WITH ACCENT_SENSITIVITY = ON AUTHORIZATION dbo GO IF EXISTS ( SELECT * FROM sys.fulltext_indexes WHERE [object_id] = OBJECT_ID(N'dbo.LogJSON') ) BEGIN ALTER FULLTEXT INDEX ON dbo.LogJSON DISABLE DROP FULLTEXT INDEX ON dbo.LogJSON END GO CREATE FULLTEXT INDEX ON dbo.LogJSON (InfoJSON) KEY INDEX pk ON JSON_FTC GO SELECT * FROM dbo.LogJSON WHERE CONTAINS(InfoJSON, 'ALTER_TABLE') 

15.パヌサヌのパフォヌマンス


最埌に、この蚘事の最も興味深い郚分にたどり着きたす。SQL Server䞊のXMLず比范しおJSON解析はどれくらい高速ですかこの質問に答えるために、䞀連のテストを準備したした。JSONおよびXML圢匏の2぀の倧きなファむルを準備したす。



 /* EXEC sys.sp_configure 'show advanced options', 1 GO RECONFIGURE GO EXEC sys.sp_configure 'xp_cmdshell', 1 GO RECONFIGURE WITH OVERRIDE GO */ USE AdventureWorks2014 GO DROP PROCEDURE IF EXISTS ##get_xml DROP PROCEDURE IF EXISTS ##get_json GO CREATE PROCEDURE ##get_xml AS SELECT r.ProductID , r.[Name] , r.ProductNumber , d.OrderQty , d.UnitPrice , r.ListPrice , r.Color , r.MakeFlag FROM Sales.SalesOrderDetail d JOIN Production.Product r ON d.ProductID = r.ProductID FOR XML PATH ('Product'), ROOT('Products') GO CREATE PROCEDURE ##get_json AS SELECT ( SELECT r.ProductID , r.[Name] , r.ProductNumber , d.OrderQty , d.UnitPrice , r.ListPrice , r.Color , r.MakeFlag FROM Sales.SalesOrderDetail d JOIN Production.Product r ON d.ProductID = r.ProductID FOR JSON PATH ) GO DECLARE @sql NVARCHAR(4000) SET @sql = 'bcp "EXEC ##get_xml" queryout "X:\sample.xml" -S ' + @@servername + ' -T -w -r -t' EXEC sys.xp_cmdshell @sql SET @sql = 'bcp "EXEC ##get_json" queryout "X:\sample.txt" -S ' + @@servername + ' -T -w -r -t' EXEC sys.xp_cmdshell @sql 

OPENJSON、OPENXMLおよびXQueryのパフォヌマンスを確認したす。

 SET NOCOUNT ON SET STATISTICS TIME ON DECLARE @xml XML SELECT @xml = BulkColumn FROM OPENROWSET(BULK 'X:\sample.xml', SINGLE_BLOB) x DECLARE @jsonu NVARCHAR(MAX) SELECT @jsonu = BulkColumn FROM OPENROWSET(BULK 'X:\sample.txt', SINGLE_NCLOB) x /* XML: CPU = 891 ms, Time = 886 ms NVARCHAR: CPU = 141 ms, Time = 166 ms */ SELECT ProductID = tcvalue('(ProductID/text())[1]', 'INT') , [Name] = tcvalue('(Name/text())[1]', 'NVARCHAR(50)') , ProductNumber = tcvalue('(ProductNumber/text())[1]', 'NVARCHAR(25)') , OrderQty = tcvalue('(OrderQty/text())[1]', 'SMALLINT') , UnitPrice = tcvalue('(UnitPrice/text())[1]', 'MONEY') , ListPrice = tcvalue('(ListPrice/text())[1]', 'MONEY') , Color = tcvalue('(Color/text())[1]', 'NVARCHAR(15)') , MakeFlag = tcvalue('(MakeFlag/text())[1]', 'BIT') FROM @xml.nodes('Products/Product') t(c) /* CPU time = 6203 ms, elapsed time = 6492 ms */ DECLARE @doc INT EXEC sys.sp_xml_preparedocument @doc OUTPUT, @xml SELECT * FROM OPENXML(@doc, '/Products/Product', 2) WITH ( ProductID INT , [Name] NVARCHAR(50) , ProductNumber NVARCHAR(25) , OrderQty SMALLINT , UnitPrice MONEY , ListPrice MONEY , Color NVARCHAR(15) , MakeFlag BIT ) EXEC sys.sp_xml_removedocument @doc /* CPU time = 2656 ms, elapsed time = 3489 ms CPU time = 3844 ms, elapsed time = 4482 ms CPU time = 0 ms, elapsed time = 4 ms */ SELECT * FROM OPENJSON(@jsonu) WITH ( ProductID INT , [Name] NVARCHAR(50) , ProductNumber NVARCHAR(25) , OrderQty SMALLINT , UnitPrice MONEY , ListPrice MONEY , Color NVARCHAR(15) , MakeFlag BIT ) /* CPU time = 1359 ms, elapsed time = 1642 ms */ SET STATISTICS TIME, IO OFF 

ここで、XQueryに察するスカラヌ関数JSON_VALUEのパフォヌマンスを確認したしょう。

 SET NOCOUNT ON DECLARE @jsonu NVARCHAR(MAX) = N'[ {"User":"Sergey Syrovatchenko","Age":28,"Skills":["SQL Server","T-SQL","JSON","XML"]}, {"User":"JC Denton","Skills":["Microfibral Muscle","Regeneration","EMP Shield"]}, {"User":"Paul Denton","Age":32,"Skills":["Vision Enhancement"]}]' DECLARE @jsonu_f NVARCHAR(MAX) = N'[ { "User":"Sergey Syrovatchenko", "Age":28, "Skills":[ "SQL Server", "T-SQL", "JSON", "XML" ] }, { "User":"JC Denton", "Skills":[ "Microfibral Muscle", "Regeneration", "EMP Shield" ] }, { "User":"Paul Denton", "Age":32, "Skills":[ "Vision Enhancement" ] } ]' DECLARE @json VARCHAR(MAX) = @jsonu , @json_f VARCHAR(MAX) = @jsonu_f DECLARE @xml XML = N' <Users> <User Name="Sergey Syrovatchenko"> <Age>28</Age> <Skills> <Skill>SQL Server</Skill> <Skill>T-SQL</Skill> <Skill>JSON</Skill> <Skill>XML</Skill> </Skills> </User> <User Name="JC Denton"> <Skills> <Skill>Microfibral Muscle</Skill> <Skill>Regeneration</Skill> <Skill>EMP Shield</Skill> </Skills> </User> <User Name="Paul Denton"> <Age>28</Age> <Skills> <Skill>Vision Enhancement</Skill> </Skills> </User> </Users>' DECLARE @i INT , @int INT , @varchar VARCHAR(100) , @nvarchar NVARCHAR(100) , @s DATETIME , @runs INT = 100000 DECLARE @t TABLE ( iter INT IDENTITY PRIMARY KEY , data_type VARCHAR(100) , [path] VARCHAR(1000) , [type] VARCHAR(1000) , time_ms INT ) SELECT @i = 1, @s = GETDATE() WHILE @i <= @runs SELECT @int = JSON_VALUE(@jsonu, '$[0].Age') , @i += 1 INSERT INTO @t SELECT '@jsonu', '$[0].Age', 'INT', DATEDIFF(ms, @s, GETDATE()) SELECT @i = 1, @s = GETDATE() WHILE @i <= @runs SELECT @int = JSON_VALUE(@jsonu_f, '$[0].Age') , @i += 1 INSERT INTO @t SELECT '@jsonu_f', '$[0].Age', 'INT', DATEDIFF(ms, @s, GETDATE()) SELECT @i = 1, @s = GETDATE() WHILE @i <= @runs SELECT @int = JSON_VALUE(@json, '$[0].Age') , @i += 1 INSERT INTO @t SELECT '@json', '$[0].Age', 'INT', DATEDIFF(ms, @s, GETDATE()) SELECT @i = 1, @s = GETDATE() WHILE @i <= @runs SELECT @int = JSON_VALUE(@json_f, '$[0].Age') , @i += 1 INSERT INTO @t SELECT '@json_f', '$[0].Age', 'INT', DATEDIFF(ms, @s, GETDATE()) SELECT @i = 1, @s = GETDATE() WHILE @i <= @runs SELECT @int = @xml.value('(Users/User[1]/Age/text())[1]', 'INT') , @i += 1 INSERT INTO @t SELECT '@xml', '(Users/User[1]/Age/text())[1]', 'INT', DATEDIFF(ms, @s, GETDATE()) SELECT @i = 1, @s = GETDATE() WHILE @i <= @runs SELECT @nvarchar = JSON_VALUE(@jsonu, '$[1].User') , @i += 1 INSERT INTO @t SELECT '@jsonu', '$[1].User', 'NVARCHAR', DATEDIFF(ms, @s, GETDATE()) SELECT @i = 1, @s = GETDATE() WHILE @i <= @runs SELECT @nvarchar = JSON_VALUE(@jsonu_f, '$[1].User') , @i += 1 INSERT INTO @t SELECT '@jsonu_f', '$[1].User', 'NVARCHAR', DATEDIFF(ms, @s, GETDATE()) SELECT @i = 1, @s = GETDATE() WHILE @i <= @runs SELECT @varchar = JSON_VALUE(@json, '$[1].User') , @i += 1 INSERT INTO @t SELECT '@json', '$[1].User', 'VARCHAR', DATEDIFF(ms, @s, GETDATE()) SELECT @i = 1, @s = GETDATE() WHILE @i <= @runs SELECT @varchar = JSON_VALUE(@json_f, '$[1].User') , @i += 1 INSERT INTO @t SELECT '@json_f', '$[1].User', 'VARCHAR', DATEDIFF(ms, @s, GETDATE()) SELECT @i = 1, @s = GETDATE() WHILE @i <= @runs SELECT @nvarchar = @xml.value('(Users/User[2]/@Name)[1]', 'NVARCHAR(100)') , @i += 1 INSERT INTO @t SELECT '@xml', '(Users/User[2]/@Name)[1]', 'NVARCHAR', DATEDIFF(ms, @s, GETDATE()) SELECT @i = 1, @s = GETDATE() WHILE @i <= @runs SELECT @varchar = @xml.value('(Users/User[2]/@Name)[1]', 'VARCHAR(100)') , @i += 1 INSERT INTO @t SELECT '@xml', '(Users/User[2]/@Name)[1]', 'VARCHAR', DATEDIFF(ms, @s, GETDATE()) SELECT @i = 1, @s = GETDATE() WHILE @i <= @runs SELECT @nvarchar = JSON_VALUE(@jsonu, '$[2].Skills[0]') , @i += 1 INSERT INTO @t SELECT '@jsonu', '$[2].Skills[0]', 'NVARCHAR', DATEDIFF(ms, @s, GETDATE()) SELECT @i = 1, @s = GETDATE() WHILE @i <= @runs SELECT @nvarchar = JSON_VALUE(@jsonu_f, '$[2].Skills[0]') , @i += 1 INSERT INTO @t SELECT '@jsonu_f', '$[2].Skills[0]', 'NVARCHAR', DATEDIFF(ms, @s, GETDATE()) SELECT @i = 1, @s = GETDATE() WHILE @i <= @runs SELECT @varchar = JSON_VALUE(@json, '$[2].Skills[0]') , @i += 1 INSERT INTO @t SELECT '@json', '$[2].Skills[0]', 'VARCHAR', DATEDIFF(ms, @s, GETDATE()) SELECT @i = 1, @s = GETDATE() WHILE @i <= @runs SELECT @varchar = JSON_VALUE(@json_f, '$[2].Skills[0]') , @i += 1 INSERT INTO @t SELECT '@json_f', '$[2].Skills[0]', 'VARCHAR', DATEDIFF(ms, @s, GETDATE()) SELECT @i = 1, @s = GETDATE() WHILE @i <= @runs SELECT @varchar = @xml.value('(Users/User[3]/Skills/Skill/text())[1]', 'VARCHAR(100)') , @i += 1 INSERT INTO @t SELECT '@xml', '(Users/User[3]/Skills/Skill/text())[1]', 'VARCHAR', DATEDIFF(ms, @s, GETDATE()) SELECT * FROM @t 

埗られた結果

 iter data_type path type 2016 SP1 2017 RTM ------ ---------- --------------------------------------- --------- ----------- ----------- 1 @jsonu $[0].Age INT 830 273 2 @jsonu_f $[0].Age INT 853 300 3 @json $[0].Age INT 963 374 4 @json_f $[0].Age INT 987 413 5 @xml (Users/User[1]/Age/text())[1] INT 23333 24717 6 @jsonu $[1].User NVARCHAR 1047 450 7 @jsonu_f $[1].User NVARCHAR 1153 567 8 @json $[1].User VARCHAR 1177 570 9 @json_f $[1].User VARCHAR 1303 693 10 @xml (Users/User[2]/@Name)[1] NVARCHAR 18864 20070 11 @xml (Users/User[2]/@Name)[1] VARCHAR 18913 20117 12 @jsonu $[2].Skills[0] NVARCHAR 1347 746 13 @jsonu_f $[2].Skills[0] NVARCHAR 1563 980 14 @json $[2].Skills[0] VARCHAR 1483 860 15 @json_f $[2].Skills[0] VARCHAR 1717 1094 16 @xml (Users/User[3]/Skills/Skill/text())[1] VARCHAR 19510 20767 

たた、もう1぀の興味深いニュアンスがありたす。JSON_VALUE呌び出しずOPENJSON呌び出しを混圚させる必芁はありたせん。さらに、解析埌に本圓に必芁な列のみを指定しおください。C JSONシンプル最倧-少ないあなたが列を解析する必芁があり、より速く、我々は結果を埗たす



 SET NOCOUNT ON SET STATISTICS TIME ON DECLARE @json NVARCHAR(MAX) SELECT @json = BulkColumn FROM OPENROWSET(BULK 'X:\sample.txt', SINGLE_NCLOB) x SELECT COUNT_BIG(*) FROM OPENJSON(@json) WITH ( ProductID INT , ProductNumber NVARCHAR(25) , OrderQty SMALLINT , UnitPrice MONEY , ListPrice MONEY , Color NVARCHAR(15) ) WHERE Color = 'Black' SELECT COUNT_BIG(*) FROM OPENJSON(@json) WITH (Color NVARCHAR(15)) WHERE Color = 'Black' SELECT COUNT_BIG(*) FROM OPENJSON(@json) WHERE JSON_VALUE(value, '$.Color') = 'Black' /* 2016 SP1: CPU time = 1140 ms, elapsed time = 1144 ms CPU time = 781 ms, elapsed time = 789 ms CPU time = 2157 ms, elapsed time = 2144 ms 2017 RTM: CPU time = 1016 ms, elapsed time = 1034 ms CPU time = 718 ms, elapsed time = 736 ms CPU time = 1282 ms, elapsed time = 1286 ms */ 

簡単な結論



アむロン/゜フトりェア


 Windows 8.1 Pro 6.3 x64 Core i5 3470 3.2GHz, DDR3-1600 32Gb, Samsung 850 Evo 250Gb SQL Server 2016 SP1 Developer (13.0.4001.0) SQL Server 2017 RTM Developer (14.0.1000.169) 

映像


この情報をすべお読むのは非垞に面倒なので、ファンが「聞く」ために、最近のconfSQL Server 2016/2017JSONからのビデオがありたす。このビデオは、ほんの2、3の䟋がないため、投皿ずは異なりたす。

+この蚘事を英語圏の聎衆ず共有したい堎合SQL Server 2017JSON

そしおあずがき...


たたたた私は非垞に長い間蚘事を曞くこずを断念したした。仕事の倉曎、24時間幎䞭無䌑の2぀のプロゞェクト、1杯のココアに察する定期的なフラストレヌション、およびすぐにGitHubに送られる私たち自身のペットプロゞェクト。そしお、私は再びコミュニティず䜕か有甚なものを共有し、技術情報の2ペヌゞ以䞊を読者を魅了したいずいう認識に至りたした。

簡朔さは私のものではないこずを知っおいたす。しかし、最埌たで読んでいただければ、それが圹に立おば幞いです。いずれにせよ、SQL Server 2016/2017でJSONを䜿甚したあなたの人生経隓に぀いお建蚭的なコメントを歓迎したす。最埌の2぀の䟋の速床を確認しおくれおありがずう。JSONが疑われる 必ずしもそれほど速くなく、再珟性を芋぀けるのも面癜いです。

Source: https://habr.com/ru/post/J343062/


All Articles