导航

  • 前言:
  • 开始干活:
  • 0.预告
  • 1.首先先建立一个“通用的json解析自定义函数”。(这个代码是网络上找到的成熟代码)
  • 2. 重点讲解一下 函数 “parseJSON( )”的用法
  • 3.学会了函数 “parseJSON( )”的用法,那么如何方便的在以后的查询语句中使用它呢?
  • 预先准备测试用的数据表和数据
  • 开始制作“专用函数”
  • 如何使用“专用函数”
  • 最后,上价值!!!
  • 附录:函数 “parseJSON( )”的代码


前言:

需求是这样的,女神遇到了一个具体问题。就是sqlserver数据库中有些数据是json字符串。
但是由于她们公司的sqlserver数据库版本比较低,没有自带的解析方法。
所以需要用自定义函数查询。

开始干活:

0.预告

我在本帖子中会创建两个自定义函数。
一个名字叫做parseJSON是通用json解析函数。另外一个需要根据业务情况,为带有json的业务表做一个专用的自定义函数。

1.首先先建立一个“通用的json解析自定义函数”。(这个代码是网络上找到的成熟代码)

直接粘贴代码

CREATE FUNCTION [dbo].[parseJSON] ( @JSON NVARCHAR(MAX) )
。。。。
这个自定义函数内容太多了,为了阅读方便,我贴在帖子尾部了。
END

2. 重点讲解一下 函数 “parseJSON( )”的用法

首先通过代码阅读了解到,函数 “parseJSON( )”的返回结果是一个表(结果集)。
我们可以通过sql测试它。
假设我们要解析的json是:

{"channelCode":"A003","random":"124317f0-8e42-4c9c-88b0-cabacc8a8079"}

我们可以直接用sql对这个json字符串进行解析。
函数 “parseJSON( )”的参数要求“输入json字符串”,位置放在 “from”后面当作 “表”用。一定要加 “dbo.”
代码如下

select *
from dbo.parseJSON('{"channelCode":"A003","random":"124317f0-8e42-4c9c-88b0-cabacc8a8079"}')
;

得到的结果如下图是一个固定结构的表。

sql server 某个字段 json sql server json 解析_自定义函数


由此可以看到,json被打开,内部的key和value分别放在两个不同的列。

我们可以根据where条件去查询它,比如如下语句:

select StringValue
from dbo.parseJSON('{"channelCode":"A003","random":"124317f0-8e42-4c9c-88b0-cabacc8a8079"}')
where NAME = 'channelCode'
;

得到结果是:

sql server 某个字段 json sql server json 解析_sqlserver_02

3.学会了函数 “parseJSON( )”的用法,那么如何方便的在以后的查询语句中使用它呢?

由于函数 “parseJSON( )”的返回结果是一个表。而我们实际上多数情况下希望在select中向查询普通字段一样查询json内的参数。
所以我们需要用一些“土办法”。
这里我选择的方案是,针对具体的业务表,做一个专用与这个表以后查询的专用函数。

预先准备测试用的数据表和数据

教学用测试表的表结构:

-- 创建一个带有json数据的表测试表table_j
create table table_j
(
    id  int not null,
    val varchar(800)
)
go

教学用测试数据:

-- 插入3条测试数据
INSERT INTO dbo.table_j (id, val)
VALUES (1, N'{"channelCode":"A001","random":"5e590a2b-a295-4ac6-af0b-92528a84a3ee"}');
INSERT INTO dbo.table_j (id, val)
VALUES (2, N'{"channelCode":"A002","random":"de823b5a-19db-4492-a144-2f66945b1ce4"}');
INSERT INTO dbo.table_j (id, val)
VALUES (3, N'{"channelCode":"A003","random":"124317f0-8e42-4c9c-88b0-cabacc8a8079"}');

开始制作“专用函数”

我们设计一个函数 叫 “ parseJSON_table_j( ) ”,
设计他有两个参数,
参数1 用来做查询数据的唯一id。
参数2 用来告诉我们要查json内的哪个值。
(这里假定我们是知道表结构并知道哪个字段存储了json数据。)

-- 为了方便使用 专门为测试表测试表table_j 做了第二个自定义函数
-- 它的功能是 通过测试表table_j的唯一key 查询对应json数据里面 某一个节点的值
-- 返回字符串类型
CREATE FUNCTION dbo.parseJSON_table_j(
    @param1_id INT, -- 唯一标识
    @param2_jsonkey nvarchar(max) -- json中的节点名称
)
    RETURNS nvarchar(max)
AS
BEGIN

    DECLARE @json nvarchar(max)
    DECLARE @result nvarchar(max)
    
	-- 这里直接硬编码写死了(表名 table_j )和(存放json字符串的字段 val),在实际使用中要根据查询json所在表进行修改。 
    select @json = val from table_j where id = @param1_id

    select @result = StringValue
    from dbo.parseJSON(@json)
    where NAME = @param2_jsonkey

    RETURN @result
