Improve SQL Server query to convert arbitrary table to JSON Improve SQL Server query to convert arbitrary table to JSON json json

Improve SQL Server query to convert arbitrary table to JSON


I say if you really want to kick up performance, use metaprogramming. The example below tries this with 40,000 rows and returns results in less than a second (not counting inserting the initial 40k rows, which in this example only takes about 2 seconds). It also takes into account your data types to not enclose numbers in quotes.

declare @xd table (col1 varchar(max), col2 int, col3 real, colDate datetime, colNull int);declare @i int = 0;while @i < 10000 begin    set @i += 1;    insert into @xd    select '', null, null, null, null    union all select 'ItemA', 123, 123.123, getDate(), null    union all select 'ItemB', 456, 456.456, getDate(), null    union all select '7890', 789, 789.789, getDate(), null;end;select *into #json_basefrom (    -- Insert SQL Statement here    select * from @xd) t;declare @columns table (    id int identity primary key,    name sysname,    datatype sysname,    is_number bit,    is_date bit);insert into @columns(name, datatype, is_number, is_date)select columns.name, types.name,       case when number_types.name is not NULL            then 1 else 0       end as is_number,       case when date_types.name is not NULL            then 1 else 0       end as is_datefrom tempdb.sys.columnsjoin tempdb.sys.types    on (columns.system_type_id = types.system_type_id)left join (values ('int'), ('real'), ('numeric'),                  ('decimal'), ('bigint'), ('tinyint')) as number_types(name)    on (types.name = number_types.name)left join (values ('date'), ('datetime'), ('datetime2'),                  ('smalldatetime'), ('time'), ('datetimeoffset')) as date_types(name)    on (types.name = date_types.name)where object_id = OBJECT_ID('tempdb..#json_base');declare @field_list varchar(max) = STUFF((    select '+'',''+' + QUOTENAME(QUOTENAME(name, '"') + ':', '''')           + '+' + case when is_number = 1                        then 'COALESCE(LTRIM('                                + QUOTENAME(name) + '),''null'')'                        when is_date = 1                        then 'COALESCE(QUOTENAME(LTRIM(convert(varchar(max), '                                + QUOTENAME(name) + ', 126)),''"''),''null'')'                        else 'COALESCE(QUOTENAME('                                + QUOTENAME(name) + ',''"''),''null'')'                   end    from @columns    for xml path('')),    1, 5, '');create table #json_result (    id int identity primary key,    line varchar(max));declare @sql varchar(max) = REPLACE(    'insert into #json_result '  + 'select '',{''+{f}+''}'' '  + 'from #json_base', '{f}', @field_list);exec(@sql);update #json_resultset line = STUFF(line, 1, 1, '')where id = 1;select '['UNION ALLselect linefrom #json_resultUNION ALLselect ']';drop table #json_base;drop table #json_result;


From Firoz Ansari:

CREATE PROCEDURE [dbo].[GetJSON] (@ParameterSQL AS VARCHAR(MAX))ASBEGINDECLARE @SQL NVARCHAR(MAX)DECLARE @XMLString VARCHAR(MAX)DECLARE @XML XMLDECLARE @Paramlist NVARCHAR(1000)SET @Paramlist = N'@XML XML OUTPUT'SET @SQL = 'WITH PrepareTable (XMLString) 'SET @SQL = @SQL + 'AS ( 'SET @SQL = @SQL + @ParameterSQL+ ' FOR XML RAW, TYPE, ELEMENTS 'SET @SQL = @SQL + ') 'SET @SQL = @SQL + 'SELECT @XML = XMLString FROM PrepareTable 'EXEC sp_executesql @SQL, @Paramlist, @XML=@XML OUTPUTSET @XMLString = CAST(@XML AS VARCHAR(MAX))DECLARE @JSON VARCHAR(MAX)DECLARE @Row VARCHAR(MAX)DECLARE @RowStart INTDECLARE @RowEnd INTDECLARE @FieldStart INTDECLARE @FieldEnd INTDECLARE @Key VARCHAR(MAX)DECLARE @Value VARCHAR(MAX)DECLARE @StartRoot VARCHAR(100); SET @StartRoot = ''DECLARE @EndRoot VARCHAR(100); SET @EndRoot = ''DECLARE @StartField VARCHAR(100); SET @StartField = ''SET @RowStart = CharIndex(@StartRoot, @XMLString, 0)SET @JSON = ''WHILE @RowStart &gt; 0BEGIN    SET @RowStart = @RowStart+Len(@StartRoot)    SET @RowEnd = CharIndex(@EndRoot, @XMLString, @RowStart)    SET @Row = SubString(@XMLString, @RowStart, @RowEnd-@RowStart)    SET @JSON = @JSON+'{'    -- for each row    SET @FieldStart = CharIndex(@StartField, @Row, 0)    WHILE @FieldStart &gt; 0    BEGIN        -- parse node key        SET @FieldStart = @FieldStart+Len(@StartField)        SET @FieldEnd = CharIndex(@EndField, @Row, @FieldStart)        SET @Key = SubString(@Row, @FieldStart, @FieldEnd-@FieldStart)        SET @JSON = @JSON+'"'+@Key+'":'        -- parse node value        SET @FieldStart = @FieldEnd+1        SET @FieldEnd = CharIndex('0 SET @JSON = SubString(@JSON, 0, LEN(@JSON))    SET @JSON = @JSON+'},'    --/ for each row    SET @RowStart = CharIndex(@StartRoot, @XMLString, @RowEnd)ENDIF LEN(@JSON) > 0 SET @JSON = SubString(@JSON, 0, LEN(@JSON))SET @JSON = '[' + @JSON + ']'SELECT @JSONEND