2010-05-19 76 views
92

解析TSQL中的JSON有可能嗎? 我不是說要創建一個JSON字符串,我的意思是解析一個傳入的JSON字符串作爲參數。解析TSQL中的JSON

回答

46

更新:從SQL Server 2016開始parsing JSON in TSQL is now possible

本地沒有支持。你將不得不使用CLR。就像這樣簡單,除非你有一個巨大的受虐狂條紋,並希望在SQL中編寫JSON解析器。通常,民間人士要求從數據庫輸出JSON並在互聯網上有示例。但成爲一個數據庫?

+0

在數據庫調用者端解析JSON可能是一個好主意:) – 2010-05-20 04:25:45

+55

JSON是一個非常簡單的協議,所以它確實不需要大量的受虐狂。一旦你有了它,你可以爲你的所有JSON使用一個例程。無論如何,我已經爲你做了這裏http://www.simple-talk.com/sql/t-sql-programming/consuming-json-strings-in-sql-server/ – 2010-11-15 18:04:56

+9

菲爾因素:我一直閱讀你的文章多年。如果你還沒有寫過這篇文章*今天*我可能會在6個月前引用它的時候我回答了... – gbn 2010-11-15 18:10:30

212

我似乎有一個巨大的受虐狂條紋,因爲我寫了一個JSON解析器。它將JSON文檔轉換爲SQL Adjacency列表表格,該表格很容易用於更新數據表格。實際上,我已經做得更糟了,因爲我已經完成了代碼來完成相反的過程,即從層級表到JSON字符串

文章和代碼在這裏:Consuming Json strings in SQL server

Select * from parseJSON('{ 
    "Person": 
    { 
    "firstName": "John", 
    "lastName": "Smith", 
    "age": 25, 
    "Address": 
    { 
     "streetAddress":"21 2nd Street", 
     "city":"New York", 
     "state":"NY", 
     "postalCode":"10021" 
    }, 
    "PhoneNumbers": 
    { 
     "home":"212 555-1234", 
     "fax":"646 555-4567" 
    } 
    } 
} 
') 

要獲取:

enter image description here

+0

這是一些很棒的功能,但確實有一些限制,例如從負數剝離「 - 」。 – Gavin 2012-04-25 22:02:02

+0

很酷!你在腳本中有一個錯字:IF OBJECT_ID(N'dbo.parseJSON')不爲NULL DROP FUNCTION dbo.JSONEscaped GO - 應該在IF測試中測試dbo.JSONEscaped。 – isapir 2013-04-18 18:36:09

+0

@phil在大數據的情況下,dbo.parseJSON工作非常緩慢。那麼我們可以通過使用其他方法來縮短這個時間嗎? – cracker 2014-04-16 04:48:28

-1

我看到這個漂亮整潔的文章......所以,如果你是這樣的:

CREATE PROC [dbo].[spUpdateMarks] 
    @inputJSON VARCHAR(MAX) -- '[{"ID":"1","C":"60","CPP":"60","CS":"60"}]' 
AS 
BEGIN 
    -- Temp table to hold the parsed data 
    DECLARE @TempTableVariable TABLE(
     element_id INT, 
     sequenceNo INT, 
     parent_ID INT, 
     [Object_ID] INT, 
     [NAME] NVARCHAR(2000), 
     StringValue NVARCHAR(MAX), 
     ValueType NVARCHAR(10) 
    ) 
    -- Parse JSON string into a temp table 
    INSERT INTO @TempTableVariable 
    SELECT * FROM parseJSON(@inputJSON) 
END 

嘗試看這裏:

https://www.simple-talk.com/sql/t-sql-programming/consuming-json-strings-in-sql-server/

有關於這一個完整的ASP.Net項目位置: http://www.codeproject.com/Articles/788208/Update-Multiple-Rows-of-GridView-using-JSON-in-ASP

+6

這篇文章的原作者已經有了一個答案:http://stackoverflow.com/a/4187412/389424 – janv8000 2015-01-30 08:14:12

