【问题标题】:SQL Server 2016 "FOR JSON PATH" - Modify JSON nestingSQL Server 2016 “FOR JSON PATH” - 修改 JSON 嵌套
【发布时间】:2015-12-28 00:58:48
【问题描述】:

我正在尝试使用FOR JSON PATH 更改查询的 JSON 输出的嵌套。 FOR JSON AUTO 查询几乎是我需要的,但并不完全。

使用FOR JSON AUTO查询:

SELECT 
    table_name      = tables.name,
    table_object_id = tables.object_id,
    index_name      = indexes.name,
    index_id        = indexes.index_id,
    column_name     = columns.name,
    column_id       = index_columns.index_column_id,
    max_length      = columns.max_length,
    precision       = columns.precision,
    scale           = columns.scale

FROM 
     sys.indexes        indexes 
INNER JOIN 
     sys.index_columns  index_columns  ON  indexes.object_id = index_columns.object_id and indexes.index_id = index_columns.index_id 
INNER JOIN 
    sys.columns         columns        ON  index_columns.object_id = columns.object_id and index_columns.column_id = columns.column_id 
INNER JOIN 
    sys.tables          tables         ON  indexes.object_id = tables.object_id 

WHERE tables.name LIKE 'tbl_%'

ORDER BY 
    tables.name, indexes.index_id, index_columns.index_column_id

FOR JSON AUTO

使用FOR JSON AUTO查询输出(sn-p):

.
.
.
{
    "table_name": "tbl_Agent",
    "table_object_id": 176055713,
    "indexes": [
        {
            "index_name": "PK_Task_tbl_Agent",
            "index_id": 1,
            "columns": [
                {
                    "column_name": "PartitionId",
                    "max_length": 4,
                    "precision": 10,
                    "scale": 0,
                    "index_columns": [
                        {
                            "column_id": 1
                        }
                    ]
                },
                {
                    "column_name": "AgentId",
                    "max_length": 4,
                    "precision": 10,
                    "scale": 0,
                    "index_columns": [
                        {
                            "column_id": 2
                        }
                    ]
                }
            ]
        },
        {
            "index_name": "IX_Task_tbl_Agent_PoolId_AgentName",
            "index_id": 2,
            "columns": [
                {
                    "column_name": "PartitionId",
                    "max_length": 4,
                    "precision": 10,
                    "scale": 0,
                    "index_columns": [
                        {
                            "column_id": 1
                        }
                    ]
                },
                {
                    "column_name": "PoolId",
                    "max_length": 4,
                    "precision": 10,
                    "scale": 0,
                    "index_columns": [
                        {
                            "column_id": 2
                        }
                    ]
                },
                {
                    "column_name": "AgentName",
                    "max_length": 128,
                    "precision": 0,
                    "scale": 0,
                    "index_columns": [
                        {
                            "column_id": 3
                        }
                    ]
                }
            ]
        },
        {
            "index_name": "IX_Task_tbl_Agent_PoolId_SessionId",
            "index_id": 3,
            "columns": [
                {
                    "column_name": "PartitionId",
                    "max_length": 4,
                    "precision": 10,
                    "scale": 0,
                    "index_columns": [
                        {
                            "column_id": 1
                        }
                    ]
                },
                {
                    "column_name": "PoolId",
                    "max_length": 4,
                    "precision": 10,
                    "scale": 0,
                    "index_columns": [
                        {
                            "column_id": 2
                        }
                    ]
                },
                {
                    "column_name": "SessionId",
                    "max_length": 16,
                    "precision": 0,
                    "scale": 0,
                    "index_columns": [
                        {
                            "column_id": 3
                        }
                    ]
                }
            ]
        }
    ]
},
.
.
.

每个表的所有索引都正确嵌套,并且在每个索引中,索引的所有列(这些是复合索引)都正确嵌套。我要做的唯一更改是取消嵌套index_columns,使column_id 成为columns 的一部分,例如:

.
.
.
"columns": [
    {
        "column_name": "PartitionId",
        "max_length": 4,
        "precision": 10,
        "scale": 0,
  --->  "column_id": 1
    },
    {
        "column_name": "PoolId",
        "max_length": 4,
        "precision": 10,
        "scale": 0,
  --->  "column_id": 2
    },
    {
        "column_name": "AgentName",
        "max_length": 128,
        "precision": 0,
        "scale": 0,
  --->  "column_id": 3
    }
]
.
.
.

