2013-02-13 30 views
8

Sau nhiều tìm kiếm và kết hợp các kỹ thuật rất tuyệt vời để chuyển đổi tập hợp kết quả bằng cách sử dụng lệnh FOR XML và .nodes() trên web, tôi có thể tạo truy vấn đơn này (không phải là một thủ tục được lưu trữ) mà thực hiện một công việc hợp lý để chuyển đổi bất kỳ truy vấn SQL tùy ý nào thành một mảng JSON.Cải thiện truy vấn SQL Server để chuyển đổi bảng tùy ý thành JSON

Truy vấn sẽ mã hóa từng hàng dữ liệu dưới dạng một đối tượng JSON đơn lẻ có dấu phẩy hàng đầu. Các hàng dữ liệu được bao bọc bởi các dấu ngoặc và toàn bộ tập kết quả sau đó được mong đợi sẽ được xuất ra một tệp.

Tôi muốn xem liệu có ai có thể xem cách cải thiện hiệu suất của nó không?

Dưới đây là các truy vấn với một bảng mẫu:

declare @xd table (col1 varchar(max), col2 int, col3 real, colNull int) 

insert into @xd 
select '', null, null, null 
UNION ALL select 'ItemA', 123, 123.123, null 
UNION ALL select 'ItemB', 456, 456.456, null 
UNION ALL select '7890', 789, 789.789, null 

select '[{}' 
UNION ALL 
select ',{' + STUFF((
    (select ',' 
     + '"' + r.value('local-name(.)', 'varchar(max)') + '":' 
     + case when r.value('./@xsi:nil', 'varchar(max)') = 'true' then 'null' 
     when isnumeric(r.value('.', 'varchar(max)')) = 1 
      then r.value('.', 'varchar(max)') 
     else '"' + r.value('.', 'varchar(max)') + '"' 
     end 
    from rows.nodes('/row/*') as x(r) for xml path('')) 
    ), 1, 1, '') + '}' 
from (
    -- Arbitrary query goes here, (fields go where t.* is, table where @xd t is) 
    select (select t.* for xml raw,type,elements XSINIL) rows 
    from @xd t 
) xd 
UNION ALL 
select ']' 

phê bình lớn nhất của tôi của nó, là nó điên cuồng chậm.
Hiện tại mất khoảng 3:30 cho ~ 42.000 hàng.

Phê phán lớn khác của tôi là hiện tại giả định rằng mọi thứ trông giống như một số là một số. Nó không cố gắng để khám phá loại cột trong ít nhất (và tôi thậm chí không chắc chắn nếu nó có thể).

Một phê bình nhỏ cuối cùng là hàng dữ liệu đầu tiên sẽ có dấu phẩy ở phía trước và về mặt kỹ thuật nó không nên. Để bù lại, nó yêu cầu đối tượng JSON trống trong hàng đầu tiên bắt đầu mảng JSON.

Các phê bình khác (tốt nhất là với các giải pháp) được mời, giới hạn thực sự duy nhất tôi có là giải pháp có thể lặp lại một cách rõ ràng trên nhiều truy vấn SQL tùy ý mà không cần phải xác định rõ tên cột.

Tôi đang sử dụng SQL Server 2012.

Cảm ơn và để bất cứ ai khác như tôi những người đang tìm kiếm một kết quả SQL tổng quát -> JSON Mảng chuyển đổi, ENJOY!

+0

Trong khi tôi hoan nghênh bạn SQL-fu, tôi phải tự hỏi: tại sao? Kịch bản trong thế giới thực là nơi bạn cần làm điều này? Tôi không cố gắng để tiêu cực ở đây, chỉ lúng túng là tại sao bạn cần điều này. –

+0

Trong trường hợp này, tôi đang tìm một cách đặc biệt để tải nhanh các bộ kết quả vào cơ sở dữ liệu NoSQL như CouchDB mà không cần phải xây dựng nhiều cơ sở hạ tầng hoặc thêm bất cứ thứ gì vào môi trường SQL sản xuất của tôi. Mongo, Couch, et al. dường như sử dụng JSON làm ngôn ngữ lingua của họ để vận chuyển dữ liệu. Khi ở trong NoSQL DB, chúng ta có thể thử nghiệm với việc cắt và dicing các bộ dữ liệu để xem chúng hoạt động như thế nào. Việc sao chép cơ sở dữ liệu CouchDB để tạo ra các kho lưu trữ cục bộ trên máy tính để bàn từ xa, máy tính xách tay, điện thoại thông minh, vv là cách dễ dàng hơn việc quản lý cơ sở hạ tầng nhân rộng SQL. Vì vậy, chúng tôi đang làm một số sự tích cực. –