23

最後SQL服務器將於2016年增加了原生JSON支持!

編號:

在SQL Server 2016的其他功能包括:

  • 爲行級安全性和動態數據屏蔽其他安全增強功能,完善了與始終
    加密我們的安全投資。
  • AlwaysOn的改進可實現更強大的可用性和災難恢復功能,同時具有多個同步副本和次級負載平衡功能
  • 本機JSON支持爲您的多種類型的數據提供更好的性能和支持。
  • SQL Server企業信息管理(EIM)工具和分析服務在性能,可用性和可伸縮性方面得到升級。
  • 更快的混合備份,高可用性和災難恢復方案可將您的本地數據庫備份到Azure
    並將您的SQL Server AlwaysOn副本放入Azure中。

Announcment:http://blogs.technet.com/b/dataplatforminsider/archive/2015/05/04/sql-server-2016-public-preview-coming-this-summer.aspx

特點的博客文章:http://blogs.msdn.com/b/jocapc/archive/2015/05/16/json-support-in-sql-server-2016.aspx

+2

很可能SQL Server 2016 CTP 3將使用OpenJSON語法來支持JSON到SQL Server:http:// www .kodyaz.com/t-sql/sql-server-2016-openjson-error.aspx – Eralper 2015-09-04 11:11:35

4

我也有一個巨大的自虐連勝爲我寫的又一個JSON解析器。這個使用程序方法。它使用類似的SQL層次結構列表來存儲解析的數據。另外,在包有:

  • 相反的過程:從層次結構,以JSON
  • 查詢功能:從JSON對象

請隨意使用,並有它的樂趣

取特定值

http://www.codeproject.com/Articles/1000953/JSON-for-Sql-Server-Part

+0

+1謝謝,效果非常好,比PhilFactor版本更好。儘管我不得不爲SQL Server 2008稍微貶低它(沒有'iif'函數或'OFFSET') – Geronimo 2016-07-15 23:53:28

