【问题标题】:Select Json formatted like a report to a table using T-SQL选择格式类似于使用 T-SQL 的表的报告的 Json
【发布时间】:2019-12-29 01:16:00
【问题描述】:

我将 JSON 以以下格式存储在 SQL Server 数据库表中。我已经能够捏造一种方法来获得我需要的值,但我觉得必须有更好的方法来使用 T-SQL 来做到这一点。 JSON 从以下格式的报告中输出,其中“列”中的列名对应于“行”-“数据”数组值。

所以“财政月份”列对应数据值“11”,“财政年度”对应“2019”等。

{
  "report": "Property ETL",
  "id": 2648,
  "columns": [
    {
      "name": "Fiscal Month",
      "dataType": "int"
    },
    {
      "name": "Fiscal Year",
      "dataType": "int"
    },
    {
      "name": "Portfolio",
      "dataType": "varchar(50)"
    },
    {
      "name": "Rent",
      "dataType": "int"
    }
],
"rows": [
    {
      "rowName": "1",
      "type": "Detail",
      "data": [
        11,
        2019,
        "West Group",
        10
      ]
    },
    {
      "rowName": "2",
      "type": "Detail",
      "data": [
        11,
        2019,
        "East Group",
        10
      ]
    },
    {
      "rowName": "3",
      "type": "Detail",
      "data": [
        11,
        2019,
        "East Group",
        10
      ]
    },
    {
      "rowName": "Totals: ",
      "type": "Total",
      "data": [
        null,
        null,
        null,
        30
      ]
    }
  ]
}    

为了获取“数据”数组中的数据,我目前在 T-SQL 中有一个 2 步过程,在该过程中我创建了一个临时表,并在此处插入“$.Rows”中的行键/值。然后我可以为每一行选择单独的列

CREATE TABLE #TempData 
(
    Id INT,
    JsonData VARCHAR(MAX)
)

DECLARE @json VARCHAR(MAX);
DECLARE @LineageKey INT;

SET @json = (SELECT JsonString FROM Stage.Report);
SET @LineageKey = (SELECT LineageKey FROM Stage.Report);    

INSERT INTO #TempData(Id, JsonData)
    (SELECT [key], value FROM OPENJSON(@json, '$.rows'))

MERGE [dbo].[DestinationTable] TARGET
USING 
(
    SELECT 
        JSON_VALUE(JsonData, '$.data[0]') AS FiscalMonth,
        JSON_VALUE(JsonData, '$.data[1]') AS FiscalYear,
        JSON_VALUE(JsonData, '$.data[2]') AS Portfolio,
        JSON_VALUE(JsonData, '$.data[3]') AS Rent
     FROM #TempData
     WHERE JSON_VALUE(JsonData, '$.data[0]') is not null
) AS SOURCE     
... 
etc., etc.

这可行,但我想知道是否有一种方法可以直接选择数据值,而无需将其放入临时表的中间步骤。我阅读的文档和示例似乎都要求数据具有与之关联的名称才能访问它。当我尝试通过索引直接访问某个位置的数据时,我得到的是 Null。

