joe*_*nte 6 sql-server query subquery json
我正在尝试构建一个查询,该查询会生成一个由 SQL Server 生成的 JSON 对象。我发现我可以使用子查询用包含问题列表的 JSON 字符串填充字段(在本例中为问题字段)。
下面是查询:
SELECT
quizzes.id AS 'id',
quizzes.name AS 'name',
quizzes.description AS 'description',
quizzes.instructions AS 'instructions',
author.id AS 'author.id',
author.midas AS 'author.midas',
author.first_name AS 'author.first_name',
author.last_name AS 'author.last_name',
author.email AS 'author.email',
author.tel AS 'author.tel',
author.department_name AS 'author.department_name',
author.created_at AS 'author.created_at',
author.last_updated AS 'author.last_updated',
course.id AS 'course.id',
course.name AS 'course.name',
course.description AS 'course.description',
course.crn AS 'course.crn',
instructor.id AS 'course.instructor.id',
instructor.midas AS 'course.instructor.midas',
instructor.first_name AS 'course.instructor.first_name',
instructor.last_name AS 'course.instructor.last_name',
instructor.email AS 'course.instructor.email',
instructor.tel AS 'course.instructor.tel',
instructor.department_name AS 'course.instructor.department_name',
instructor.created_at AS 'course.instructor.created_at',
instructor.last_updated AS 'course.instructor.last_updated',
course.created_at AS 'course.created_at',
course.last_updated AS 'course.last_updated',
(
SELECT
questions.id AS 'id',
questions.text AS 'text',
question_types.id AS 'type.id',
question_types.name AS 'type.name',
question_types.created_at AS 'type.created_at',
question_types.description AS 'type.description',
question_author.id AS 'author.id',
question_author.midas AS 'author.midas',
question_author.first_name AS 'author.first_name',
question_author.last_name AS 'author.last_name',
question_author.email AS 'author.email',
question_author.tel AS 'author.tel',
question_author.department_name AS 'author.department_name',
question_author.created_at AS 'author.created_at',
question_author.last_updated AS 'author.last_updated',
questions.is_graded AS 'is_graded',
questions.score_value AS 'score_value',
questions.created_at AS 'created_at',
questions.last_updated AS 'last_updated'
FROM
questions
LEFT JOIN users AS question_author ON question_author.id = questions.author
LEFT JOIN question_types ON question_types.id = questions.type
WHERE
questions.quiz = quizzes.id FOR JSON PATH, INCLUDE_NULL_VALUES
) AS 'questions',
quizzes.created_at AS 'created_at',
quizzes.last_updated AS 'last_updated'
FROM
quizzes
LEFT JOIN users AS author ON quizzes.author = author.id
LEFT JOIN courses AS course ON quizzes.course = course.id
LEFT JOIN users AS instructor ON course.instructor = instructor.id FOR JSON PATH,
INCLUDE_NULL_VALUES;
Run Code Online (Sandbox Code Playgroud)
问题是:当我执行此查询时,它以两行响应,其中生成的 JSON 字符串被分成两部分。显然这是不可取的。
经过调查,我发现如果我删除了LEFT JOIN
's,那么查询会按它应该的方式响应(只有一行完整的字符串)。
以下是以下行为的示例:
SELECT n = sc1.name FROM sys.syscolumns sc1 FOR JSON AUTO
Run Code Online (Sandbox Code Playgroud)
如上所示,返回了 11 行。
JSON 输出的长度约为 20,000 个字符。
我正在使用以下版本的 SQL Server
Microsoft SQL Server 2019 - 15.0.4073.23 (X64)
Developer Edition (64-bit) on Linux (Ubuntu 18.04.5 LTS) <X64>
Run Code Online (Sandbox Code Playgroud)
为什么会这样?我该如何解决?
joe*_*nte 10
经过进一步研究,我从这篇 StackOverflow 帖子中发现 SQL Server 将FOR JSON
查询分解为“~2kb 块”。
Sql Server 将 FOR JSON 查询的结果拆分为 ~2KB 块,因此您应该像 MSDN 页面那样连接片段,或者您可以将结果流式传输到某个输出流中。
这意味着每个块只能发送约 2000 个字符。
在Max Vernon和Andriy M的帮助下,我们找到了一个相当简单的解决方案。
DECLARE @json nvarchar(max);
;WITH src (n) AS
(
SELECT n = sc1.name
FROM sys.syscolumns sc1
FOR JSON AUTO
)
SELECT @json = src.n
FROM src
SELECT @json, LEN(@json);
Run Code Online (Sandbox Code Playgroud)
上面的查询返回两列。
理想情况下,您可以用您自己的查询替换两个括号之间的查询。
根据微软的文档
SQL Server 对此行集使用预定义的列名,其中一列是 NTEXT 类型——“XML_F52E2B61-18A1-11d1-B105-00805F49916B”——以 UTF-16 编码表示分块的 XML 行集。这需要 API 对 XML 块行集进行特殊处理,以在客户端将其公开为单个 XML 实例。在 ADO.Net 中,需要使用 ExecuteXmlReader,而在 ADO/OLEDB 中,应该使用 ICommandStream 接口。
(虽然以上确实是指XML,但JSON也是如此。)
首先,JSON 和 XML 响应以块的形式返回的原因是出于性能原因:
为了获得最大的 XML [JSON] 发布性能 FOR XML [JSON] 对结果行集进行流式 XML 格式化,并将其输出以小块的形式直接发送到服务器端 TDS 代码,而无需在服务器空间中缓冲整个 XML。块大小为 2033 个 UCS-2 字符。因此,大于 2033 个 UCS-2 字符的 XML 以多行的形式发送到客户端,每行都包含一个 XML 块。
上面的解决方案通过首先将结果设置FOR JSON
为变量,然后发送变量的值来规避这一点,这导致 SQL Server 将响应作为一行返回给客户端。
应该注意的是,一些数据库客户端(特别是 SQL Server Management Studio)能够“重建”分块响应,但是如果您使用 PHP ( PDO ) 或在免费试用客户端(例如 TablePlus for Mac)上运行,您将看到原始的、分块的响应。
就性能而言,我没有做过任何广泛的测试,但我可以提供以下来自我所做的有限测试的数据:
使用运行最新操作系统的 MacBook Pro,我发现平均以下查询
SELECT n = sc1.name
FROM sys.syscolumns sc1
FOR JSON AUTO
Run Code Online (Sandbox Code Playgroud)
将在大约23,300 秒内处理
虽然查询
DECLARE
@json nvarchar (max);
;WITH src (n) AS
(
SELECT n = sc1.name
FROM sys.syscolumns sc1
FOR JSON AUTO
)
SELECT @json = src.n
FROM src
SELECT @json, LEN(@json);
Run Code Online (Sandbox Code Playgroud)
平均花费137.8 秒的处理时间。
这似乎与文档所说的直接冲突,所以我不确定这些结果的可信度。但是,可能值得自己对此进行测试。
归档时间: |
|
查看次数: |
729 次 |
最近记录: |