但是,当我尝试使用 FOR JSON PATH 时,它最终嵌套不正确。

使用FOR JSON PATH查询:

SELECT 

    tables.name                   AS [tables.table_name],
    tables.object_id              AS [tables.table_object_id],
    indexes.name                  AS [tables.indexes.index_name],
    indexes.index_id              AS [tables.indexes.index_id],
    columns.name                  AS [tables.indexes.columns.column_name],
    index_columns.index_column_id AS [tables.indexes.columns.column_id],
    columns.max_length            AS [tables.indexes.columns.max_length],
    columns.precision             AS [tables.indexes.columns.precision],
    columns.scale                 AS [tables.indexes.columns.scale]

FROM 
     sys.indexes        indexes 
INNER JOIN 
     sys.index_columns  index_columns  ON  indexes.object_id = index_columns.object_id and indexes.index_id = index_columns.index_id 
INNER JOIN 
     sys.columns        columns        ON  index_columns.object_id = columns.object_id and index_columns.column_id = columns.column_id 
INNER JOIN 
     sys.tables         tables         ON  indexes.object_id = tables.object_id 

WHERE tables.name LIKE 'tbl_%'

ORDER BY 
     tables.name, indexes.index_id, index_columns.index_column_id

FOR JSON PATH

使用FOR JSON PATH查询输出(sn-p):

.
.
.
{
    "tables": {
===>    "table_name": "tbl_Agent",
        "table_object_id": 176055713,
        "indexes": {
  --->      "index_name": "PK_Task_tbl_Agent",
            "index_id": 1,
            "columns": {
                "column_name": "PartitionId",
                "column_id": 1,
                "max_length": 4,
                "precision": 10,
                "scale": 0
            }
        }
    }
},
{
    "tables": {
===>    "table_name": "tbl_Agent",
        "table_object_id": 176055713,
        "indexes": {
  --->      "index_name": "PK_Task_tbl_Agent",
            "index_id": 1,
            "columns": {
                "column_name": "AgentId",
                "column_id": 2,
                "max_length": 4,
                "precision": 10,
                "scale": 0
            }
        }
    }
},
{
    "tables": {
===>    "table_name": "tbl_Agent",
        "table_object_id": 176055713,
        "indexes": {
            "index_name": "IX_Task_tbl_Agent_PoolId_AgentName",
            "index_id": 2,
            "columns": {
                "column_name": "PartitionId",
                "column_id": 1,
                "max_length": 4,
                "precision": 10,
                "scale": 0
            }
        }
    }
},
.
.
.

虽然column_id 现在是我想要的位置,但columns 现在是唯一正确嵌套的东西。每个table 现在对其每个indexes 重复,每个index 对其每个columns 重复。

如何在我想要的位置获得column_id(如FOR JSON PATH 查询的输出),同时保持正确的嵌套(如FOR JSON AUTO 查询的输出)?

更新 -- 工作中

根据 DimaSUN 的回复和 Ben 的评论,我想出了这个正在运行的查询:

SELECT 
    tables.name      AS [table_name],
    tables.object_id AS [table_object_id],

   (SELECT 
        indexes.name     AS [index_name],
        indexes.index_id AS [index_id],

       (SELECT
            columns.name                  AS [column_name],
            index_columns.index_column_id AS [column_id],
            columns.max_length            AS [max_length],
            columns.precision             AS [precision],
            columns.scale                 AS [scale]
            FROM 
                sys.index_columns index_columns
                JOIN
                sys.columns columns ON index_columns.object_id = columns.object_id and index_columns.column_id = columns.column_id 
            WHERE
                indexes.object_id = index_columns.object_id and indexes.index_id = index_columns.index_id
            ORDER BY
                index_columns.index_column_id
            FOR JSON PATH
       ) AS 'columns'

    FROM
        sys.indexes indexes
    WHERE 
        indexes.object_id = tables.object_id
    ORDER BY
        indexes.index_id
    FOR JSON PATH
    ) AS 'indexes'

FROM
    sys.tables tables
WHERE
    tables.name LIKE 'tbl_%'
ORDER BY
    tables.name
FOR JSON PATH, ROOT('tables')

新查询输出(sn-p):

