将json文件保存到sql server数据库表

wqnecbli  于 2021-07-29  发布在  Java
关注(0)|答案(4)|浏览(953)

我有一个嵌套的json文件,如下所示(其中条件和规则可以嵌套到多个级别)

{
    "condition": "and",
    "rules": [
      {
        "field": "26",
        "operator": "=",
        "value": "TEST1"
      },
      {
        "field": "36",
        "operator": "=",
        "value": "TEST2"
      },
      {
        "condition": "or",
        "rules": [
          {
            "field": "2",
            "operator": "=",
            "value": 100
          },
          {
            "field": "3",
            "operator": "=",
            "value": 12
          },
          {
            "condition": "or",
            "rules": [
              {
                "field": "12",
                "operator": "=",
                "value": "CA"
              },
              {
                "field": "12",
              "operator": "=",
              "value": "AL"
            }
          ]
        }
      ]
    }
  ]
}

我想将这个json(json文件中的conditon和rules字段可以嵌套到多个级别)保存到sqlserver表中,然后想从这些创建的表中构造相同的json。我该怎么做?我计划从这些表中获取其他json格式,这就是为什么决定将json拆分为表列的原因。
我认为需要创建一个递归sql函数来实现同样的功能。
我创建了以下表来保存相同的json。

CREATE TABLE [Ruleset]
([RulesetID]       [BIGINT] IDENTITY(1, 1) NOT NULL PRIMARY KEY,
 [Condition]       [VARCHAR](50) NOT NULL,
 [ParentRuleSetID] [BIGINT] NULL
);
GO
CREATE TABLE [Rules]
([RuleID]    [BIGINT] IDENTITY(1, 1) NOT NULL PRIMARY KEY,
 [Fields]    [VARCHAR](MAX) NOT NULL,
 [Operator]  [VARCHAR](MAX) NOT NULL,
 [Value]     [VARCHAR](MAX) NOT NULL,
 [RulesetID] [BIGINT] NULL
                      FOREIGN KEY REFERENCES [Ruleset](RulesetID)
);

插入脚本如下:,

INSERT INTO [Ruleset] values  
 ('AND',0),
 ('OR',1),
 ('OR',2) 

 INSERT INTO [Rules] values  
 ('26','=','TEST1',1),
 ('364','=','TEST2',1),
 ('2','=','100',2),
 ('3','=','12',2),
  ('12','=','CA',3),
 ('12','=','AL',3)

这些table够吗?是否可以保存所有详细信息?
将我手动添加到这些表中的值附加到这些表中。

如何将这个json保存到这些表中,然后通过存储过程或查询从这些表构造相同的json?
请提供建议和样品!

3hvapo4f

3hvapo4f1#

由于json区分大小写,请检查您的模式定义和示例数据。我看到表的定义、内容和json之间存在差异
在ms sql server 2016上测试的所有脚本
我在这个脚本中使用了一个临时表变量,但是您可以不使用它。请参见SQLFiddle中的一个示例

-- JSON -> hierarchy table
DECLARE @ExpectedJSON NVARCHAR(MAX) = '
{
    "condition": "and",
    "rules": [
      {
        "field": "26",
        "operator": "=",
        "value": "TEST1"
      },
      {
        "field": "36",
        "operator": "=",
        "value": "TEST2"
      },
      {
        "condition": "or",
        "rules": [
          {
            "field": "2",
            "operator": "=",
            "value": 100
          },
          {
            "field": "3",
            "operator": "=",
            "value": 12
          },
          {
            "condition": "or",
            "rules": [
              {
                "field": "12",
                "operator": "=",
                "value": "CA"
              },
              {
                "field": "12",
              "operator": "=",
              "value": "AL"
            }
          ]
        }
      ]
    }
  ]
}
'

