如何在MS SQL 2014中从SQL查询生成JSON

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)

**编辑 - 纠正错字

  • 我已经实现了一个简单的建议,添加一个参数以强制在将@IncludeHead = 0表示为列表(用方括号括起来)时强制结果,即使仅返回一个结果 (2认同)

Joh*_*tti 5

以下应该为几乎任何数据集创建 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 将动态取消数据透视并生成以下内容:

在此处输入图片说明

cteBLD 将扩展并添加标志 New/End Row 在此处输入图片说明

最终选择

这会将它们放在一起并生成一个最终字符串,可以根据需要包装或嵌套。