2013-02-13 14 views
8

पर मनमानी तालिका को रूपांतरित करने के लिए SQL सर्वर क्वेरी को बेहतर बनाने के लिए वेब पर मौजूद एक्सएमएल और .nodes() कमांड का उपयोग करके परिणाम सेट को परिवर्तित करने के लिए बहुत ही उत्कृष्ट तकनीकों को एक साथ खोज और पाई जाने के बाद, मैं बनाने में सक्षम था यह एकल क्वेरी (संग्रहीत प्रक्रिया नहीं है) जो किसी भी मनमानी SQL क्वेरी को JSON सरणी में परिवर्तित करने का एक उचित काम है।जेएसओएन

क्वेरी प्रत्येक डेटा पंक्ति को एक अग्रणी कॉमा के साथ एक JSON ऑब्जेक्ट के रूप में एन्कोड करेगा। डेटा पंक्तियां ब्रैकेट द्वारा लिपटे हैं और पूरे परिणाम सेट को तब फ़ाइल में निर्यात करने की उम्मीद है।

मैं देखना चाहता हूं कि कोई भी इसके प्रदर्शन को बेहतर बनाने के तरीके देख सकता है?

यहां नमूने तालिका के साथ क्वेरी है:

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 ']' 

इसके बारे में मेरे सबसे बड़ी आलोचना यह है कि यह पागलपन की हद तक धीमी है है।
वर्तमान में ~ 42,000 पंक्तियों के लिए लगभग 3:30 लगते हैं।

मेरी दूसरी बड़ी आलोचना यह है कि वर्तमान में यह मानता है कि संख्या की तरह दिखने वाली हर चीज एक संख्या है। यह कॉलम प्रकार को कम से कम खोजने की कोशिश नहीं करता है (और मुझे यह भी यकीन नहीं है कि यह कर सकता है)।

अंतिम मामूली आलोचना यह है कि पहली डेटा पंक्ति में कॉमा ऊपर और तकनीकी रूप से नहीं होना चाहिए। इसके लिए क्षतिपूर्ति करने के लिए जेएसओएन सरणी शुरू करने वाली पहली पंक्ति में खाली JSON ऑब्जेक्ट की आवश्यकता होती है।

अन्य आलोचकों (अधिमानतः समाधान के साथ) आमंत्रित किया गया है, मेरे पास एकमात्र वास्तविक सीमा है कि समाधान कॉलम नामों को स्पष्ट रूप से पहचानने के बिना कई मनमानी SQL क्वेरी पर समाधान को दोहराया जा सकता है।

मैं एसक्यूएल सर्वर का उपयोग कर रहा 2012

धन्यवाद और मेरे जैसे किसी और को जो के लिए एक सामान्यीकृत एसक्यूएल परिणाम देख रहा था करने के लिए -> JSON सारणी कनवर्टर, आनंद!

+0

जबकि मैं अपने एसक्यूएल फू सराहना, मैं पूछने के लिए है? असली दुनिया में परिदृश्य क्या है जहां आपको ऐसा करने की ज़रूरत है? मैं यहाँ नकारात्मक होने की कोशिश नहीं कर रहा हूं, सिर्फ इतना परेशान है कि आपको इसकी आवश्यकता क्यों है। –

+0

इस मामले में मैं बहुत सारे बुनियादी ढांचे का निर्माण किए बिना या मेरे उत्पादन एसक्यूएल वातावरण में कुछ भी जोड़ने के बिना कोच डीबी जैसे नोएसक्यूएल डेटाबेस में परिणाम सेट को तेज़ी से लोड करने के लिए एक विज्ञापन-प्रसार तरीका ढूंढ रहा हूं। मोंगो, सोफे, एट अल। डेटा ट्रांसपोर्ट के लिए जेएसओएन को उनके लिंगुआ फ्रैंका के रूप में उपयोग करने लगते हैं। एक बार नोएसक्यूएल डीबी में हम डेटासेट को स्लाइसिंग और डाइसिंग के साथ प्रयोग कर सकते हैं ताकि वे यह देख सकें कि वे कैसा प्रदर्शन करते हैं। रिमोट डेस्कटॉप, लैपटॉप, स्मार्टफोन इत्यादि पर स्थानीय भंडार बनाने के लिए कॉच डीबी डेटाबेस को दोहराना एक एसक्यूएल प्रतिकृति आधारभूत संरचना के प्रबंधन से आसान है। तो हम कुछ उचित परिश्रम कर रहे हैं। –

