存储具有可变值类型的扁平化 JSON(键值对)

Posted

技术标签:

【中文标题】存储具有可变值类型的扁平化 JSON(键值对)【英文标题】:Storing flattened JSON (key-value pairs) with variable value types 【发布时间】:2021-12-27 21:57:02 【问题描述】:

我有没有定义架构的 JSON 文档。每个文档可能有不同的架构和值。


    "location":
    
        "latitude": 58.23,
        "longitude": 25.11
    ,
    "building": "A1",
    "active": true,
    "parameters": [ 1,  "scanInterval": 1000  ]

这些 JSON 文档被扁平化 - 格式化为键值对。


    "location.latitude": 58.23,
    "location.longitude": 25.11,
    "building": "A1",
    "active": true,
    "parameter[0]": 1,
    "parameter[1].scanInterval": 1000

Key 总是 String

Value可以是StringNumberBoolean

这些键值对将存储在 SQL 表中。要求是能够根据 JSON 原生值过滤键值。

SELECT .... FROM ... WHERE [Key] = @key AND [Value] > @value; -- [Value] is integer/float
SELECT .... FROM ... WHERE [Key] = @key AND [Value] != @value; -- [Value] is bit/boolean
SELECT .... FROM ... WHERE [Key] = @key AND [Value] = @value; -- [Value] is string

这让我疑问 - 如何设计我的桌子

选项 A) 投射

CREATE TABLE [dbo].[OptionA](
    ....
    [Key] [nvarchar](max),
    [ValueType] [nvarchar](max)
    [Value] [nvarchar](max)
)

始终将 [Value] 存储为 String,在查询数据时,选择匹配 [ValueType] 的行并转换值:

... WHERE [ValueType] = 'Number' AND [Key] = @key AND CAST([Value] AS FLOAT) > @value

选项 B) 每种值类型的列

CREATE TABLE [dbo].[OptionB](
    ....
    [Key] [nvarchar](50),
    [StringValue] [nvarchar](50) NULL,
    [NumericValue] [float] NULL,
    [BooleanValue] [bit] NULL
)

有 3 列。每列有 1 个值类型。在所有 3 列中,只有 1 列可以包含值,其余为 NULL。

查询数据时,选择对应值类型的列:

SELECT .... FROM ... WHERE [Key] = @key AND [NumericValue] > @value

哪个选项产生最好的结果或总体上更好/看起来更好?或许还有其他更好的选择?

我更倾向于 A) 方法,但是所有的转换都可能会增加额外的复杂性,并且可能会影响性能。

【问题讨论】:

取决于您的用例,基本上基于意见。如果您经常查询这些内容,那么选项 B 似乎更好。如果您只是检索整个对象,也许使用选项 A。如果您有深度嵌套的对象,那么首先将它们保留为适当的 JSON 对象可能更容易(这可能是我会做的) 【参考方案1】:

有点丑,not fully tested,但也许这会给你一个正确的方向。

我应该注意SEQuence 是可选的

示例

Declare @JSON varchar(max) = '

    "location":
    
        "latitude": 58.23,
        "longitude": 25.11
    ,
    "building": "A1",
    "active": true,
    "parameters": [ 1,  "scanInterval": 1000  ]

';

with cte0 as (
   Select *
         ,spath = convert(varchar(max),[key])
         ,seq   = convert(varchar(250),10000+row_number() over(order by 1/0))
    From OpenJSON(@json,'$') 
   Union All
   Select R.*
         ,spath = convert(varchar(max),concat(P.spath,case when try_convert(int,r.[key]) is not null and P.[type]>3 then quotename(r.[key]) else '.'+r.[key] end))
         ,seq   = convert(varchar(250),concat(P.seq,'\',10000+row_number() over(order by 1/0)))
    From  cte0 P 
    Cross Apply OpenJSON(P.[Value],'$') R 
    Where P.[Type]>3
)
Select [key] = spath
      ,value
      ,[Type]  = choose([Type],'string','numeric','bool','array','object')
 from cte0 
 Where [type]<=3
 Order By seq

结果

key                         value   Type
location.latitude           58.23   numeric
location.longitude          25.11   numeric
building                    A1      string
active                      true    bool
parameters[0]               1       numeric
parameters[1].scanInterval  1000    numeric

【讨论】:

以上是关于存储具有可变值类型的扁平化 JSON(键值对)的主要内容,如果未能解决你的问题,请参考以下文章

如何“扁平化”具有可变列数的 Spark 模式?

查询字符串中扁平化 JSON 对象的 Struts2 类型转换

嵌套的json扁平化火花数据框

python序列类型及一些操作

关于ES6的flat(扁平化数组)

python学习4