【问题讨论】:

    标签: json sql-server tsql sql-server-2016


    【解决方案1】:

    希望我能正确理解您的问题。如果您知道列名,则需要一个带有显式架构的 OPENJSON() 调用,但如果您想从 $.columns 读取 JSON 结构,则需要一个动态语句。

    JSON:

    DECLARE @json nvarchar(max) = N'{
      "report": "Property ETL",
      "id": 2648,
      "columns": [
        {
          "name": "Fiscal Month",
          "dataType": "int"
        },
        {
          "name": "Fiscal Year",
          "dataType": "int"
        },
        {
          "name": "Portfolio",
          "dataType": "varchar(50)"
        },
        {
          "name": "Rent",
          "dataType": "int"
        }
    ],
    "rows": [
        {
          "rowName": "1",
          "type": "Detail",
          "data": [
            11,
            2019,
            "West Group",
            10
          ]
        },
        {
          "rowName": "2",
          "type": "Detail",
          "data": [
            11,
            2019,
            "East Group",
            10
          ]
        },
        {
          "rowName": "3",
          "type": "Detail",
          "data": [
            11,
            2019,
            "East Group",
            10
          ]
        },
        {
          "rowName": "Totals: ",
          "type": "Total",
          "data": [
            null,
            null,
            null,
            30
          ]
        }
      ]
    }'
    

    固定结构声明:

    SELECT *
    FROM OPENJSON(@json, '$.rows') WITH (
       [Fiscal Month] int '$.data[0]',
       [Fiscal Year] int '$.data[1]',
       [Portfolio] varchar(50) '$.data[2]',
       [Rent] int '$.data[3]'
    )
    

    动态语句:

    DECLARE @stm nvarchar(max) = N''
    
    SELECT @stm = CONCAT(
       @stm,
       N',',
       QUOTENAME(j2.name),
       N' ',
       j2.dataType,
       N' ''$.data[',
       j1.[key],
       N']'''
    )
    FROM OPENJSON(@json, '$.columns') j1
    CROSS APPLY OPENJSON(j1.value) WITH (
       name varchar(50) '$.name',
       dataType varchar(50) '$.dataType'
    ) j2
    
    SELECT @stm = CONCAT(
       N'SELECT * FROM OPENJSON(@json, ''$.rows'') WITH (',
       STUFF(@stm, 1, 1, N''),
       N')'
    )
    PRINT @stm
    EXEC sp_executesql @stm, N'@json nvarchar(max)', @json
    

    结果:

    --------------------------------------------
    Fiscal Month    Fiscal Year Portfolio   Rent
    --------------------------------------------
    11              2019        West Group  10
    11              2019        East Group  10
    11              2019        East Group  10
                                            30 
    

    【讨论】:

      【解决方案2】:

      是的,没有临时表也是可以的:

      DECLARE @json NVARCHAR(MAX) = 
      N'
      {
        "report": "Property ETL",
        "id": 2648,
        "columns": [
          {
            "name": "Fiscal Month",
            "dataType": "int"
          },
          {
            "name": "Fiscal Year",
            "dataType": "int"
          },
          {
            "name": "Portfolio",
            "dataType": "varchar(50)"
          },
          {
            "name": "Rent",
            "dataType": "int"
          }
      ],
      "rows": [
          {
            "rowName": "1",
            "type": "Detail",
            "data": [
              11,
              2019,
              "West Group",
              10
            ]
          },
          {
            "rowName": "2",
            "type": "Detail",
            "data": [
              11,
              2019,
              "East Group",
              10
            ]
          },
          {
            "rowName": "3",
            "type": "Detail",
            "data": [
              11,
              2019,
              "East Group",
              10
            ]
          },
          {
            "rowName": "Totals: ",
            "type": "Total",
            "data": [
              null,
              null,
              null,
              30
            ]
          }
        ]
      }    
      }';
      

      并查询:

      SELECT s.value,
        rowName = JSON_VALUE(s.value, '$.rowName'),
        [type] = JSON_VALUE(s.value, '$.type'),
        s2.[key],
        s2.value
      FROM OPENJSON(JSON_QUERY(@json, '$.rows')) s
      CROSS APPLY OPENJSON(JSON_QUERY(s.value, '$.data')) s2;
      

      db<>fiddle demo

      或者作为每个细节的单行:

      SELECT s.value,
        rowName = JSON_VALUE(s.value, '$.rowName'),
        [type] = JSON_VALUE(s.value, '$.type'),
        JSON_VALUE(s.value, '$.data[0]') AS FiscalMonth,
        JSON_VALUE(s.value, '$.data[1]') AS FiscalYear,
        JSON_VALUE(s.value, '$.data[2]') AS Portfolio,
        JSON_VALUE(s.value, '$.data[3]') AS Rent
      FROM OPENJSON(JSON_QUERY(@json, '$.rows')) s;
      

      db<>fiddle demo 2

      【讨论】:

        猜你喜欢
        • 2020-02-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-04-24
        相关资源
        最近更新 更多