DECLARE @TempRuleset AS TABLE 
(RulesetID          BIGINT NOT NULL PRIMARY KEY,
 condition          VARCHAR(50) NOT NULL,
 ParentRuleSetID    BIGINT NOT NULL,
 RulesJSON          NVARCHAR(MAX)
)

;WITH ParseRuleset AS (
    SELECT  1 AS RulesetID,
            p.condition,
            p.rules,
            0 AS ParentRuleSetID
    FROM OPENJSON(@ExpectedJSON, '$') WITH (
        condition   VARCHAR(50),
        rules       NVARCHAR(MAX) AS JSON
    ) AS p

    UNION ALL

    SELECT  RulesetID + 1,
            p.condition,
            p.rules,
            c.RulesetID AS ParentRuleSetID
    FROM ParseRuleset AS c
        CROSS APPLY OPENJSON(c.rules) WITH (
            condition   VARCHAR(50),
            rules       NVARCHAR(MAX) AS JSON
        ) AS p
    where
        p.Rules IS NOT NULL
)

INSERT INTO @TempRuleset (RulesetID, condition, ParentRuleSetID, RulesJSON)
SELECT  RulesetID,
        condition,
        ParentRuleSetID,
        rules
FROM ParseRuleset

 -- INSEERT INTO Ruleset ...
SELECT RulesetID,
        condition,
        ParentRuleSetID,
        RulesJSON
FROM @TempRuleset

-- INSERT INTO Rules ...
SELECT  RulesetID,
        field,
        operator,
        value
FROM @TempRuleset tmp
     CROSS APPLY OPENJSON(tmp.RulesJSON) WITH (
                field       VARCHAR(MAX),
                operator    VARCHAR(MAX),
                value       VARCHAR(MAX)
             ) AS p
WHERE   p.field IS NOT NULL

sql小提琴
层次结构表->json:

CREATE TABLE Ruleset
(RulesetID       BIGINT IDENTITY(1, 1) NOT NULL PRIMARY KEY,
 condition       VARCHAR(50) NOT NULL,
 ParentRuleSetID BIGINT NULL
);
GO
CREATE TABLE Rules
(RuleID     BIGINT IDENTITY(1, 1) NOT NULL PRIMARY KEY,
 field      VARCHAR(MAX) NOT NULL,
 operator   VARCHAR(MAX) NOT NULL,
 value      VARCHAR(MAX) NOT NULL,
 RulesetID  BIGINT NULL FOREIGN KEY REFERENCES Ruleset(RulesetID)
);

INSERT INTO Ruleset values  
    ('and',0),
    ('or',1),
    ('or',2) 

INSERT INTO Rules values  
    ('26','=','TEST1',1),
    ('36','=','TEST2',1),
    ('2','=','100',2),
    ('3','=','12',2),
    ('12','=','CA',3),
    ('12','=','AL',3)

-- hierarchy table -> JSON
;WITH GetLeafLevel AS 
(
    SELECT  Ruleset.RulesetID,
            Ruleset.condition,
            Ruleset.ParentRuleSetID,
            1 AS lvl,
            (   SELECT  field,
                        operator,
                        value
                FROM    Rules
                WHERE   Rules.RulesetID = Ruleset.RulesetID
                FOR JSON AUTO, WITHOUT_ARRAY_WRAPPER 
            ) AS JSON_Rules
    FROM    Ruleset
    WHERE   ParentRuleSetID = 0
    UNION ALL
    SELECT  Ruleset.RulesetID,
            Ruleset.condition,
            Ruleset.ParentRuleSetID,
            GetLeafLevel.lvl + 1,
            (   SELECT  field,
                        operator,
                        value
                FROM    Rules
                WHERE   Rules.RulesetID = Ruleset.RulesetID
                FOR JSON AUTO, WITHOUT_ARRAY_WRAPPER 
            )
    FROM    Ruleset
            INNER JOIN GetLeafLevel ON Ruleset.ParentRuleSetID = GetLeafLevel.RulesetID
),
-- SELECT * FROM GetLeafLevel       -- debug 
ConcatReverseOrder AS 
(
    SELECT  GetLeafLevel.*,
            CONCAT('{"condition":"',
                    GetLeafLevel.condition,
                    '","rules":[',
                    GetLeafLevel.JSON_Rules,
                    ']}'
                    ) AS js
    FROM    GetLeafLevel
    WHERE   GetLeafLevel.lvl = (SELECT MAX(lvl) FROM GetLeafLevel)
    UNION ALL
    SELECT  GetLeafLevel.*,
            CONCAT('{"condition":"',
                            GetLeafLevel.condition,
                            '","rules":[',
                            GetLeafLevel.JSON_Rules,
                            ',',
                            ConcatReverseOrder.js,
                            ']}'
                            ) AS js
    FROM    GetLeafLevel
            INNER JOIN ConcatReverseOrder ON GetLeafLevel.RuleSetID = ConcatReverseOrder.ParentRuleSetID 
)
-- SELECT * FROM ConcatReverseOrder     -- debug 

