SQL सर्वर डायनेमिक PIVOT क्वेरी?


202

मुझे निम्नलिखित डेटा का अनुवाद करने के साधन के साथ आने का काम सौंपा गया है:

date        category        amount
1/1/2012    ABC             1000.00
2/1/2012    DEF             500.00
2/1/2012    GHI             800.00
2/10/2012   DEF             700.00
3/1/2012    ABC             1100.00

निम्नलिखित में:

date        ABC             DEF             GHI
1/1/2012    1000.00
2/1/2012                    500.00
2/1/2012                                    800.00
2/10/2012                   700.00
3/1/2012    1100.00

रिक्त स्थान NULLs या रिक्त हो सकते हैं, या तो ठीक है, और श्रेणियों को गतिशील होने की आवश्यकता होगी। इसके लिए एक और संभावित चेतावनी यह है कि हम क्वेरी को सीमित क्षमता में चला रहे हैं, जिसका अर्थ है कि टेम्‍प टेबल बाहर हैं। मैंने शोध करने की कोशिश की है और उस पर उतरा है, PIVOTलेकिन जैसा कि मैंने कभी भी इसका इस्तेमाल नहीं किया है, इससे पहले कि मैं इसे समझने की पूरी कोशिश करूं, वास्तव में इसे समझ नहीं पाया। क्या कोई मुझे सही दिशा दिखा सकता है?


3
SQL सर्वर का कौन सा संस्करण कृपया?
आरोन बर्ट्रेंड

जवाबों:


250

गतिशील SQL PIVOT:

create table temp
(
    date datetime,
    category varchar(3),
    amount money
)

insert into temp values ('1/1/2012', 'ABC', 1000.00)
insert into temp values ('2/1/2012', 'DEF', 500.00)
insert into temp values ('2/1/2012', 'GHI', 800.00)
insert into temp values ('2/10/2012', 'DEF', 700.00)
insert into temp values ('3/1/2012', 'ABC', 1100.00)


DECLARE @cols AS NVARCHAR(MAX),
    @query  AS NVARCHAR(MAX);

