Reputation: 1079
I'm trying to change the nesting of the JSON output from a query using FOR JSON PATH
. The FOR JSON AUTO
query is almost what I need, but not quite.
Query using 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
Query using FOR JSON AUTO
Output (snippet):
.
.
.
{
"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
}
]
}
]
}
]
},
.
.
.
Each table has all of its indexes nested properly and, within each index, all of the index's columns (these are composite indexes) are nested properly. The only change I want to make is to un-nest index_columns
, making column_id
part of columns
, e.g.:
.
.
.
"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
}
]
.
.
.
When I try to use FOR JSON PATH
, however, it ends up nesting improperly.
Query using 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
Query using FOR JSON PATH
Output (snippet):
.
.
.
{
"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
}
}
}
},
.
.
.
While column_id
is now where I would like it to be, columns
is now the only thing properly nested. Each table
is now repeated for each of its indexes
, and each index
is repeated for each of its columns
.
How do I get column_id
where I want it (like the output from the FOR JSON PATH
query), while maintaining the proper nesting (like the output from the FOR JSON AUTO
query)?
UPDATE -- WORKING
Based on DimaSUN's response and Ben's comment, I came up with this query that is now working:
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')
New Query Output (snippet):
.
.
.
{
"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
}
]
}
]
},
.
.
.
The AS 'columns'
and AS 'indexes'
at the ends of the nested selects was a critical piece because otherwise I was getting the following error:
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.
Upvotes: 1
Views: 2249
Reputation: 921
for XML it looks like
( 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')
replace XML with JSON.
Upvotes: 1