2017-03-03 103 views
0

在我的表empid列中包含像下面的json格式的值。在SQL SERVER 2012中讀取JSON

row 1->[{"id":85574},{"id":169794}] 
row 2->[{"id":180313},{"id":169796},{"id":173467}] 

我想像下面的輸出一樣一行一行地顯示empid。

輸出

empid 
85574 
169794 
180313 
169796 
173467 

我想下面的查詢,但它顯示子查詢返回多個值

select 
(
SELECT StringValue from parseJSON(v1.empid) 
where Name='id' 
) from emp v1 where year=1994 

回答

1

2012不支持JSON,但有一點解析和字符串操作:

Declare @YourTable table (id int,JSON varchar(max)) 
Insert Into @YourTable values 
(1,'[{"id":85574},{"id":169794}]'), 
(2,'[{"id":180313},{"id":169796},{"id":173467}]') 

Select A.ID 
     ,EmpID = replace(replace(replace(replace(replace(B.RetVal,'[',''),'{',''),'}',''),']',''),'"id":','') 
From @YourTable A 
Cross Apply (
       Select RetSeq = Row_Number() over (Order By (Select null)) 
         ,RetVal = LTrim(RTrim(B.i.value('(./text())[1]', 'varchar(max)'))) 
       From (Select x = Cast('<x>' + replace((Select replace(A.JSON,'},{','§§Split§§') as [*] For XML Path('')),'§§Split§§','</x><x>')+'</x>' as xml).query('.')) as X 
       Cross Apply x.nodes('x') AS B(i) 
      ) B 
Where B.RetVal Like '%"id":%' 

返回

ID EmpID 
1 85574 
1 169794 
2 180313 
2 169796 
2 173467 
+0

我在2016年使用parseJSON函數在下面的查詢中使用了我的上面的表格,但它返回的子查詢返回的值超過1。 select ( 從parseJSON(v1.empid) 中選擇StringValue,其中Name ='id' )from emp v1 where year = 1994 – Ram

0

有由PhilFactor寫入一個功能可用於使用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

粘貼ParseJson確定指標

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