END

如何使用“专用函数”

直接通过传参是方式可以查询。比如下面的sql脚本:

-- 查一下试试
SELECT id,
       val,
       dbo.parseJSON_table_j(id, 'channelCode') as channelCode,
       dbo.parseJSON_table_j(id, 'random')      as random
from table_j
;

得到的结果如下,完美的把json字符串解析到了查询语句中。

sql server 某个字段 json sql server json 解析_数据库_03

最后,上价值!!!

我家女神天下第一美,不接受任何反驳。

女神的毒唯,爱宠,我家小公主,妞妞镇楼。(话说谁能告诉我毒唯是什么。。。。。。。。)

sql server 某个字段 json sql server json 解析_数据库_04

附录:函数 “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(max) ,--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 = '0123456789abcdefghijklmnopqrstuvwxyz',
          @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 @start = @Start + 1;
                    SET @end = PATINDEX('%[^\]["]%',
                                        RIGHT(@json,
                                              LEN(@json + '|') - @start) 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
                                           @result = @result + POWER(16,
                                                                     @index)
                                               * (CHARINDEX(SUBSTRING(@token,
                                                                      @escape + 2 + 3
                                                                          - @index, 1),
                                                            @characters) - 1),
                                           @index = @index + 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 @parent_ID = @parent_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 @OpenDelimiter = @firstObject

            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,
                                                               @lenJSON
                                                                   -
                                                               @OpenDelimiter) COLLATE SQL_Latin1_General_CP850_Bin)--object
                    IF @NextOpenDelimiter = 0
                        BREAK
                    SELECT @NextOpenDelimiter = @NextOpenDelimiter
                        + @OpenDelimiter
                    IF @NextCloseDelimiter < @NextOpenDelimiter
                        BREAK
                    IF SUBSTRING(@json, @NextOpenDelimiter, 1) = '{'
                        SELECT @NextCloseDelimiterChar = '}',
                               @type = 'object'
                    ELSE
                        SELECT @NextCloseDelimiterChar = ']',
                               @type = 'array'
                    SELECT @OpenDelimiter = @NextOpenDelimiter
                END
            ---and parse out the list or name/value pairs
            SELECT @contents = SUBSTRING(@json, @OpenDelimiter + 1,
                                         @NextCloseDelimiter
                                             - @OpenDelimiter - 1)
            SELECT @JSON = STUFF(@json, @OpenDelimiter,
                                 @NextCloseDelimiter - @OpenDelimiter + 1,
                                 '@' + @type
                                     + CONVERT(NVARCHAR(5), @parent_ID))
            WHILE (PATINDEX('%[A-Za-z0-9@+.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(':', ' ' + @contents)--if there is anything, it will be a string-based name.
                            SELECT @start = PATINDEX('%[^A-Za-z@][@]%',
                                                     ' ' + @contents COLLATE SQL_Latin1_General_CP850_Bin)--AAAAAAAA
                            SELECT @token = SUBSTRING(' ' + @contents,
                                                      @start + 1,
                                                      @End - @Start - 1),
                                   @endofname = PATINDEX('%[0-9]%',
                                                         @token COLLATE SQL_Latin1_General_CP850_Bin),
                                   @param = RIGHT(@token,
                                                  LEN(@token)
                                                      - @endofname + 1)
                            SELECT @token = LEFT(@token, @endofname - 1),
                                   @Contents = RIGHT(' ' + @contents,
                                                     LEN(' ' + @contents
                                                         + '|') - @end
                                                         - 1)
                            SELECT @name = stringvalue
                            FROM @strings
                            WHERE string_id = @param --fetch the name
                        END
                    ELSE
                        SELECT @Name = NULL,
                               @SequenceNo = @SequenceNo + 1
                    SELECT @end = CHARINDEX(',', @contents)-- a string-token, object-token, list-token, number,boolean, or null
                    IF @end = 0
                        BEGIN
                            SELECT @end = PATINDEX('%[A-Za-z0-9@+.e][^A-Za-z0-9@+.e]%',
                                                   @Contents + ' ' COLLATE SQL_Latin1_General_CP850_Bin)
                                + 1
                        END
                    SELECT @start = PATINDEX('%[^-A-Za-z0-9@+.e][-A-Za-z0-9@+.e]%',
                                             ' ' + @contents COLLATE SQL_Latin1_General_CP850_Bin)
                    --select @start,@end, LEN(@contents+'|'), @contents  
                    SELECT @Value = RTRIM(SUBSTRING(@contents, @start,
                                                    @End - @Start)),
                           @Contents = RIGHT(@contents + ' ',
                                             LEN(@contents + '|') - @end)
                    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