针对结构不良 API 响应的 OPENJSON() 优化
OPENJSON() optimization for poorly structured API response
我正在尝试使用 TSheets API 将数据提取到我的内部数据库中,但是返回的响应方式让我无法弄清楚如何有效地将其放入 table结构。
出于演示目的,我已经简化了响应,但它基本上看起来像这样:
{
"results": {
"users": {
"12345": {
"id": 12345,
"first_name": "Demo",
"last_name": "User",
"username": "demo@gmail.com",
"email": "demo@gmail.com"
},
"321123": {
"id": 321123,
"first_name": "John",
"last_name": "Wayne",
"username": "notreal@email.com",
"email": "notreal@email.com"
},
"98765": {
"id": 98765,
"first_name": "Suzie",
"last_name": "Q",
"username": "email@company.com",
"email": "email@company.com"
}
}
},
"more": false
}
每个用户都列为单独的 属性,而不是一组用户,其 ID 作为 属性 的名称。他们在所有端点上都使用这种模式,所以我需要一种方法来了解响应的结构,以便像我习惯的那样查询它。
我写了一个语句,使用动态 sql 将其放入 table 结构中,但我想知道是否有人更擅长 JSON 函数可以提出一个更好的解决方案。
这是我的 SQL 代码...
GO
--// simplifed version of the actual json response for security and demo purposes
DECLARE @user_response NVARCHAR(MAX) = N'
{
"results": {
"users": {
"12345": {
"id": 12345,
"first_name": "Demo",
"last_name": "User",
"username": "demo@gmail.com",
"email": "demo@gmail.com"
},
"321123": {
"id": 321123,
"first_name": "John",
"last_name": "Wayne",
"username": "notreal@email.com",
"email": "notreal@email.com"
},
"98765": {
"id": 98765,
"first_name": "Suzie",
"last_name": "Q",
"username": "email@company.com",
"email": "email@company.com"
}
}
},
"more": false
}
'
--// put users object into variable
DECLARE @users NVARCHAR(MAX) = (
SELECT users.users
FROM OPENJSON(@user_response)
WITH (results NVARCHAR(MAX) AS JSON
, more VARCHAR(20)) as body
CROSS APPLY OPENJSON(results)
WITH (users NVARCHAR(MAX) AS JSON) as users
)
--// extract the keys from the users object
DECLARE @keys TABLE ([key] VARCHAR(100), [index] INT)
INSERT INTO @keys
SELECT [key], ROW_NUMBER() OVER (ORDER BY [key]) 'index'
FROM OPENJSON(@users)
--// initialize looping variables
DECLARE @i INT = 1
, @key VARCHAR(100)
, @sql NVARCHAR(MAX)
SELECT @sql = 'DECLARE @user_response NVARCHAR(MAX) = N''' + @user_response + ''''
--// loop through keys and UNION individual queries on the original json response
WHILE (@i <= (SELECT MAX([index]) FROM @keys))
BEGIN
SELECT @key = (SELECT [key] FROM @keys WHERE [index] = @i)
SELECT @sql = @sql + CASE WHEN @i = 1 THEN '' ELSE 'UNION' END + '
SELECT user_data.*
FROM OPENJSON(@user_response)
WITH (results NVARCHAR(MAX) AS JSON)
CROSS APPLY OPENJSON(results)
WITH (users NVARCHAR(MAX) AS JSON)
CROSS APPLY OPENJSON(users)
WITH ([' + @key + '] NVARCHAR(MAX) AS JSON)
CROSS APPLY OPENJSON([' + @key + '])
WITH (id INT
, first_name VARCHAR(100)
, last_name VARCHAR(100)
, username VARCHAR(200)
, email VARCHAR(200)) as [user_data]'
SELECT @i = @i + 1
END
--// execute final dynamic query
EXEC sp_executesql @sql
这条语句的结果集如下所示:
|id |first_name|last_name|username |email |
|-----|----------|---------|-----------------|-----------------|
|98765|Suzie |Q |email@company.com|email@company.com|
|321123|John |Wayne |notreal@email.com|notreal@email.com|
|12345|Demo |User |demo@gmail.com |demo@gmail.com |
提前感谢您的想法和反馈。
像这样:
select u.*
from openjson(@user_response, '$.results.users') d
cross apply
openjson(d.value)
with
(
id int '$.id',
first_name varchar(200) '$.first_name',
last_name varchar(200) '$.last_name',
username varchar(200) '$.username',
email varchar(200) '$.email'
) u
如果你需要它,你可以通过 d.[key]
获取对象名称
我正在尝试使用 TSheets API 将数据提取到我的内部数据库中,但是返回的响应方式让我无法弄清楚如何有效地将其放入 table结构。
出于演示目的,我已经简化了响应,但它基本上看起来像这样:
{
"results": {
"users": {
"12345": {
"id": 12345,
"first_name": "Demo",
"last_name": "User",
"username": "demo@gmail.com",
"email": "demo@gmail.com"
},
"321123": {
"id": 321123,
"first_name": "John",
"last_name": "Wayne",
"username": "notreal@email.com",
"email": "notreal@email.com"
},
"98765": {
"id": 98765,
"first_name": "Suzie",
"last_name": "Q",
"username": "email@company.com",
"email": "email@company.com"
}
}
},
"more": false
}
每个用户都列为单独的 属性,而不是一组用户,其 ID 作为 属性 的名称。他们在所有端点上都使用这种模式,所以我需要一种方法来了解响应的结构,以便像我习惯的那样查询它。
我写了一个语句,使用动态 sql 将其放入 table 结构中,但我想知道是否有人更擅长 JSON 函数可以提出一个更好的解决方案。
这是我的 SQL 代码...
GO
--// simplifed version of the actual json response for security and demo purposes
DECLARE @user_response NVARCHAR(MAX) = N'
{
"results": {
"users": {
"12345": {
"id": 12345,
"first_name": "Demo",
"last_name": "User",
"username": "demo@gmail.com",
"email": "demo@gmail.com"
},
"321123": {
"id": 321123,
"first_name": "John",
"last_name": "Wayne",
"username": "notreal@email.com",
"email": "notreal@email.com"
},
"98765": {
"id": 98765,
"first_name": "Suzie",
"last_name": "Q",
"username": "email@company.com",
"email": "email@company.com"
}
}
},
"more": false
}
'
--// put users object into variable
DECLARE @users NVARCHAR(MAX) = (
SELECT users.users
FROM OPENJSON(@user_response)
WITH (results NVARCHAR(MAX) AS JSON
, more VARCHAR(20)) as body
CROSS APPLY OPENJSON(results)
WITH (users NVARCHAR(MAX) AS JSON) as users
)
--// extract the keys from the users object
DECLARE @keys TABLE ([key] VARCHAR(100), [index] INT)
INSERT INTO @keys
SELECT [key], ROW_NUMBER() OVER (ORDER BY [key]) 'index'
FROM OPENJSON(@users)
--// initialize looping variables
DECLARE @i INT = 1
, @key VARCHAR(100)
, @sql NVARCHAR(MAX)
SELECT @sql = 'DECLARE @user_response NVARCHAR(MAX) = N''' + @user_response + ''''
--// loop through keys and UNION individual queries on the original json response
WHILE (@i <= (SELECT MAX([index]) FROM @keys))
BEGIN
SELECT @key = (SELECT [key] FROM @keys WHERE [index] = @i)
SELECT @sql = @sql + CASE WHEN @i = 1 THEN '' ELSE 'UNION' END + '
SELECT user_data.*
FROM OPENJSON(@user_response)
WITH (results NVARCHAR(MAX) AS JSON)
CROSS APPLY OPENJSON(results)
WITH (users NVARCHAR(MAX) AS JSON)
CROSS APPLY OPENJSON(users)
WITH ([' + @key + '] NVARCHAR(MAX) AS JSON)
CROSS APPLY OPENJSON([' + @key + '])
WITH (id INT
, first_name VARCHAR(100)
, last_name VARCHAR(100)
, username VARCHAR(200)
, email VARCHAR(200)) as [user_data]'
SELECT @i = @i + 1
END
--// execute final dynamic query
EXEC sp_executesql @sql
这条语句的结果集如下所示:
|id |first_name|last_name|username |email |
|-----|----------|---------|-----------------|-----------------|
|98765|Suzie |Q |email@company.com|email@company.com|
|321123|John |Wayne |notreal@email.com|notreal@email.com|
|12345|Demo |User |demo@gmail.com |demo@gmail.com |
提前感谢您的想法和反馈。
像这样:
select u.*
from openjson(@user_response, '$.results.users') d
cross apply
openjson(d.value)
with
(
id int '$.id',
first_name varchar(200) '$.first_name',
last_name varchar(200) '$.last_name',
username varchar(200) '$.username',
email varchar(200) '$.email'
) u
如果你需要它,你可以通过 d.[key]