4
CREATE FUNCTION dbo.parseJSON(@JSON NVARCHAR(MAX)) 
RETURNS @hierarchy TABLE 
    (
    element_id INT IDENTITY(1, 1) NOT NULL, /* internal surrogate primary key gives the order of parsing and the list order */ 
    sequenceNo [int] NULL, /* the place in the sequence for the element */ 
    parent_ID INT,/* if the element has a parent then it is in this column. The document is the ultimate parent, so you can get the structure from recursing from the document */ 
    Object_ID INT,/* each list or object has an object id. This ties all elements to a parent. Lists are treated as objects here */ 
    NAME NVARCHAR(2000),/* the name of the object */ 
    StringValue NVARCHAR(MAX) NOT NULL,/*the string representation of the value of the element. */ 
    ValueType VARCHAR(10) NOT null /* the declared type of the value represented as a string in StringValue*/ 
) 
AS 
BEGIN 
    DECLARE 
    @FirstObject INT, --the index of the first open bracket found in the JSON string 
    @OpenDelimiter INT,--the index of the next open bracket found in the JSON string 
    @NextOpenDelimiter INT,--the index of subsequent open bracket found in the JSON string 
    @NextCloseDelimiter INT,--the index of subsequent close bracket found in the JSON string 
    @Type NVARCHAR(10),--whether it denotes an object or an array 
    @NextCloseDelimiterChar CHAR(1),--either a '}' or a ']' 
    @Contents NVARCHAR(MAX), --the unparsed contents of the bracketed expression 
    @Start INT, --index of the start of the token that you are parsing 
    @end INT,--index of the end of the token that you are parsing 
    @param INT,--the parameter at the end of the next Object/Array token 
    @EndOfName INT,--the index of the start of the parameter at end of Object/Array token 
    @token NVARCHAR(200),--either a string or object 
    @value NVARCHAR(MAX), -- the value as a string 
    @SequenceNo int, -- the sequence number within a list 
    @name NVARCHAR(200), --the name as a string 
    @parent_ID INT,--the next parent ID to allocate 
    @lenJSON INT,--the current length of the JSON String 
    @characters NCHAR(36),--used to convert hex to decimal 
    @result BIGINT,--the value of the hex symbol being parsed 
    @index SMALLINT,--used for parsing the hex value 
    @Escape INT --the index of the next escape character 


    DECLARE @Strings TABLE /* in this temporary table we keep all strings, even the names of the elements, since they are 'escaped' in a different way, and may contain, unescaped, brackets denoting objects or lists. These are replaced in the JSON string by tokens representing the string */ 
    (
    String_ID INT IDENTITY(1, 1), 
    StringValue NVARCHAR(MAX) 
    ) 
    SELECT--initialise the characters to convert hex to ascii 
    @characters='abcdefghijklmnopqrstuvwxyz', 
    @SequenceNo=0, --set the sequence no. to something sensible. 
    /* firstly we process all strings. This is done because [{} and ] aren't escaped in strings, which complicates an iterative parse. */ 
    @parent_ID=0; 
    WHILE 1=1 --forever until there is nothing more to do 
    BEGIN 
     SELECT 
     @start=PATINDEX('%[^a-zA-Z]["]%', @json collate SQL_Latin1_General_CP850_Bin);--next delimited string 
     IF @start=0 BREAK --no more so drop through the WHILE loop 
     IF SUBSTRING(@json, @start+1, 1)='"' 
     BEGIN --Delimited Name 
      SET @[email protected]+1; 
      SET @end=PATINDEX('%[^\]["]%', RIGHT(@json, LEN(@json+'|')[email protected]) collate SQL_Latin1_General_CP850_Bin); 
     END 
     IF @end=0 --no end delimiter to last string 
     BREAK --no more 
     SELECT @token=SUBSTRING(@json, @start+1, @end-1) 
     --now put in the escaped control characters 
     SELECT @token=REPLACE(@token, FROMString, TOString) 
     FROM 
     (SELECT 
      '\"' AS FromString, '"' AS ToString 
     UNION ALL SELECT '\\', '\' 
     UNION ALL SELECT '\/', '/' 
     UNION ALL SELECT '\b', CHAR(08) 
     UNION ALL SELECT '\f', CHAR(12) 
     UNION ALL SELECT '\n', CHAR(10) 
     UNION ALL SELECT '\r', CHAR(13) 
     UNION ALL SELECT '\t', CHAR(09) 
     ) substitutions 
     SELECT @result=0, @escape=1 
    --Begin to take out any hex escape codes 
     WHILE @escape>0 
     BEGIN 
      SELECT @index=0, 
      --find the next hex escape sequence 
      @escape=PATINDEX('%\x[0-9a-f][0-9a-f][0-9a-f][0-9a-f]%', @token collate SQL_Latin1_General_CP850_Bin) 
      IF @escape>0 --if there is one 
      BEGIN 
       WHILE @index<4 --there are always four digits to a \x sequence 
       BEGIN 
        SELECT --determine its value 
        @[email protected]+POWER(16, @index) 
        *(CHARINDEX(SUBSTRING(@token, @[email protected], 1), 
           @characters)-1), @[email protected]+1 ; 

       END 
       -- and replace the hex sequence by its unicode value 
       SELECT @token=STUFF(@token, @escape, 6, NCHAR(@result)) 
      END 
     END 
     --now store the string away 
     INSERT INTO @Strings (StringValue) SELECT @token 
     -- and replace the string with a token 
     SELECT @JSON=STUFF(@json, @start, @end+1, 
        '@string'+CONVERT(NVARCHAR(5), @@identity)) 
    END 
    -- all strings are now removed. Now we find the first leaf. 
    WHILE 1=1 --forever until there is nothing more to do 
    BEGIN 

    SELECT @[email protected]_ID+1 
    --find the first object or list by looking for the open bracket 
    SELECT @FirstObject=PATINDEX('%[{[[]%', @json collate SQL_Latin1_General_CP850_Bin)--object or array 
    IF @FirstObject = 0 BREAK 
    IF (SUBSTRING(@json, @FirstObject, 1)='{') 
    SELECT @NextCloseDelimiterChar='}', @type='object' 
    ELSE 
    SELECT @NextCloseDelimiterChar=']', @type='array' 
    SELECT @[email protected] 

    WHILE 1=1 --find the innermost object or list... 
    BEGIN 
     SELECT 
     @lenJSON=LEN(@JSON+'|')-1 
    --find the matching close-delimiter proceeding after the open-delimiter 
     SELECT 
     @NextCloseDelimiter=CHARINDEX(@NextCloseDelimiterChar, @json, 
             @OpenDelimiter+1) 
    --is there an intervening open-delimiter of either type 
     SELECT @NextOpenDelimiter=PATINDEX('%[{[[]%', 
      RIGHT(@json, @[email protected])collate SQL_Latin1_General_CP850_Bin)--object 
     IF @NextOpenDelimiter=0 
     BREAK 
     SELECT @[email protected][email protected] 
     IF @NextCloseDelimiter<@NextOpenDelimiter 
     BREAK 
     IF SUBSTRING(@json, @NextOpenDelimiter, 1)='{' 
     SELECT @NextCloseDelimiterChar='}', @type='object' 
     ELSE 
     SELECT @NextCloseDelimiterChar=']', @type='array' 
     SELECT @[email protected] 
    END 
    ---and parse out the list or name/value pairs 
    SELECT 
    @contents=SUBSTRING(@json, @OpenDelimiter+1, 
         @[email protected]) 
    SELECT 
    @JSON=STUFF(@json, @OpenDelimiter, 
       @[email protected]+1, 
       '@'[email protected]+CONVERT(NVARCHAR(5), @parent_ID)) 
    WHILE (PATINDEX('%[[email protected]+.e]%', @contents collate SQL_Latin1_General_CP850_Bin))<>0 
    BEGIN 
     IF @Type='Object' --it will be a 0-n list containing a string followed by a string, number,boolean, or null 
     BEGIN 
      SELECT 
      @SequenceNo=0,@end=CHARINDEX(':', ' '[email protected])--if there is anything, it will be a string-based name. 
      SELECT @start=PATINDEX('%[^[email protected]][@]%', ' '[email protected] collate SQL_Latin1_General_CP850_Bin)--AAAAAAAA 
      SELECT @token=SUBSTRING(' '[email protected], @start+1, @[email protected]), 
      @endofname=PATINDEX('%[0-9]%', @token collate SQL_Latin1_General_CP850_Bin), 
      @param=RIGHT(@token, LEN(@token)[email protected]+1) 
      SELECT 
      @token=LEFT(@token, @endofname-1), 
      @Contents=RIGHT(' '[email protected], LEN(' '[email protected]+'|')[email protected]) 
      SELECT @name=stringvalue FROM @strings 
      WHERE [email protected] --fetch the name 
     END 
     ELSE 
     SELECT @Name=null,@[email protected]+1 
     SELECT 
     @end=CHARINDEX(',', @contents)-- a string-token, object-token, list-token, number,boolean, or null 
     IF @end=0 
     SELECT @end=PATINDEX('%[[email protected]+.e][^[email protected]+.e]%', @Contents+' ' collate SQL_Latin1_General_CP850_Bin) 
      +1 
     SELECT 
     @start=PATINDEX('%[^[email protected]+.e][[email protected]+.e]%', ' '[email protected] collate SQL_Latin1_General_CP850_Bin) 
     --select @start,@end, LEN(@contents+'|'), @contents 
     SELECT 
     @Value=RTRIM(SUBSTRING(@contents, @start, @[email protected])), 
     @Contents=RIGHT(@contents+' ', LEN(@contents+'|')[email protected]) 
     IF SUBSTRING(@value, 1, 7)='@object' 
     INSERT INTO @hierarchy 
      (NAME, SequenceNo, parent_ID, StringValue, Object_ID, ValueType) 
      SELECT @name, @SequenceNo, @parent_ID, SUBSTRING(@value, 8, 5), 
      SUBSTRING(@value, 8, 5), 'object' 
     ELSE 
     IF SUBSTRING(@value, 1, 6)='@array' 
      INSERT INTO @hierarchy 
      (NAME, SequenceNo, parent_ID, StringValue, Object_ID, ValueType) 
      SELECT @name, @SequenceNo, @parent_ID, SUBSTRING(@value, 7, 5), 
       SUBSTRING(@value, 7, 5), 'array' 
     ELSE 
      IF SUBSTRING(@value, 1, 7)='@string' 
      INSERT INTO @hierarchy 
       (NAME, SequenceNo, parent_ID, StringValue, ValueType) 
       SELECT @name, @SequenceNo, @parent_ID, stringvalue, 'string' 
       FROM @strings 
       WHERE string_id=SUBSTRING(@value, 8, 5) 
      ELSE 
      IF @value IN ('true', 'false') 
       INSERT INTO @hierarchy 
       (NAME, SequenceNo, parent_ID, StringValue, ValueType) 
       SELECT @name, @SequenceNo, @parent_ID, @value, 'boolean' 
      ELSE 
       IF @value='null' 
       INSERT INTO @hierarchy 
        (NAME, SequenceNo, parent_ID, StringValue, ValueType) 
        SELECT @name, @SequenceNo, @parent_ID, @value, 'null' 
       ELSE 
       IF PATINDEX('%[^0-9]%', @value collate SQL_Latin1_General_CP850_Bin)>0 
        INSERT INTO @hierarchy 
        (NAME, SequenceNo, parent_ID, StringValue, ValueType) 
        SELECT @name, @SequenceNo, @parent_ID, @value, 'real' 
       ELSE 
        INSERT INTO @hierarchy 
        (NAME, SequenceNo, parent_ID, StringValue, ValueType) 
        SELECT @name, @SequenceNo, @parent_ID, @value, 'int' 
     if @Contents=' ' Select @SequenceNo=0 
    END 
    END 
