Joa*_*aal 8 xml sql t-sql json sql-server-2014
问题:在MS SQL 2014中从SQL查询生成JSON的最佳解决方案是什么?我创建了一个程序,但速度非常慢.
我的例子:
DECLARE @customers xml;
DECLARE @json NVARCHAR(max);
SET @customers = (SELECT * FROM dbo.Customers FOR XML path, root)
EXEC [dbo].[HTTP_JSON] @customers, @json
EXEC [dbo].[HTTP_JSON](@Shopping)
Create PROCEDURE [dbo].[HTTP_JSON]
@parameters xml, @response NVARCHAR(max) OUTPUT
WITH EXEC AS CALLER
AS
set @response = (SELECT Stuff(
(SELECT * from
(SELECT ',
{'+
Stuff((SELECT ',"'+coalesce(b.c.value('local-name(.)', 'NVARCHAR(MAX)'),'')+'":"'+
b.c.value('text()[1]','NVARCHAR(MAX)') +'"'
from x.a.nodes('*') b(c)
for xml path(''),TYPE).value('(./text())[1]','NVARCHAR(MAX)')
,1,1,'')+'}'
from @parameters.nodes('/root/*') x(a)
) JSON(theLine)
for xml path(''),TYPE).value('.','NVARCHAR(MAX)' )
,1,1,''))
GO
Run Code Online (Sandbox Code Playgroud)
Joh*_*tti 12
只是为了好玩,我根据我之前的答案创建了一个标量函数.
除了明显的XML参数之外,我还添加了两个:1)Include Header(如下图所示),以及2)ToLower案例(我更喜欢我的JSON字段名称,小写链接到我的类等).
如果查询是多个记录,则将返回格式化的数组.
Declare @Table table (ID int,Active bit,First_Name varchar(50),Last_Name varchar(50),EMail varchar(50))
Insert into @Table values
(1,1,'John','Smith','john.smith@email.com'),
(2,0,'Jane','Doe' ,'jane.doe@email.com')
Select A.ID
,A.Last_Name
,A.First_Name
,B.JSON
From @Table A
Cross Apply (Select JSON=[dbo].[udf-Str-JSON](0,1,(Select A.* For XML Raw)) ) B
Run Code Online (Sandbox Code Playgroud)
返回
ID Last_Name First_Name JSON
1 Smith John {"id":"1","active":"1","first_name":"John","last_name":"Smith","email":"john.smith@email.com"}
2 Doe Jane {"id":"2","active":"0","first_name":"Jane","last_name":"Doe","email":"jane.doe@email.com"}
Run Code Online (Sandbox Code Playgroud)
或者更简单
Select JSON=[dbo].[udf-Str-JSON](0,1,(Select * From @Table for XML RAW))
Run Code Online (Sandbox Code Playgroud)
返回Header ON
{
"status": {
"successful": "true",
"timestamp": "2016-10-09 06:08:16 GMT",
"rows": "2"
},
"results": [{
"id": "1",
"active": "1",
"first_name": "John",
"last_name": "Smith",
"email": "john.smith@email.com"
}, {
"id": "2",
"active": "0",
"first_name": "Jane",
"last_name": "Doe",
"email": "jane.doe@email.com"
}]
}
Run Code Online (Sandbox Code Playgroud)
返回Header Off
[{
"id": "1",
"active": "1",
"first_name": "John",
"last_name": "Smith",
"email": "john.smith@email.com"
}, {
"id": "2",
"active": "0",
"first_name": "Jane",
"last_name": "Doe",
"email": "jane.doe@email.com"
}]
Run Code Online (Sandbox Code Playgroud)
UDF
ALTER FUNCTION [dbo].[udf-Str-JSON] (@IncludeHead int,@ToLowerCase int,@XML xml)
Returns varchar(max)
AS
Begin
Declare @Head varchar(max) = '',@JSON varchar(max) = ''
; with cteEAV as (Select RowNr=Row_Number() over (Order By (Select NULL))
,Entity = xRow.value('@*[1]','varchar(100)')
,Attribute = xAtt.value('local-name(.)','varchar(100)')
,Value = xAtt.value('.','varchar(max)')
From @XML.nodes('/row') As R(xRow)
Cross Apply R.xRow.nodes('./@*') As A(xAtt) )
,cteSum as (Select Records=count(Distinct Entity)
,Head = IIF(@IncludeHead=0,IIF(count(Distinct Entity)<=1,'[getResults]','[[getResults]]'),Concat('{"status":{"successful":"true","timestamp":"',Format(GetUTCDate(),'yyyy-MM-dd hh:mm:ss '),'GMT','","rows":"',count(Distinct Entity),'"},"results":[[getResults]]}') )
From cteEAV)
,cteBld as (Select *
,NewRow=IIF(Lag(Entity,1) over (Partition By Entity Order By (Select NULL))=Entity,'',',{')
,EndRow=IIF(Lead(Entity,1) over (Partition By Entity Order By (Select NULL))=Entity,',','}')
,JSON=Concat('"',IIF(@ToLowerCase=1,Lower(Attribute),Attribute),'":','"',Value,'"')
From cteEAV )
Select @JSON = @JSON+NewRow+JSON+EndRow,@Head = Head From cteBld, cteSum
Return Replace(@Head,'[getResults]',Stuff(@JSON,1,1,''))
End
-- Parameter 1: @IncludeHead 1/0
-- Parameter 2: @ToLowerCase 1/0 (converts field name to lowercase
-- Parameter 3: (Select * From ... for XML RAW)
Run Code Online (Sandbox Code Playgroud)
**编辑 - 纠正错字
以下应该为几乎任何数据集创建 JSON 数组。但是,我还没有创建将位转换为真/假的方法。
只需要考虑一点:初始 SELECT 中的 FIRST 列必须是主键,它等同于 ENTITY 字段。在这种情况下,Select * from @User for XML RAW
... ID 是实体,恰好是表中的第一个字段
就性能而言,具有 19 个字段的 500 条记录在 0.694 秒内创建了一个 191,987 字节的 JSON 字符串(50 条记录在 0.098 秒内)
考虑以下:
Declare @User table (ID int,Active bit,First_Name varchar(50),Last_Name varchar(50),EMail varchar(50),LastOn DateTime)
Insert into @User values
(1,1,'John','Smith','john.smith@email.com','2016-10-05 17:32:41.903'),
(2,0,'Jane','Doe' ,'jane.doe@email.com','2016-10-05 08:25:18.203')
Declare @XML xml = (Select * From @User for XML RAW)
Declare @JSON varchar(max) = ''
;with cteEAV as (
Select RowNr = Row_Number() over (Order By (Select NULL))
,Entity = xRow.value('@*[1]','varchar(100)')
,Attribute = xAtt.value('local-name(.)','varchar(100)')
,Value = xAtt.value('.','varchar(max)')
From @XML.nodes('/row') As A(xRow)
Cross Apply A.xRow.nodes('./@*') As B(xAtt) )
,cteBld as (
Select *
,NewRow = IIF(Lag(Entity,1) over (Partition By Entity Order By (Select NULL))=Entity,'',',{')
,EndRow = IIF(Lead(Entity,1) over (Partition By Entity Order By (Select NULL))=Entity,',','}')
,JSON = Concat('"',Attribute,'":','"',Value,'"')
From cteEAV )
Select @JSON = @JSON+NewRow+JSON+EndRow
From cteBld
Select '['+Stuff(@JSON,1,1,'')+']'
Run Code Online (Sandbox Code Playgroud)
退货
[{"ID":1, "Active":1, "First_Name":"John", "Last_Name":"Smith", "EMail":"john.smith@email.com", "LastOn":"2016-10-05T17:32:41.903", "TotalSales":25569.0000} ,{"ID":2, "Active":0, "First_Name":"Jane", "Last_Name":"Doe", "EMail":"jane.doe@email.com", "LastOn":"2016-10-05T08:25:18.203", "TotalSales":22888.0000}]
Run Code Online (Sandbox Code Playgroud)
更易读的版本
cteEAV 将动态取消数据透视并生成以下内容:
最终选择
这会将它们放在一起并生成一个最终字符串,可以根据需要包装或嵌套。