【问题标题】:Extract value from double escaped JSON column in SQL从 SQL 中的双转义 JSON 列中提取值
【发布时间】:2021-06-08 16:55:21
【问题描述】:

我有 ports_list 表,下面有 ports 列:

ports intended_output
"[{\"id\":193,\"name\":\"PORT C\"}]" PORT C
"[{\"id\":204,\"name\":\"PORT D\"}]" PORT D
"[{\"id\":45,\"name\":\"PORT I\"},{\"id\":193,\"name\":\"PORT C\"},{\"id\":204,\"name\":\"PORT D\"},{\"id\":271,\"name\":\"PORT G\"}]" PORT I, PORT C, PORT D, PORT G

当一行中只有一个端口值时,我使用这种方法并以某种方式设法获得了结果,不幸的是它无法连续满足多个端口值:

select ports,
REPLACE( substring(ports, CHARINDEX('"name\":\', ports, 1)+10, LEN(ports)-10), 
                        '\"}]"', 
                        ''
                       )
from ports_list
ports output
"[{\"id\":193,\"name\":\"PORT C\"}]" PORT C
"[{\"id\":204,\"name\":\"PORT D\"}]" PORT D
"[{\"id\":45,\"name\":\"PORT I\"},{\"id\":193,\"name\":\"PORT C\"},{\"id\":204,\"name\":\"PORT D\"},{\"id\":271,\"name\":\"PORT G\"}]" PORT I"},{"id":193,"name":"PORT C"},{"id":204,"name":"PORT D"},{"id":271,"name":"PORT G"}]"

有人可以帮我按照第一个表获得预期的输出吗?

【问题讨论】:

  • 你的关系型数据库是什么?部分支持 JSON
  • 编写自定义函数。你用什么数据库?你用什么语言编码?添加这些标签。
  • 我使用 Microsoft sql server。添加了标签。
  • 您的 SQL Server 版本是多少?
  • 这是我的版本 Microsoft SQL Server 2012 (SP4) (KB4018073) - 11.0.7001.0 (X64) @Zhorov

标签: sql sql-server string substring


【解决方案1】:

您的ports 列是一个双重转义的 JSON,换句话说,它包含一个表示 JSON 对象的 JSON 转义字符串。

所以我们只需要将它恢复为常规 JSON 即可查询。让我们把它塞进一个[] JSON 数组,然后传递给JSON_VALUE

select ports,
    STRING_AGG(j.name, ', ')
from ports_list
outer apply OPENJSON(JSON_VALUE('[' + ports + ']', '$[0]'))
    with (name nvarchar(100) '$.name') as j
group by ports;

【讨论】:

  • 我尝试使用您的方法,这就是我得到的 'JSON_VALUE' 不是公认的内置函数名称。我正在使用 Microsoft SQL Server 2012。
  • 很抱歉刚刚意识到您有多个要聚合的值。另外,是的,您可能应该从 SQL2012 升级,它已于 2016 年终止使用!
  • 不幸的是,升级它超出了我的控制范围。这是我公司的决定
【解决方案2】:

您创建了一个解决方案,仅在存在一个端口时才解析。

函数 CharIndex 总是返回 '"name":' 的第一个字符,函数 SUBSTRING 总是返回减去最后十个字符的字符串。

解决问题的一种形式是创建用户函数以返回所有端口的相关性。

尝试创建下面的函数

CREATE FUNCTION uf_findPorts
(
    @string nvarchar(max)
)
RETURNS nvarchar(MAX)
AS
BEGIN
    DECLARE
          @length INT ,
          @aux INT ,
          @return NVARCHAR(MAX)
           

  
  SET @length = LEN(@string)
  SET @aux = 1
  SET @return = ''


  WHILE(CHARINDEX('"name\":\', @string,@aux) != 0)
  BEGIN 
    SET @return = @return + '  ' + REPLACE( substring(@string,
            CHARINDEX('"name\":\', @string,@aux)+10, 
               6), 
                        '\"}]"', 
                        ''
                       )
    
    SET @string = SUBSTRING(@string,CHARINDEX('"name\":\', @string,@aux) + 6,LEN(@string) )
    

  END

  RETURN @return
 

END
GO

测试:

select ports,dbo.uf_findPorts(ports)
from ports_list

SELECT dbo.uf_findPorts('[{\"id\":45,\"name\":\"PORT I\"},{\"id\":193,\"name\":\"PORT C\"},{\"id\":204,\"name\":\"PORT D\"},{\"id\":271,\"name\":\"PORT G\"}]')

SELECT dbo.uf_findPorts('"[{\"id\":193,\"name\":\"PORT C\"}]"')

OBS:如果存在任何写入错误,您可以纠正我

【讨论】:

  • 嗨,它很接近但我的端口名称并不总是 6 个字符。例如,如果我的港口名称是广州,则使用您的方法只需要前 6 个字符“Guangz”
  • 另外,当一行中有多个值时,我希望它显示为“PORT A, PORT B, PORT C”,而不是您的输出 PORT A PORT B PORT C
  • @rain123 你可以在 return + ' , ' + REPLACE( substring(string, CHARINDEX('"name\":\', string,aux)+10, 6), ' 中包含逗号\"}]"', '' )
【解决方案3】:

感谢那些试图提供帮助的人。

如果以后有人遇到类似问题,请在此处发布我的解决方案:

 select ports = replace(replace(replace(dbo.fnRemovePatternFromString(dbo.fnRemovePatternFromString(dbo.fnRemovePatternFromString(dbo.fnRemovePatternFromString(ports,'%[\"{}]%',1),'%name:%',5),'%id:%',3),'%[0-9]%',1),'[,',''),',,',','),']','')
    from ports_list

【讨论】:

    猜你喜欢
    • 2023-04-02
    • 2021-07-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-05-21
    • 1970-01-01
    • 2020-09-21
    相关资源
    最近更新 更多