+0

Và đừng quá ngu ngốc ở đây, hầu hết điều đó được tìm thấy bằng cách tìm kiếm câu lệnh SQL hiện có thành JSON và SQL tới các câu trả lời của cặp khóa/giá trị.Có, tôi đã đặt nó lại với nhau, đặt một số dấu ngoặc xung quanh nó qua UNION ALL, sử dụng câu lệnh case để xử lý các dấu ngoặc kép thông minh hơn, và bắt đầu thử với điều XSINIL (và có lẽ tôi đã học được một số điều này thực sự hoạt động như thế nào;)) nhưng tôi thực sự không thể lấy tín dụng cho việc này. Về điều duy nhất tôi đã làm mới được kết hợp các truy vấn phụ ở phía dưới để trả về mỗi hàng như một tập dữ liệu XML đầu tiên trước khi có truy vấn trên biến nó thành các cặp khóa/giá trị. –

Trả lời

11

Tôi nói nếu bạn thực sự muốn khởi động hiệu suất, hãy sử dụng lập trình meta. Ví dụ dưới đây cố gắng điều này với 40.000 hàng và trả về kết quả trong chưa đầy một giây (không tính chèn các hàng 40k ban đầu, trong ví dụ này chỉ mất khoảng 2 giây). Nó cũng đưa vào tài khoản các loại dữ liệu của bạn để không kèm theo các số trong dấu ngoặc kép.

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_base 
from (
    -- 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_date 
from tempdb.sys.columns 
join 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_result 
set line = STUFF(line, 1, 1, '') 
where id = 1; 

select '[' 
UNION ALL 
select line 
from #json_result 
UNION ALL 
select ']'; 

drop table #json_base; 
drop table #json_result; 
+0

Đẹp. Tôi biết tôi không nên ngủ trên đó - đây là cách tiếp cận mà tôi cũng sẽ đề xuất. Thực sự có thể trở nên rất hữu ích ... –

+0

Tốt thôi! Chỉ 6 giây trên bàn thực tế của tôi. Một hiệu ứng phụ khác là những ngày tháng sắp ra mắt vào ngày 8 tháng 2 năm 2013 lúc 12:00 sáng. Hãy để tôi nhìn vào đó –

+0

Ok, tôi đã mở rộng giải pháp bên cạnh để thêm trường bit "is_date", có thích hợp để điều chỉnh câu trả lời gốc với các sửa đổi của tôi trước khi chấp nhận không? Và cảm ơn vì đã cho tôi thấy về việc tham gia vào "giá trị" mà tôi chưa từng thấy trước đây. Đó là khá mát mẻ! –

1

Từ Firoz Ansari:

CREATE PROCEDURE [dbo].[GetJSON] (
@ParameterSQL AS VARCHAR(MAX) 
) 
AS 
BEGIN 

DECLARE @SQL NVARCHAR(MAX) 
DECLARE @XMLString VARCHAR(MAX) 
DECLARE @XML XML 
DECLARE @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, @[email protected] OUTPUT 
SET @XMLString = CAST(@XML AS VARCHAR(MAX)) 

DECLARE @JSON VARCHAR(MAX) 
DECLARE @Row VARCHAR(MAX) 
DECLARE @RowStart INT 
DECLARE @RowEnd INT 
DECLARE @FieldStart INT 
DECLARE @FieldEnd INT 
DECLARE @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; 0 
BEGIN 
    SET @RowStart = @RowStart+Len(@StartRoot) 
    SET @RowEnd = CharIndex(@EndRoot, @XMLString, @RowStart) 
    SET @Row = SubString(@XMLString, @RowStart, @[email protected]) 
    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, @[email protected]) 
     SET @JSON = @JSON+'"'[email protected]+'":' 

     -- 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) 
END 
IF LEN(@JSON) > 0 SET @JSON = SubString(@JSON, 0, LEN(@JSON)) 
SET @JSON = '[' + @JSON + ']' 
SELECT @JSON 
END 
+0

Vấn đề/giới hạn với cách tiếp cận này là kết quả được giới hạn trong giới hạn chuỗi @JSON varchar (tối đa). Vì vậy, khi kết quả trở nên lớn hơn, kết quả bắt đầu bị cắt ngắn. –

+0

BTW, cảm ơn câu trả lời này ngay cả khi tôi không thể sử dụng nó! –

Các vấn đề liên quan