我试图写一个相当复杂的SQL查询,结果产生JSON。一切都很好,除了一些硬编码数组我需要在层次结构中更深入,我必须使用UNION ALL
来创建。我在这里制作了一个显示我的问题的查询(不需要数据 - 我在Azure SQL数据库中运行它):
SELECT
'Hi' AS Greeting,
(
SELECT
CASE WHEN DatePart(second, GetDate()) % 2 = 1 THEN
'qwerty'
ELSE
'asdf'
END AS Stuff
FOR JSON PATH
) AS StuffArray,
(
CASE WHEN DatePart(second, GetDate()) % 2 = 1 THEN
(
SELECT 'qwerty' AS Stuff
FOR JSON PATH
)
ELSE
(
SELECT 'asdf' AS Stuff
FOR JSON PATH
)
END
) AS QuotedStuffArray,
(
CASE WHEN DatePart(second, GetDate()) % 2 = 1 THEN
(
SELECT * FROM
(
SELECT 'qwerty' AS Stuff
UNION ALL
SELECT 'zxcvb' AS Stuff
) AS SubSelect
FOR JSON PATH
)
ELSE
(
SELECT 'asdf' AS Stuff
FOR JSON PATH
)
END
) AS WhyItMatters,
(
SELECT * FROM
(
SELECT 'qwerty' AS Stuff
UNION ALL
SELECT 'zxcvb' AS Stuff
) AS SubSelect
FOR JSON PATH
) AS ButThisIsFine
FOR JSON PATH
这输出这个JSON:
[
{
"Greeting": "Hi",
"StuffArray": [
{
"Stuff": "qwerty"
}
],
"QuotedStuffArray": "[{\"Stuff\":\"qwerty\"}]",
"WhyItMatters": "[{\"Stuff\":\"qwerty\"},{\"Stuff\":\"zxcvb\"}]",
"ButThisIsFine": [
{
"Stuff": "qwerty"
},
{
"Stuff": "zxcvb"
}
]
}
]
在此查询中,您将在基础对象之外的层次结构中看到四个不同的对象:StuffArray
,QuotedStuffArray
,WhyItMatters
和ButThisIsFine
。 StuffArray
对象正是我想要的所有对象的样子 - 纯JSON没有任何转义。但是,当我开始将SELECT
语法放在我的CASE
语句中时,我的结果开始被引用,如QuotedStuffArray
对象所示。所以对于前两个对象,这很好。但我有一个问题,我有时需要对两个硬编码值进行条件UNION
,这迫使我将SELECT
放入CASE
语句中,如WhyItMatters
所示宾语。 ButThisIsFine
对象生成的输出格式化,就像我想要格式化WhyItMatters
对象一样,但它删除了我需要的条件UNION
。
如何在保留条件WhyItMatters
语句的同时使用ButThisIsFine
对象生成纯JSON而不使用转义引号生成纯JSON?
答案 0 :(得分:6)
在这个查询的优化器中有一些令人着迷的行为,我不确定它是不是一个bug。以下查询将不添加转义:
SELECT
'Hi' AS Greeting,
(
CASE WHEN 1 = 1 THEN (
SELECT * FROM (
SELECT 'qwerty' AS [Stuff]
UNION ALL
SELECT 'zxcvb' AS [Stuff]
) _
FOR JSON PATH
) ELSE (
SELECT 'asdf' AS [Stuff]
FOR JSON PATH
)
END
) AS WhyItMatters
FOR JSON PATH
可以优化CASE
, 优化,最终结果是嵌套良好的JSON。但是如果我们删除了优化事物的能力,它就会退化为以转义字符串粘贴:
SELECT
'Hi' AS Greeting,
(
CASE WHEN RAND() = 1 THEN (
SELECT * FROM (
SELECT 'qwerty' AS [Stuff]
UNION ALL
SELECT 'zxcvb' AS [Stuff]
) _
FOR JSON PATH
) ELSE (
SELECT 'asdf' AS [Stuff]
FOR JSON PATH
)
END
) AS WhyItMatters
FOR JSON PATH
似乎不合逻辑的是,一个查询会导致处理类型化的JSON而另一个不会,但是你去了。 JSON
不是T-SQL中的实际类型(与XML
不同),因此我们不能CAST
或CONVERT
,但JSON_QUERY
将大致相同同样的事情:
SELECT
'Hi' AS Greeting,
JSON_QUERY(
CASE WHEN RAND() = 1 THEN (
SELECT * FROM (
SELECT 'qwerty' AS [Stuff]
UNION ALL
SELECT 'zxcvb' AS [Stuff]
) _
FOR JSON PATH
) ELSE (
SELECT 'asdf' AS [Stuff]
FOR JSON PATH
)
END
) AS WhyItMatters
FOR JSON PATH
请注意,如果参数已经是JSON(在常量情况下),这也有效,因此无论如何都可以安全添加。
答案 1 :(得分:2)
我找到了一个可能的解决方案,但我真的不喜欢它。我发布了我所拥有的东西,希望有人有更好的解决方案。
在WHERE
的每个分支上使用UNION
语句,对我的CASE语句的肯定或完全否定都可以防止" strigifying"我的结果。
例如,此查询:
SELECT
'Hi' AS Greeting,
(
SELECT * FROM
(
SELECT 'asdf' AS Stuff WHERE DatePart(second, GetDate()) % 2 = 0
UNION ALL
SELECT 'qwerty' AS Stuff WHERE DatePart(second, GetDate()) % 2 = 1
UNION ALL
SELECT 'zxcvb' AS Stuff WHERE DatePart(second, GetDate()) % 2 = 1
) AS SubSelect
FOR JSON PATH
) AS Try1
FOR JSON PATH
提供以下结果:
[
{
"Greeting": "Hi",
"Try1": [
{
"Stuff": "qwerty"
},
{
"Stuff": "zxcvb"
}
]
}
]
如果找不到更好的东西,我可以继续前进。但这似乎是控制这种情况的一种黑客方式。
答案 2 :(得分:2)
我测试了两种解决方案的性能:
首先 - 通过JSON_QUERY
:
declare @i int = 1;
SELECT
JSON_QUERY(
CASE when @i = 1 THEN
(
SELECT * FROM
(
select textCol AS Stuff from table1 where id % 2 = 0
UNION ALL
SELECT textCol AS Stuff from table1 where id % 2 <> 0
) AS SubSelect
FOR JSON PATH
)
ELSE
(
SELECT textCol AS Stuff from table1
FOR JSON PATH
)
END
) AS WhyItMatters
FOR JSON path
给我平均执行时间91ms。
第二
declare @i int = 1;
SELECT
(SELECT * FROM
(
select textCol AS Stuff from table1 where id % 2 = 0 and @i = 1
UNION ALL
SELECT textCol AS Stuff from table1 where id % 2 <> 0 and @i = 1
union all
SELECT textCol AS Stuff from table1 where @i <> 1
) AS SubSelect
FOR JSON PATH
) AS WhyItMatters
FOR JSON path
给我平均执行时间45ms。
table1
包含12727行。生成的JSON长度约为1500000个字符。