【问题标题】:Parsing JSON with SQL: How to extract a record within a JSON object?使用 SQL 解析 JSON:如何在 JSON 对象中提取记录?
【发布时间】:2015-07-29 19:05:03
【问题描述】:

我正在查看 SQL Server 表中的大约 13,000 行,并尝试解析出存储为 json 的列中的某些值。

json 列值如下所示:

..."http://www.companyurl.com","FoundedYear":"2007","Status":"Private","CompanySize":"51-200","TagLine":"We build software we believe in","Origi...

我想提取“CompanySize”的值,但并非所有行都包含此属性。其他复杂因素:

  • 我不确定“CompanySize”参数中有多少个可能的值。
  • “CompanySize”后面并不总是跟“TagLine”参数。

我肯定知道的一条规则:CompanySize 值始终是一个长度未知的字符串,它跟在 varchar 字符串 "CompanySize":" 之后,并在下一个 "," 字符串之前终止。

理想情况下,我们会完全升级到 SQL Server 2016,这样我就可以利用 SQL Server's JSON support,但事实并非如此。

【问题讨论】:

  • 由于不可能对此类数据进行索引,也没有任何方法可以在不完全扫描您的表的情况下返回结果,除非相关字符串非常大,您想避免这样做 - 我' d 只返回整个字符串并在适当的环境中处理它。
  • @Amit 所说的,甚至更好:实际为这些数据创建一个表或模式,并首先将其正确存储。
  • @Amit 你为什么假设这个值被过滤了?无论如何,索引可能不相关。获得这个值也相当容易,13k 行几乎不是任何数据。
  • @JoelCoehoorn 你在这里做了一些可能不公平的假设。可能是他们已经在应用层处理了这些数据,但是现在有一个特殊的原因需要数据库中的一个最初没有考虑过的值,并且不需要将数据存储两次。我曾在一个系统上工作过,其中数据以 JSON 形式输入,因为开发人员喜欢它,并且不用担心以后需要在数据库中解析它。在这种情况下,XML 更有意义,因为它可以在两个地方进行解析。

标签: sql-server json string tsql


【解决方案1】:

您可以使用CHARINDEX 执行此操作,因为您可以将起始位置传递给它,这将允许您获得结束"。您可能不应该寻找",",因为如果CompanySize 是最终属性,则该片段末尾不会有,"。将其作为内联表值函数 (iTVF) 执行将非常有效(特别是因为 13k 行几乎没有),您只需将其与 CROSS APPLYOUTER APPLY 一起使用:

USE [tempdb];
GO

CREATE FUNCTION dbo.GetCompanySize(@JSON NVARCHAR(MAX))
RETURNS TABLE
AS RETURN

WITH SearchStart AS
(
  SELECT '"CompanySize":"' AS [Fragment]
), Search AS
(
  SELECT CHARINDEX(ss.Fragment, @JSON) AS [Start],
         LEN(ss.Fragment) AS [FragmentLength]
  FROM   SearchStart ss
)
SELECT CASE Search.Start
         WHEN 0 THEN NULL
         ELSE SUBSTRING(@JSON,
                        (Search.Start + Search.FragmentLength),
                        CHARINDEX('"',
                                  @JSON,
                                  Search.Start + Search.FragmentLength
                                 ) - (Search.Start + Search.FragmentLength)
                       )
       END AS [CompanySize]
FROM Search;
GO

设置测试:

CREATE TABLE #tmp (JSON NVARCHAR(MAX));

INSERT INTO #tmp (JSON) VALUES
('"http://www.companyurl.com","FoundedYear":"2007","Status":"Private","CompanySize":"51-200","TagLine":"We build software we believe in","Origi..');
INSERT INTO #tmp (JSON) VALUES
('"http://www.companyurl.com","FoundedYear":"2009","Status":"Public","TagLine":"We build software we believe in","Origi..');
INSERT INTO #tmp (JSON) VALUES (NULL);

运行测试:

SELECT comp.CompanySize
FROM   #tmp tmp
CROSS APPLY tempdb.dbo.GetCompanySize(tmp.JSON) comp

返回:

CompanySize
-----------
51-200
NULL
NULL

【讨论】:

  • 感谢@srutzky,这很有帮助。
【解决方案2】:

基于@srutzky 的回答,以下解决方案避免创建 UDF(尽管您没有说这是一个约束,但它可能对某些人有用)。

select
    c.Id,
    substring(i2.jsontail, 0, i3.[length]) CompanySize
from
    Companies c cross apply
    ( select charindex('CompanySize":"', c.json) start ) i1 cross apply
    ( select substring(c.json, start + len('CompanySize":"'), len(c.json) - start ) jsontail ) i2 cross apply
    ( select charindex('"', i2.jsontail) [length] ) i3
where
    i1.[start] != 0

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-03-19
    • 1970-01-01
    • 1970-01-01
    • 2022-01-11
    • 2012-09-28
    • 2017-09-24
    • 2020-05-08
    相关资源
    最近更新 更多