INSERT INTO @hierarchy (NAME, SequenceNo, parent_ID, StringValue, Object_ID, ValueType) 
    SELECT '-',1, NULL, '', @parent_id-1, @type 
-- 
    RETURN 
END 
GO 

---磷酸酶JSON

Declare @pars varchar(MAX) = 
' {"shapes":[{"type":"polygon","geofenceName":"","geofenceDescription":"", 
"geofenceCategory":"1","color":"#1E90FF","paths":[{"path":[{ 
"lat":"26.096254906968525","lon":"65.709228515625"} 
,{"lat":"28.38173504322308","lon":"66.741943359375"} 
,{"lat":"26.765230565697482","lon":"68.983154296875"} 
,{"lat":"26.254009699865737","lon":"68.609619140625"} 
,{"lat":"25.997549919572112","lon":"68.104248046875"} 
,{"lat":"26.843677401113002","lon":"67.115478515625"} 
,{"lat":"25.363882272740255","lon":"65.819091796875"}]}]}]}' 
Select * from parseJSON(@pars) AS MyResult 
1

SQL服務器2016支持json data使用OPENJSON解析。您可以使用OPENJSONjson data映射到行和列。

json Data

[ 
{ "id" : 2,"name": "John"}, 
{ "id" : 5,"name": "John"} 
] 

這裏是你如何處理JSON在SQL

//@pJson is json data passed from code. 

INSERT INTO YourTable (id, Name) 
SELECT id, name 
FROM OPENJSON(@pJson) 
WITH (id int, 
     name nvarchar(max)) 

Here是一個詳細的文章覆蓋這個話題。