SET @cols = STUFF((SELECT distinct ',' + QUOTENAME(c.category) 
            FROM temp c
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')

set @query = 'SELECT date, ' + @cols + ' from 
            (
                select date
                    , amount
                    , category
                from temp
           ) x
            pivot 
            (
                 max(amount)
                for category in (' + @cols + ')
            ) p '


execute(@query)

drop table temp

परिणाम:

Date                        ABC         DEF    GHI
2012-01-01 00:00:00.000     1000.00     NULL    NULL
2012-02-01 00:00:00.000     NULL        500.00  800.00
2012-02-10 00:00:00.000     NULL        700.00  NULL
2012-03-01 00:00:00.000     1100.00     NULL    NULL

तो \ @ रंग स्ट्रिंग-संक्षिप्त होना चाहिए, है ना? हम sp_executesql और पैरामीटर-बाइंडिंग का उपयोग करने के लिए वहाँ में \ @cols को प्रक्षेपित नहीं कर सकते हैं? भले ही हम अपने आप ही \ _cols का निर्माण करते हैं, क्या होगा अगर किसी तरह यह दुर्भावनापूर्ण SQL निहित है। किसी भी अतिरिक्त शमन करने वाले कदम जो मैं इसे समाप्‍त करने और इसे क्रियान्वित करने से पहले उठा सकता था?
लाल मटर

आप इस पर पंक्तियों और स्तंभों को कैसे छाँटेंगे?
पैट्रिक शोम्बर्ग

@PatrickSchomburg कई तरह के तरीके हैं - अगर आप @colsतब छांटना चाहते थे तो आप इसे हटा सकते थे DISTINCTऔर इस्तेमाल कर सकते थे GROUP BYऔर ORDER BYजब आपको इसकी सूची मिल जाती थी @cols
टैरिन

मैं कोशिश करूँगा कि पंक्तियों का क्या? मैं एक तिथि का भी उपयोग कर रहा हूं, और यह क्रम में नहीं आता है।
पैट्रिक शोम्बर्ग

1
कोई बात नहीं मैं गलत जगह से आदेश डाल रहा था।
पैट्रिक शोम्बर्ग

27

डायनेमिक SQL PIVOT

कॉलम स्ट्रिंग बनाने के लिए अलग-अलग दृष्टिकोण

create table #temp
(
    date datetime,
    category varchar(3),
    amount money
)

insert into #temp values ('1/1/2012', 'ABC', 1000.00)
insert into #temp values ('2/1/2012', 'DEF', 500.00)
insert into #temp values ('2/1/2012', 'GHI', 800.00)
insert into #temp values ('2/10/2012', 'DEF', 700.00)
insert into #temp values ('3/1/2012', 'ABC', 1100.00)

DECLARE @cols  AS NVARCHAR(MAX)='';
DECLARE @query AS NVARCHAR(MAX)='';

SELECT @cols = @cols + QUOTENAME(category) + ',' FROM (select distinct category from #temp ) as tmp
select @cols = substring(@cols, 0, len(@cols)) --trim "," at end

set @query = 
'SELECT * from 
(
    select date, amount, category from #temp
) src
pivot 
(
    max(amount) for category in (' + @cols + ')
) piv'

execute(@query)
drop table #temp

परिणाम

date                    ABC     DEF     GHI
2012-01-01 00:00:00.000 1000.00 NULL    NULL
2012-02-01 00:00:00.000 NULL    500.00  800.00
2012-02-10 00:00:00.000 NULL    700.00  NULL
2012-03-01 00:00:00.000 1100.00 NULL    NULL

13

मुझे पता है कि यह सवाल पुराना है, लेकिन मैं जवाबों को देख रहा था और सोचा था कि मैं समस्या के "गतिशील" हिस्से पर विस्तार करने में सक्षम हो सकता हूं और संभवत: किसी की मदद कर सकता हूं।

सबसे पहले और सबसे पहले मैंने इस समस्या को हल करने के लिए एक सहकर्मी के एक जोड़े को अनिर्णायक और बड़े डेटा सेटों को जल्दी से समाप्त करने की आवश्यकता के साथ बनाया था।

इस समाधान के लिए एक संग्रहीत प्रक्रिया के निर्माण की आवश्यकता है ताकि यदि आपकी आवश्यकताओं के लिए यह प्रश्न बाहर हो तो कृपया अब पढ़ना बंद कर दें।

यह प्रक्रिया अलग-अलग तालिकाओं, स्तंभ नामों और समुच्चय के लिए धुरी बयानों को गतिशील रूप से बनाने के लिए एक धुरी कथन के प्रमुख चर में लेने जा रही है। स्थैतिक स्तंभ का उपयोग समूह के रूप में / धुरी के लिए पहचान कॉलम के रूप में किया जाता है (यदि आवश्यक नहीं है तो कोड से बाहर निकाला जा सकता है लेकिन धुरी के बयानों में बहुत सामान्य है और मूल मुद्दे को हल करने के लिए आवश्यक था), धुरी स्तंभ वह जगह है जहां अंतिम परिणामी स्तंभ नाम से उत्पन्न किया जाएगा, और मूल्य स्तंभ वह है जो कुल लागू किया जाएगा। तालिका पैरामीटर स्कीमा (schema.tablename) सहित तालिका का नाम है। कोड का यह भाग कुछ प्रेम का उपयोग कर सकता है क्योंकि यह उतना साफ नहीं है जितना कि मैं चाहूंगा। इसने मेरे लिए काम किया क्योंकि मेरा उपयोग सार्वजनिक रूप से सामना नहीं कर रहा था और एसक्यूएल इंजेक्शन चिंता का विषय नहीं था।

संग्रहीत कार्यविधि बनाने के लिए कोड के साथ प्रारंभ करें। यह कोड SSMS 2005 और उसके बाद के सभी संस्करणों में काम करना चाहिए लेकिन मैंने इसे 2005 या 2016 में परीक्षण नहीं किया है, लेकिन मैं यह नहीं देख सकता कि यह काम क्यों नहीं करेगा।

create PROCEDURE [dbo].[USP_DYNAMIC_PIVOT]
    (
        @STATIC_COLUMN VARCHAR(255),
        @PIVOT_COLUMN VARCHAR(255),
        @VALUE_COLUMN VARCHAR(255),
        @TABLE VARCHAR(255),
        @AGGREGATE VARCHAR(20) = null
    )

AS


BEGIN

SET NOCOUNT ON;
declare @AVAIABLE_TO_PIVOT NVARCHAR(MAX),
        @SQLSTRING NVARCHAR(MAX),
        @PIVOT_SQL_STRING NVARCHAR(MAX),
        @TEMPVARCOLUMNS NVARCHAR(MAX),
        @TABLESQL NVARCHAR(MAX)

if isnull(@AGGREGATE,'') = '' 
    begin
        SET @AGGREGATE = 'MAX'
    end


 SET @PIVOT_SQL_STRING =    'SELECT top 1 STUFF((SELECT distinct '', '' + CAST(''[''+CONVERT(VARCHAR,'+ @PIVOT_COLUMN+')+'']''  AS VARCHAR(50)) [text()]
                            FROM '+@TABLE+'
                            WHERE ISNULL('+@PIVOT_COLUMN+','''') <> ''''
                            FOR XML PATH(''''), TYPE)
                            .value(''.'',''NVARCHAR(MAX)''),1,2,'' '') as PIVOT_VALUES
                            from '+@TABLE+' ma
                            ORDER BY ' + @PIVOT_COLUMN + ''

declare @TAB AS TABLE(COL NVARCHAR(MAX) )

INSERT INTO @TAB EXEC SP_EXECUTESQL  @PIVOT_SQL_STRING, @AVAIABLE_TO_PIVOT 

SET @AVAIABLE_TO_PIVOT = (SELECT * FROM @TAB)


SET @TEMPVARCOLUMNS = (SELECT replace(@AVAIABLE_TO_PIVOT,',',' nvarchar(255) null,') + ' nvarchar(255) null')


SET @SQLSTRING = 'DECLARE @RETURN_TABLE TABLE ('+@STATIC_COLUMN+' NVARCHAR(255) NULL,'+@TEMPVARCOLUMNS+')  
                    INSERT INTO @RETURN_TABLE('+@STATIC_COLUMN+','+@AVAIABLE_TO_PIVOT+')

                    select * from (
                    SELECT ' + @STATIC_COLUMN + ' , ' + @PIVOT_COLUMN + ', ' + @VALUE_COLUMN + ' FROM '+@TABLE+' ) a

                    PIVOT
                    (
                    '+@AGGREGATE+'('+@VALUE_COLUMN+')
                    FOR '+@PIVOT_COLUMN+' IN ('+@AVAIABLE_TO_PIVOT+')
                    ) piv

                    SELECT * FROM @RETURN_TABLE'



EXEC SP_EXECUTESQL @SQLSTRING

END

आगे हम अपना डेटा उदाहरण के लिए तैयार करेंगे। मैंने समग्र परिवर्तन के विभिन्न आउटपुट दिखाने के लिए अवधारणा के इस प्रमाण में उपयोग करने के लिए डेटा तत्वों के एक जोड़े के साथ स्वीकृत उत्तर से डेटा उदाहरण लिया है।

create table temp
(
    date datetime,
    category varchar(3),
    amount money
)

insert into temp values ('1/1/2012', 'ABC', 1000.00)
insert into temp values ('1/1/2012', 'ABC', 2000.00) -- added
insert into temp values ('2/1/2012', 'DEF', 500.00)
insert into temp values ('2/1/2012', 'DEF', 1500.00) -- added
insert into temp values ('2/1/2012', 'GHI', 800.00)
insert into temp values ('2/10/2012', 'DEF', 700.00)
insert into temp values ('2/10/2012', 'DEF', 800.00) -- addded
insert into temp values ('3/1/2012', 'ABC', 1100.00)

विभिन्न उदाहरणों को सरल उदाहरण के रूप में दिखाते हुए निम्नलिखित उदाहरण विभिन्न निष्पादन विवरण दिखाते हैं। उदाहरण को सरल रखने के लिए मैंने स्थैतिक, धुरी और मान स्तंभ बदलने का विकल्प नहीं चुना। आपको इसे स्वयं से मैसेज करना शुरू करने के लिए कोड को कॉपी और पेस्ट करने में सक्षम होना चाहिए

exec [dbo].[USP_DYNAMIC_PIVOT] 'date','category','amount','dbo.temp','sum'
exec [dbo].[USP_DYNAMIC_PIVOT] 'date','category','amount','dbo.temp','max'
exec [dbo].[USP_DYNAMIC_PIVOT] 'date','category','amount','dbo.temp','avg'
exec [dbo].[USP_DYNAMIC_PIVOT] 'date','category','amount','dbo.temp','min'

यह निष्पादन क्रमशः निम्नलिखित डेटा सेट लौटाता है।

यहां छवि विवरण दर्ज करें


बहुत बढ़िया! क्या आप संग्रहीत प्रक्रिया के बजाय TVF का विकल्प बना सकते हैं। ऐसे TVF से चयन करना सुविधाजनक होगा।
प्रेज़ेमीस्लाव रिमिन

3
दुर्भाग्य से, मेरे ज्ञान का सबसे अच्छा करने के लिए नहीं, क्योंकि आप एक टीवीएफ के लिए एक गतिशील संरचना नहीं कर सकते। आपके पास TVF में स्तंभों का एक स्थिर सेट होना चाहिए।
SFrejofsky

8

धुरी स्तंभ सूची बनाने के लिए STRING_AGG फ़ंक्शन का उपयोग करते हुए SQL सर्वर 2017 के लिए अपडेट किया गया संस्करण:

create table temp
(
    date datetime,
    category varchar(3),
    amount money
);

insert into temp values ('20120101', 'ABC', 1000.00);
insert into temp values ('20120201', 'DEF', 500.00);
insert into temp values ('20120201', 'GHI', 800.00);
insert into temp values ('20120210', 'DEF', 700.00);
insert into temp values ('20120301', 'ABC', 1100.00);


DECLARE @cols AS NVARCHAR(MAX),
    @query  AS NVARCHAR(MAX);

SET @cols = (SELECT STRING_AGG(category,',') FROM (SELECT DISTINCT category FROM temp WHERE category IS NOT NULL)t);

set @query = 'SELECT date, ' + @cols + ' from 
            (
                select date
                    , amount
                    , category
                from temp
           ) x
            pivot 
            (
                 max(amount)
                for category in (' + @cols + ')
            ) p ';

execute(@query);

drop table temp;

6

आप इसे गतिशील TSQL का उपयोग करके प्राप्त कर सकते हैं (याद रखें कि SQL इंजेक्शन के हमलों से बचने के लिए QUOTENAME का उपयोग करें):

SQL सर्वर 2005 में डायनामिक कॉलम के साथ पिवोट्स

SQL सर्वर - डायनेमिक PIVOT टेबल - SQL इंजेक्शन

गतिशील एसक्यूएल के अभिशाप और आशीर्वाद के लिए अप्रचलित संदर्भ


11
QUOTENAMEयदि आप किसी उपयोगकर्ता से पैरामीटर के रूप में @tableName स्वीकार कर रहे हैं, और जैसे क्वेरी से जोड़ रहे हैं, तो FWIW केवल SQL इंजेक्शन हमलों में मदद करता है SET @sql = 'SELECT * FROM ' + @tableName;। आप बहुत सारे गतिशील डायनेमिक SQL स्ट्रिंग्स का निर्माण कर सकते हैं और QUOTENAMEआपकी सहायता के लिए एक चाटना नहीं करेंगे।
आरोन बर्ट्रेंड

2
@davids कृपया इस मेटा चर्चा का संदर्भ लें । यदि आप हाइपरलिंक निकालते हैं, तो आपका उत्तर अधूरा है।
कर्मी

@Kermit, मैं मानता हूं कि कोड दिखाना अधिक मददगार है, लेकिन क्या आप कह रहे हैं कि इसका उत्तर देने के लिए यह आवश्यक है? लिंक के बिना, मेरा उत्तर है "आप गतिशील TSQL का उपयोग करके इसे प्राप्त कर सकते हैं"। चयनित उत्तर उसी मार्ग का सुझाव देता है, जोड़े गए लाभ के साथ यदि यह भी दिखाया जाए कि यह कैसे करना है, यही कारण है कि इसे उत्तर के रूप में चुना गया था।
दाविद

2
मैंने चयनित उत्तर को चुना (इसके लिए चयनित होने से पहले) क्योंकि यह एक उदाहरण था और किसी नए की बेहतर मदद करेगा। हालांकि, मुझे लगता है कि किसी नए व्यक्ति को मेरे द्वारा दिए गए लिंक को भी पढ़ना चाहिए, यही वजह है कि मैंने उन्हें नहीं हटाया।
18

3

वहाँ मेरा समाधान अपवित्र अशक्त मूल्यों की सफाई है

DECLARE @cols AS NVARCHAR(MAX),
@maxcols AS NVARCHAR(MAX),
@query  AS NVARCHAR(MAX)

select @cols = STUFF((SELECT ',' + QUOTENAME(CodigoFormaPago) 
                from PO_FormasPago
                order by CodigoFormaPago
        FOR XML PATH(''), TYPE
        ).value('.', 'NVARCHAR(MAX)') 
    ,1,1,'')

select @maxcols = STUFF((SELECT ',MAX(' + QUOTENAME(CodigoFormaPago) + ') as ' + QUOTENAME(CodigoFormaPago)
                from PO_FormasPago
                order by CodigoFormaPago
        FOR XML PATH(''), TYPE
        ).value('.', 'NVARCHAR(MAX)')
    ,1,1,'')

set @query = 'SELECT CodigoProducto, DenominacionProducto, ' + @maxcols + '
            FROM
            (
                SELECT 
                CodigoProducto, DenominacionProducto,
                ' + @cols + ' from 
                 (
                    SELECT 
                        p.CodigoProducto as CodigoProducto,
                        p.DenominacionProducto as DenominacionProducto,
                        fpp.CantidadCuotas as CantidadCuotas,
                        fpp.IdFormaPago as IdFormaPago,
                        fp.CodigoFormaPago as CodigoFormaPago
                    FROM
                        PR_Producto p
                        LEFT JOIN PR_FormasPagoProducto fpp
                            ON fpp.IdProducto = p.IdProducto
                        LEFT JOIN PO_FormasPago fp
                            ON fpp.IdFormaPago = fp.IdFormaPago
                ) xp
                pivot 
                (
                    MAX(CantidadCuotas)
                    for CodigoFormaPago in (' + @cols + ')
                ) p 
            )  xx 
            GROUP BY CodigoProducto, DenominacionProducto'

t @query;

execute(@query);

2

नीचे दिया गया कोड उन परिणामों को प्रदान करता है जो आउटपुट में NULL को शून्य में बदल देता है

तालिका निर्माण और डेटा प्रविष्टि:

create table test_table
 (
 date nvarchar(10),
 category char(3),
 amount money
 )

 insert into test_table values ('1/1/2012','ABC',1000.00)
 insert into test_table values ('2/1/2012','DEF',500.00)
 insert into test_table values ('2/1/2012','GHI',800.00)
 insert into test_table values ('2/10/2012','DEF',700.00)
 insert into test_table values ('3/1/2012','ABC',1100.00)

सटीक परिणाम उत्पन्न करने के लिए क्वेरी जो शून्य को शून्य से प्रतिस्थापित करती है:

DECLARE @DynamicPivotQuery AS NVARCHAR(MAX),
@PivotColumnNames AS NVARCHAR(MAX),
@PivotSelectColumnNames AS NVARCHAR(MAX)

--Get distinct values of the PIVOT Column
SELECT @PivotColumnNames= ISNULL(@PivotColumnNames + ',','')
+ QUOTENAME(category)
FROM (SELECT DISTINCT category FROM test_table) AS cat

--Get distinct values of the PIVOT Column with isnull
SELECT @PivotSelectColumnNames 
= ISNULL(@PivotSelectColumnNames + ',','')
+ 'ISNULL(' + QUOTENAME(category) + ', 0) AS '
+ QUOTENAME(category)
FROM (SELECT DISTINCT category FROM test_table) AS cat

--Prepare the PIVOT query using the dynamic 
SET @DynamicPivotQuery = 
N'SELECT date, ' + @PivotSelectColumnNames + '
FROM test_table
pivot(sum(amount) for category in (' + @PivotColumnNames + ')) as pvt';

--Execute the Dynamic Pivot Query
EXEC sp_executesql @DynamicPivotQuery

OUTPUT:

यहां छवि विवरण दर्ज करें

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.