+0

और चलिए यहां बहुत बेवकूफ नहीं बनते हैं, जिनमें से अधिकांश मौजूदा एसक्यूएल के लिए JSON और SQL को कुंजी/मूल्य जोड़ी उत्तरों के लिए खोज कर पाया जाता है।हां, मैंने इसे एक साथ रखा है, यूनियन ऑल के माध्यम से इसके चारों ओर कुछ ब्रैकेट रखे हैं, उद्धरणों को थोड़ा अधिक समझने के लिए एक केस स्टेटमेंट का इस्तेमाल किया, और XSINIL चीज़ के साथ प्रयास करना शुरू किया (और शायद मैंने यह भी सीखा कि यह वास्तव में कैसे काम कर रहा है;)) लेकिन मैं वास्तव में इसके लिए क्रेडिट नहीं ले सकता। एकमात्र चीज के बारे में मैंने जो कुछ किया था, उसके बारे में ऊपरी क्वेरी को कुंजी/मूल्य जोड़े में बदलने से पहले प्रत्येक पंक्ति को एक्सएमएल डेटासेट के रूप में वापस करने के लिए नीचे की उप क्वेरी को गठबंधन किया गया था। –

उत्तर

11

मैं कहता हूं कि यदि आप वास्तव में प्रदर्शन करना चाहते हैं, तो मेटाप्रोग्रामिंग का उपयोग करें। नीचे दिया गया उदाहरण 40,000 पंक्तियों के साथ प्रयास करता है और परिणाम एक सेकंड से भी कम समय में होता है (प्रारंभिक 40k पंक्तियों को सम्मिलित करने की गणना नहीं करता है, जिसमें इस उदाहरण में केवल 2 सेकंड लगते हैं)। यह आपके डेटा प्रकारों को उद्धरणों में संख्याओं को संलग्न करने के लिए भी ध्यान में रखता है।

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

अच्छा। मुझे पता था कि मुझे उस पर सोना नहीं चाहिए था - यह वह दृष्टिकोण था जिसे मैं सुझाव देने वाला था। वास्तव में बहुत उपयोगी हो सकता है ... –

+0

अच्छी तरह से अच्छा! मेरी वास्तविक तालिका पर बस 6 सेकंड। हालांकि एक अन्य दुष्प्रभाव दिनांक 8 फरवरी 2013 12:00 बजे के रूप में बाहर आ रहे हैं। मुझे –

+0

ठीक है, मैंने एक "is_date" बिट फ़ील्ड जोड़ने के लिए मेरे पक्ष में समाधान बढ़ाया है, क्या यह स्वीकार करने से पहले मेरे संशोधनों के साथ मूल उत्तर समायोजित करना उचित है? और मुझे "मूल्यों" में शामिल होने के बारे में दिखाने के लिए धन्यवाद, मैंने पहले कभी उस तकनीक को नहीं देखा है। यह बहुत मजेदार है! –

1

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

इस दृष्टिकोण के साथ समस्या/सीमा यह है कि परिणाम @ जेएसओएन वर्कर (अधिकतम) स्ट्रिंग सीमा तक ही सीमित हैं। तो जब परिणाम बड़े हो जाते हैं, तो परिणाम कम हो जाते हैं। –

+0

बीटीडब्ल्यू, इस उत्तर के लिए भी धन्यवाद, भले ही मैं इसका उपयोग नहीं कर पा रहा था! –

संबंधित मुद्दे