将 JSON 文件保存到 SQL 服务器数据库表

Saving JSON file to SQL Server Database tables

我有一个嵌套的 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 文件中的条件和规则字段可以嵌套到多个级别)保存到 SQL 服务器表中,稍后想构建相同的JSON 来自这些创建的 table。我怎样才能做到这一点 ?从这些 table 我计划获得其他 json 格式,这也是为什么决定将 json 拆分为 table 列的原因。

我认为需要创建一个递归 sql 函数来做同样的事情。

我创建了以下 table 以保存相同的 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 够了吗?将能够保存所有详细信息吗?

手动附加我添加到这些 table 的值。

如何将这个 JSON 保存到这些 table 中,然后通过存储过程或查询从这些 table 构造相同的 JSON?

请提供建议和样品!

我觉得我需要更多地了解您打算如何使用这些数据来回答这个问题。我的心告诉我,将这些信息存储在 MSSQL 中是有问题的,如果没有错误,那就是有问题的。

如果我必须这样做,我会将这些条件转换为矩阵查找 table 您分支内的 rotatable 事件,因此对于每个可能的逻辑分支,您可以在查找以对此进行评估。

根据您所需的输出/功能集,您可以执行上述操作,也可以按照 rkortekaas 的建议将所有内容都放入 NVARCHAR 中。

您的用例看起来确实非常适合 NoSql 选项,例如 MongoDb, Azure Table storage, or CosmosDB(如果您不了解 CosmosDB,它可能会很贵)。

摘自 MongoDB 页:

In MongoDB, data is stored as documents. These documents are stored in MongoDB in JSON (JavaScript Object Notation) format. JSON documents support embedded fields, so related data and lists of data can be stored with the document instead of an external table.

但是,从现在开始,我假设您由于其他原因绑定到 SQL 服务器。

您已经声明您将只是将文档放入并取出相同的文档,因此拆分所有字段的努力没有意义。

SQL 服务器在处理文本字段方面比以前的 IMO 要好得多。

我之前工作过的系统有以下列(我会写 sql,但我不在我的开发机器上):

  • Id [主键,整数,递增索引]
  • UserId [与此相关的外键 - 在您的情况下可能 不是 'user'!]
  • Value [nvarchar(1000) 包含 json 作为字符串]

根据外键轻松查找。

但是,假设您希望它更像 NoSql,您可以:

  • Id [主键,整数,递增索引]
  • Key [nvarchar(100) 您制作的字符串键,可以轻松重新制作以查找值(例如 User_43_Level_6_GameData - 此列应该有一个索引]
  • Value [nvarchar(1000) 包含 json 作为字符串]

我一直使用整数 ID 的原因是为了避免 fragmentation。您显然可以使 Value 列更大。

Json 可以很容易地在 json 对象和 json 字符串之间转换。在 Javascript 中,您将使用 Json Parse and Stringify. If you are using C# you could use the following snippets,尽管有很多方法可以完成此任务(对象可以嵌套任意深度)

.NET 对象到 Json

天气 w = new Weather("rainy", "windy", "32"); var jsonString = JsonSerializer.Serialize(w);

Json 到 .NET 对象 (C#)

var w = JsonSerializer.Deserialize(jsonString);

更新

尽管这是我过去做事的方式,看起来 sql 服务器中有新的选项来处理 JSON - OPENJSON and JSONQUERY could be potential options, though I haven't used them myself - they still use nvarchar JSON列。

因为 JSON 区分大小写,请检查您的架构定义和样本数据。我发现 table 的定义、它们的内容与您的 JSON

之间存在差异

所有脚本在 MS SQL Server 2016

上测试

我在此脚本中使用了一个临时 table 变量,但您可以不用它。请参阅 SQL Fiddle

中的示例
-- 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 Fiddle

层次结构 tables -> 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 Fiddle

其实你可以声明列类型为NVARCHAR(MAX),然后将json字符串存入其中。