SQL सर्वर: पंक्तियों तक कॉलम


129

स्तंभों को पंक्तियों में बदलने के लिए सुरुचिपूर्ण (या किसी भी) समाधान की तलाश में।

यहाँ एक उदाहरण है: मेरे पास निम्नलिखित स्कीमा के साथ एक तालिका है:

[ID] [EntityID] [Indicator1] [Indicator2] [Indicator3] ... [Indicator150]

यहाँ मैं परिणाम के रूप में प्राप्त करना चाहता हूं:

[ID] [EntityId] [IndicatorName] [IndicatorValue]

और परिणाम मान इस प्रकार होंगे:

1 1 'Indicator1' 'Value of Indicator 1 for entity 1'
2 1 'Indicator2' 'Value of Indicator 2 for entity 1'
3 1 'Indicator3' 'Value of Indicator 3 for entity 1'
4 2 'Indicator1' 'Value of Indicator 1 for entity 2'

और इसी तरह..

इसका कोई मतलब भी है क्या? क्या आपके पास कोई सुझाव है कि कहाँ देखना है और कैसे इसे टी-एसक्यूएल में लाना है?


2
क्या आपने अभी तक पिवट / अनपिवेट में देखा है?
जोश जे

इसके अंत में ब्लूफेट के समाधान के साथ चला गया। सुरुचिपूर्ण और कार्यात्मक। सभी को बहुत बहुत धन्यवाद।
सर्गेई

जवाबों:


248

आप स्तंभों को पंक्तियों में बदलने के लिए UNPIVOT फ़ंक्शन का उपयोग कर सकते हैं :

select id, entityId,
  indicatorname,
  indicatorvalue
from yourtable
unpivot
(
  indicatorvalue
  for indicatorname in (Indicator1, Indicator2, Indicator3)
) unpiv;

ध्यान दें, आपके द्वारा अप्रकाशित किए जा रहे स्तंभों का डेटाटाइप समान होना चाहिए, ताकि आपको अप्रत्यक्ष आवेदन करने से पहले डेटाटिप्स को बदलना पड़े।

आप CROSS APPLYकॉलम बदलने के लिए UNION ALL के साथ भी उपयोग कर सकते हैं:

select id, entityid,
  indicatorname,
  indicatorvalue
from yourtable
cross apply
(
  select 'Indicator1', Indicator1 union all
  select 'Indicator2', Indicator2 union all
  select 'Indicator3', Indicator3 union all
  select 'Indicator4', Indicator4 
) c (indicatorname, indicatorvalue);

SQL सर्वर के आपके संस्करण के आधार पर आप VALPSES खंड के साथ CROSS APPLY का भी उपयोग कर सकते हैं:

select id, entityid,
  indicatorname,
  indicatorvalue
from yourtable
cross apply
(
  values
  ('Indicator1', Indicator1),
  ('Indicator2', Indicator2),
  ('Indicator3', Indicator3),
  ('Indicator4', Indicator4)
) c (indicatorname, indicatorvalue);

अंत में, यदि आपके पास 150 स्तंभ हैं, तो आप बिना किसी क्वेरी के हार्ड-कोड करना चाहते हैं, तो आप डायनेमिक SQL का उपयोग करके sql स्टेटमेंट जेनरेट कर सकते हैं:

DECLARE @colsUnpivot AS NVARCHAR(MAX),
   @query  AS NVARCHAR(MAX)

select @colsUnpivot 
  = stuff((select ','+quotename(C.column_name)
           from information_schema.columns as C
           where C.table_name = 'yourtable' and
                 C.column_name like 'Indicator%'
           for xml path('')), 1, 1, '')

set @query 
  = 'select id, entityId,
        indicatorname,
        indicatorvalue
     from yourtable
     unpivot
     (
        indicatorvalue
        for indicatorname in ('+ @colsunpivot +')
     ) u'

exec sp_executesql @query;

4
उन लोगों के लिए जो अधिक पागल और बोल्ट के बारे में UNPIVOTऔर / बनाम चाहते हैं। APPLY, ब्रैड शुल्ज़ (और फॉलो-ऑन ) का यह 2010 ब्लॉग पोस्ट सुंदर है।
रफिन

2
Msg 8167, स्तर 16, राज्य 1, रेखा 147 स्तंभ का प्रकार "ब्लाब्ला" UNPIVOT सूची में निर्दिष्ट अन्य स्तंभों के प्रकार के साथ संघर्ष करता है।
JDPeckham