SELECT  js
FROM    ConcatReverseOrder
WHERE   ParentRuleSetID = 0

sql小提琴

ne5o7dgx

ne5o7dgx2#

实际上,您可以将列类型声明为nvarchar(max),并将json字符串保存到其中。

zed5wv10

zed5wv103#

我觉得我需要更多地了解你打算如何使用这些数据来回答这个问题。我的心告诉我,在mssql中存储这些信息是有问题的,如果没有错的话,就是有问题的。
如果必须这样做,我会将这些条件转换为分支内可循环事件的矩阵查找表,因此对于每个可能的逻辑分支,您可以在查找中创建一行来计算它。
根据您所需的输出/特性集,您可以执行上述操作,也可以按照rkortekaas的建议将所有内容都放到nvarchar中。

wnvonmuf

wnvonmuf4#

您的用例看起来确实非常适合nosql选项,比如mongodb、azure表存储或cosmosdb(如果您不知道如何使用cosmosdb,那么cosmosdb可能非常昂贵)。
mongodb页面摘录:
在mongodb中,数据存储为文档。这些文档以json(javascript对象表示法)格式存储在mongodb中。json文档支持嵌入字段,因此相关数据和数据列表可以与文档而不是外部表一起存储。
但是,从这里开始,我将假设您由于其他原因与SQLServer绑定。
您已经说过,您只需要将文档放入并取出同一个文档,所以将所有字段拆分是没有意义的。
SQLServer在处理文本字段方面比以前要好得多。
我以前工作过的系统有以下列(我会编写sql,但我不在我的开发机器上): Id [主键,整数,递增索引] UserId [与此相关的外键-在您的案例中可能不是'user'!] Value [nvarchar(1000)以字符串形式包含json]
根据外键很容易进行查找。
但是,假设您希望它更像nosql,您可以: Id [主键,整数,递增索引] Key [nvarchar(100)一个字符串键,可以很容易地重新生成以查找值(例如。 User_43_Level_6_GameData -此列应具有索引] Value [nvarchar(1000)以字符串形式包含json]
我一直使用整数id的原因是为了避免碎片。很明显你可以 Value 列更大。
json可以很容易地在json对象和json字符串之间转换。在javascript中,可以使用json解析和stringify。如果您使用的是c语言,那么您可以使用以下代码段,尽管执行此任务的方法有很多种(对象可以嵌套得越深越好)
.net对象到json
weather w=新天气(“下雨”、“刮风”、“32”);var jsonstring=jsonserializer.serialize(w);
json到.net对象(c#)
var w=jsonserializer.deserialize(jsonstring);
更新
虽然这是我过去做事情的方式,但sql server中似乎有了处理json的新选项—openjson和jsonquery可能是潜在的选项,尽管我自己没有使用过—它们仍然使用nvarchar作为json列。

相关问题