.
.
.
{
    "table_name": "tbl_Agent",
    "table_object_id": 176055713,
    "indexes": [
        {
            "index_name": "PK_Task_tbl_Agent",
            "index_id": 1,
            "columns": [
                {
                    "column_name": "PartitionId",
                    "column_id": 1,
                    "max_length": 4,
                    "precision": 10,
                    "scale": 0
                },
                {
                    "column_name": "AgentId",
                    "column_id": 2,
                    "max_length": 4,
                    "precision": 10,
                    "scale": 0
                }
            ]
        },
        {
            "index_name": "IX_Task_tbl_Agent_PoolId_AgentName",
            "index_id": 2,
            "columns": [
                {
                    "column_name": "PartitionId",
                    "column_id": 1,
                    "max_length": 4,
                    "precision": 10,
                    "scale": 0
                },
                {
                    "column_name": "PoolId",
                    "column_id": 2,
                    "max_length": 4,
                    "precision": 10,
                    "scale": 0
                },
                {
                    "column_name": "AgentName",
                    "column_id": 3,
                    "max_length": 128,
                    "precision": 0,
                    "scale": 0
                }
            ]
        },
        {
            "index_name": "IX_Task_tbl_Agent_PoolId_SessionId",
            "index_id": 3,
            "columns": [
                {
                    "column_name": "PartitionId",
                    "column_id": 1,
                    "max_length": 4,
                    "precision": 10,
                    "scale": 0
                },
                {
                    "column_name": "PoolId",
                    "column_id": 2,
                    "max_length": 4,
                    "precision": 10,
                    "scale": 0
                },
                {
                    "column_name": "SessionId",
                    "column_id": 3,
                    "max_length": 16,
                    "precision": 0,
                    "scale": 0
                }
            ]
        }
    ]
},
.
.
.

嵌套选择末尾的 AS 'columns'AS 'indexes' 是一个关键部分,否则我会收到以下错误:

Msg 13605, Level 16, State 1, Line 1
Unnamed tables cannot be used as JSON identifiers as well as unnamed columns cannot be used as key names. Add alias to the unnamed column/table.

【问题讨论】:

  • 您通常希望为此类事情执行相关子查询。也就是说,您的驾驶查询将是select * from sys.tables as twhere name like 'tbl_%',而不是选择“*”,您将发出一个选择,例如,sys.indexes 看起来像select i.name from sys.indexes as i where i.object_id = t.object_id。 where 子句说“给我正在考虑的当前表的索引”。
  • 感谢您的回复,本!帮助很大。请参阅上面的更新。
  • @BenThul 那么这意味着对于上述场景我们除了使用子查询之外别无选择使用内连接?
  • 内部连接的问题是每次匹配都会产生一行。例如,假设您有一个关系,其中 tableA 中的每一行在 tableB 中有 10 行。如果 tableA 有 5 行,而您执行 select * from tableA join tableB,您将返回 5x10=50 行。您可以将其转换为 JSON,但大多数情况下,您会希望 JSON 具有(在我的示例中)5 个元素,每个元素有 10 个属性。

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


【解决方案1】:

对于 XML,它看起来像

  ( select 
    indexes.name                  AS [index_name],
    indexes.index_id              AS [index_id],
    ( select 
    columns.name                  AS [column_name],
    index_columns.index_column_id AS [column_id],
    columns.max_length            AS [max_length],
    columns.precision             AS [precision],
    columns.scale                 AS [scale]
    from 
     sys.index_columns  index_columns 
     JOIN 
     sys.columns        columns        ON  index_columns.object_id = columns.object_id and index_columns.column_id = columns.column_id 
     where    indexes.object_id = index_columns.object_id and indexes.index_id = index_columns.index_id 
     FOR xml PATH(''), root('columns'), type
     )

  from
     sys.indexes        indexes 
     where indexes.object_id = tables.object_id 
     FOR xml PATH(''), root('indices'), type
     )
FROM 
   sys.tables         tables      


WHERE tables.name LIKE 'tbl_%'

--ORDER BY      tables.name, indexes.index_id, index_columns.index_column_id

FOR XML PATH('tables')

用 JSON 替换 XML。

【讨论】:

  • 感谢 DimaSUN 的详细解答!它让我达到了 99%。由于FOR JSON PATHFOR XML PATH 之间的语法明显略有不同,我只需要稍微玩一下。请参阅上面的更新。干杯!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-06-10
  • 1970-01-01
  • 1970-01-01
  • 2014-11-04
相关资源
最近更新 更多