@JDPeckham यदि आपके पास अलग-अलग डेटाटाइप्स हैं, तो आपको उन्हें एक ही प्रकार और लंबाई में परिवर्तित करने की आवश्यकता है, जो कि अनप्राइवेट करने से पहले है। यहाँ इसके बारे में अधिक जानकारी है
टैरिन

xml विधि में दोष है क्योंकि यह xml कोड जैसे & gt; को & lt; और & amp; प्लस प्रदर्शन को फिर से लिखकर बेहतर बनाया जा सकता है: @colsUnpivot = सामान (चयन करें), '+ quotename (C.column_name) को [text ()] के रूप में information_schema.columns से C के रूप में चुनें जहाँ C.table_name =' yourtable ' और C.column_name जैसे xml पाथ ('') के लिए 'इंडिकेटर%' टाइप करें, .value ('टेक्स्ट] ([1]', 'nvarchar (
मैक्सिमम

24

यदि आपके पास 150 कॉलम हैं तो मुझे लगता है कि UNPIVOT कोई विकल्प नहीं है। तो आप xml ट्रिक का उपयोग कर सकते हैं

;with CTE1 as (
    select ID, EntityID, (select t.* for xml raw('row'), type) as Data
    from temp1 as t
), CTE2 as (
    select
         C.id, C.EntityID,
         F.C.value('local-name(.)', 'nvarchar(128)') as IndicatorName,
         F.C.value('.', 'nvarchar(max)') as IndicatorValue
    from CTE1 as c
        outer apply c.Data.nodes('row/@*') as F(C)
)
select * from CTE2 where IndicatorName like 'Indicator%'

sql fiddle demo

आप डायनेमिक SQL भी लिख सकते हैं, लेकिन मुझे xml अधिक पसंद है - डायनेमिक SQL के लिए आपके पास टेबल से सीधे डेटा का चयन करने की अनुमति है और यह हमेशा एक विकल्प नहीं है।

अद्यतन
टिप्पणियों में एक बड़ी लौ के रूप में, मुझे लगता है कि मैं xml / गतिशील SQL के कुछ पेशेवरों और विपक्ष को जोड़ दूंगा। मैं उद्देश्य के रूप में के रूप में मैं कर सकता हूँ और सुरुचिपूर्णता और कुरूपता का उल्लेख नहीं करने की कोशिश करता हूँ। यदि आपके पास कोई अन्य पेशेवरों और विपक्ष हैं, तो उत्तर को संपादित करें या टिप्पणियों में लिखें

विपक्ष

  • यह डायनेमिक एसक्यूएल जितना तेज़ नहीं है , मोटे परीक्षणों ने मुझे बताया कि xml लगभग 2.5 गुना धीमा है जो डायनेमिक है (यह ~ 250000 पंक्तियों की तालिका पर एक क्वेरी थी, इसलिए यह अनुमान सही नहीं है)। यदि आप चाहें, तो आप इसकी तुलना स्वयं कर सकते हैं, यहाँ का चक्रवर्ती उदाहरण, 100000 पंक्तियों पर यह 29s (xml) बनाम 14s (डायनामिक) था;
  • हो सकता है कि xpath से परिचित लोगों के लिए समझना कठिन हो ;

पेशेवरों

  • यह आपके अन्य प्रश्नों के समान ही है , और यह बहुत काम आ सकता है। कुछ उदाहरण दिमाग में आते हैं
    • आप अपने ट्रिगर के अंदर क्वेरी कर सकते हैं insertedऔर (सभी पर गतिशील के साथ संभव नहीं);deleted
    • उपयोगकर्ता के पास टेबल से सीधे चयन की अनुमति नहीं है । मेरे कहने का मतलब यह है कि यदि आपके पास प्रक्रिया परतें संग्रहीत हैं और उपयोगकर्ता के पास sp चलाने की अनुमति है, लेकिन सीधे क्वेरी टेबल की अनुमति नहीं है, तो आप अभी भी संग्रहीत कार्यविधि के अंदर इस क्वेरी का उपयोग कर सकते हैं;
    • आप अपने दायरे में पॉप्युलेट टेबल चर को क्वेरी कर सकते हैं (डायनेमिक SQL के अंदर इसे पास करने के लिए या तो आपको इसके बजाय इसे अस्थायी टेबल बनाना होगा या टाइप बनाना होगा और डायनामिक SQL में एक पैरामीटर के रूप में पास करना होगा;
  • आप इस क्वेरी को फंक्शन (स्केलर या टेबल-वैल्यू) के अंदर कर सकते हैं । फ़ंक्शन के अंदर गतिशील एसक्यूएल का उपयोग करना संभव नहीं है;

2
आप XML से कौन सा डेटा चुन रहे हैं, जिसे तालिका से डेटा का चयन करने की आवश्यकता नहीं है?
आरोन बर्ट्रेंड

1
उदाहरण के लिए, आप उपयोगकर्ताओं को तालिकाओं से डेटा का चयन करने की अनुमति नहीं देने का निर्णय ले सकते हैं, लेकिन केवल तालिकाओं के साथ काम करने वाली संग्रहीत प्रक्रियाओं पर, इसलिए मैं प्रक्रिया के अंदर xml के लिए चयन कर सकता हूं, लेकिन अगर मुझे गतिशील एसक्यूएल का उपयोग करना है तो मुझे कुछ वर्कअराउंड का उपयोग करना होगा
रोमन पाकर

3
यदि आप चाहते हैं कि आपके उपयोगकर्ता कोड को निष्पादित करने में सक्षम हों, तो आपको उन्हें कोड को निष्पादित करने के लिए जिस भी एक्सेस की आवश्यकता होगी, उन्हें देना होगा। उन आवश्यकताओं को पूरा न करें जो आपके उत्तर को बेहतर बनाने के लिए मौजूद नहीं हैं (आपको अपने उत्तर को देखने के लिए प्रतिस्पर्धी उत्तरों पर टिप्पणी करने की आवश्यकता नहीं है - यदि उन्हें वह उत्तर मिला, तो वे आपका भी पता लगा सकते हैं)।
आरोन बर्ट्रेंड

2
यदि XML का उपयोग करने का आपका औचित्य यह है कि आप इसे संग्रहीत कार्यविधि में रख सकते हैं, तो टेबल पर सीधी पहुँच देने से बचने के लिए, हो सकता है कि आपका उदाहरण यह दिखाए कि इसे एक संग्रहीत प्रक्रिया में कैसे रखा जाए और किसी उपयोगकर्ता को अधिकार कैसे प्रदान किए जाएँ ताकि वे अंतर्निहित तालिका तक पढ़ने की पहुंच के बिना इसे निष्पादित कर सकते हैं। मेरे लिए यह गुंजाइश है, क्योंकि एक टेबल के खिलाफ क्वेरी लिखने वाले ज्यादातर लोगों ने टेबल पर पहुंच पढ़ी है।
आरोन बर्ट्रेंड

2
मैं कहूंगा कि अवधि में 10x का अंतर मायने रखता है, हां। और ~ 8,000 पंक्तियाँ "डेटा की बड़ी मात्रा" नहीं है - क्या हमें देखना चाहिए कि 800,000 पंक्तियों के खिलाफ क्या होता है?
हारून बर्ट्रेंड

7

नए पाठकों की मदद करने के लिए, मैंने UNPIVOT के बारे में @ Bluefeet के बेहतर उत्तर को समझने के लिए एक उदाहरण बनाया है।

 SELECT id
        ,entityId
        ,indicatorname
        ,indicatorvalue
  FROM (VALUES
        (1, 1, 'Value of Indicator 1 for entity 1', 'Value of Indicator 2 for entity 1', 'Value of Indicator 3 for entity 1'),
        (2, 1, 'Value of Indicator 1 for entity 2', 'Value of Indicator 2 for entity 2', 'Value of Indicator 3 for entity 2'),
        (3, 1, 'Value of Indicator 1 for entity 3', 'Value of Indicator 2 for entity 3', 'Value of Indicator 3 for entity 3'),
        (4, 2, 'Value of Indicator 1 for entity 4', 'Value of Indicator 2 for entity 4', 'Value of Indicator 3 for entity 4')
       ) AS Category(ID, EntityId, Indicator1, Indicator2, Indicator3)
UNPIVOT
(
    indicatorvalue
    FOR indicatorname IN (Indicator1, Indicator2, Indicator3)
) UNPIV;

3

मुझे Microsoft SQL Server में कॉलम्स को पंक्तियों में बदलने के लिए एक समाधान की आवश्यकता थी, बिना कोलम नाम (ट्रिगर में उपयोग किए गए) और डायनेमिक एसक्यूएल के बिना (डायनेमिक एसक्यूएल ट्रिगर में उपयोग के लिए बहुत धीमा है)।

मुझे आखिरकार यह समाधान मिल गया, जो ठीक काम करता है:

SELECT
    insRowTbl.PK,
    insRowTbl.Username,
    attr.insRow.value('local-name(.)', 'nvarchar(128)') as FieldName,
    attr.insRow.value('.', 'nvarchar(max)') as FieldValue 
FROM ( Select      
          i.ID as PK,
          i.LastModifiedBy as Username,
          convert(xml, (select i.* for xml raw)) as insRowCol
       FROM inserted as i
     ) as insRowTbl
CROSS APPLY insRowTbl.insRowCol.nodes('/row/@*') as attr(insRow)

जैसा कि आप देख सकते हैं, मैं पंक्ति को XML (सबक्लेरी सेलेक्ट i, * में xml कच्चे के लिए परिवर्तित करता हूं, यह सभी स्तंभों को एक xml कॉलम में परिवर्तित करता है)

फिर मैं इस कॉलम के प्रत्येक XML विशेषता के लिए एक फ़ंक्शन का उपयोग करता हूं, ताकि मुझे प्रति पंक्ति एक पंक्ति मिल जाए।

कुल मिलाकर, यह कॉलम को नामों के बिना और डायनामिक एसक्यूएल का उपयोग किए बिना कॉलम को पंक्तियों में परिवर्तित करता है। यह मेरे उद्देश्य के लिए काफी तेज है।

(संपादित करें: मैंने अभी ऊपर रोमन पाइकर का उत्तर देखा, जो ऐसा ही कर रहा है। मैंने पहले कर्सर के साथ डायनामिक एसक्यूएल ट्रिगर का उपयोग किया, जो इस समाधान की तुलना में 10 से 100 गुना धीमा था, लेकिन शायद यह कर्सर के कारण था, न कि कर्सर के कारण गतिशील एसक्यूएल। वैसे भी, यह समाधान एक सार्वभौमिक बहुत सरल है, इसलिए इसका निश्चित रूप से एक विकल्प है)।

मैं इस टिप्पणी को इस जगह पर छोड़ रहा हूं, क्योंकि मैं अपनी पोस्ट में इस स्पष्टीकरण को पूर्ण ऑडिट ट्रिगर के बारे में बताना चाहता हूं, जिसे आप यहां पा सकते हैं: https://stackoverflow.com/a/43800286/4160788


3
DECLARE @TableName varchar(max)=NULL
SELECT @TableName=COALESCE(@TableName+',','')+t.TABLE_CATALOG+'.'+ t.TABLE_SCHEMA+'.'+o.Name
  FROM sysindexes AS i
  INNER JOIN sysobjects AS o ON i.id = o.id
  INNER JOIN INFORMATION_SCHEMA.TABLES T ON T.TABLE_NAME=o.name
 WHERE i.indid < 2
  AND OBJECTPROPERTY(o.id,'IsMSShipped') = 0
  AND i.rowcnt >350
  AND o.xtype !='TF'
 ORDER BY o.name ASC

 print @tablename

आप उन तालिकाओं की सूची प्राप्त कर सकते हैं जिनमें पंक्तिबद्ध> 350 हैं। आप पंक्ति के रूप में तालिका की समाधान सूची में देख सकते हैं।


2

सिर्फ इसलिए कि मैंने इसका उल्लेख नहीं देखा।

यदि 2016+, यहाँ वास्तव में डायनेमिक SQL का उपयोग किए बिना डायनेमिक रूप से अनप्राइवेट डेटा का एक और विकल्प है।

उदाहरण

Declare @YourTable Table ([ID] varchar(50),[Col1] varchar(50),[Col2] varchar(50))
Insert Into @YourTable Values 
 (1,'A','B')
,(2,'R','C')
,(3,'X','D')

Select A.[ID]
      ,Item  = B.[Key]
      ,Value = B.[Value]
 From  @YourTable A
 Cross Apply ( Select * 
                From  OpenJson((Select A.* For JSON Path,Without_Array_Wrapper )) 
                Where [Key] not in ('ID','Other','Columns','ToExclude')
             ) B

रिटर्न

ID  Item    Value
1   Col1    A
1   Col2    B
2   Col1    R
2   Col2    C
3   Col1    X
3   Col2    D
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.