किसी समूह पर अंतिम गैर-पूर्ण मान प्रति कॉलम के सेट का चयन कैसे करें?


9

मैं SQL सर्वर 2016 का उपयोग कर रहा हूं और मेरे द्वारा उपयोग किए जा रहे डेटा का निम्न रूप है।

CREATE TABLE #tab (cat CHAR(1), t CHAR(2), val1 INT, val2 CHAR(1));

INSERT INTO #tab VALUES 
    ('A','Q1',2,NULL),('A','Q2',NULL,'P'),('A','Q3',1,NULL),('A','Q3',NULL,NULL),
    ('B','Q1',5,NULL),('B','Q2',NULL,'P'),('B','Q3',NULL,'C'),('B','Q3',10,NULL);

SELECT *
FROM    #tab;

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

मैं स्तंभों पर अंतिम गैर-शून्य मान प्राप्त करना चाहूंगा val1और इसके val2द्वारा catआदेश दिया जाएगा t। जो परिणाम मैं चाह रहा हूं वह है

cat  val1 val2
A    1    P
B    10   C

सबसे नज़दीकी मैं उपयोग कर रहा हूँ LAST_VALUE, ORDER BYजिसकी अनदेखी करते हुए मैं काम नहीं कर रहा हूँ क्योंकि मुझे अंतिम गैर-शून्य मूल्य के ऑर्डर की आवश्यकता है।

SELECT DISTINCT 
        cat, 
        LAST_VALUE(val1) OVER(PARTITION BY cat ORDER BY (SELECT NULL) ) AS val1,
        LAST_VALUE(val2) OVER(PARTITION BY cat ORDER BY (SELECT NULL) ) AS val2
FROM    #tab
cat  val1 val2
A    NULL NULL
B    10   NULL

वास्तविक तालिका में catअंतिम गैर-शून्य मान का चयन करने के लिए और अधिक कॉलम (दिनांक और स्ट्रिंग कॉलम) और अधिक वैल कॉलम (दिनांक, स्ट्रिंग और संख्या स्तंभ) हैं।

किसी भी विचार यह कैसे चयन करने के लिए।


1
@ Vrace द्वारा catआदेश दिया गया है t
एडमंड

1
@ ypercube y नहीं, कोई अनुपलब्ध Q4 मान नहीं है, tमान दोहराते हैं। यह अच्छी तरह से व्यवहार डेटा नहीं है।
एडमंड

4
सब ठीक है लेकिन उस मामले में, आपको एक आदेश देना होगा जो एक सही क्रम निर्धारित करता है। PARTITION BY cat ORDER BY t, idउदाहरण के लिए। अन्यथा, एक ही क्वेरी (कोई भी क्वेरी) आपको अलग-अलग निष्पादन पर अलग-अलग परिणाम दे सकती है। यदि तालिका में कॉलम केवल वही हैं जो आप दिखाते हैं, तो मैं यह नहीं देखता कि हमारे पास हालांकि एक निर्धारित आदेश कैसे हो सकता है!
ypercube y

1
@ ypercube y उसमें चुनौती है। डेटा में कोई आईडी कॉलम नहीं है। कई समूहीकरण स्तंभ हैं, एक स्ट्रिंग स्तंभ जो समूह आदेश देने के लिए उपयोग किया जा सकता है, और फिर नल के साथ कई मान स्तंभों को प्रतिच्छेदित किया जाता है।
एडमंड

1
यदि आप SQL सर्वर को निर्धारित रूप से नहीं बता सकते हैं कि पंक्तियों का क्रम क्या होना चाहिए, तो इस डेटा का कोई भी उपभोक्ता अंतर कैसे जान सकता है?
हारून बर्ट्रेंड

जवाबों:


10

इटज़िक बेन गण द्वारा द लास्ट नॉन नाल पज़ल से कॉन्सेप्टेशन तकनीक का उपयोग करना आपकी नमूना तालिका और स्तंभ डेटा प्रकारों के साथ इस तरह दिखाई देगा।

select T.cat,
       cast(substring(
                     max(cast(T.t as binary(2)) + cast(T.val1 as binary(4))),
                     3,
                     4
                     ) as int),
       cast(substring(
                     max(cast(T.t as binary(2)) + cast(T.val2 as binary(1))),
                     3,
                     1
                     ) as char(1))
from #tab as T
group by T.cat;

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

इस क्वेरी को लिखने का एक और तरीका है जो CTE के कदमों को बेहतर प्रदर्शन करने के लिए विभाजित करता है कि क्या चल रहा है। यह ऊपर दिए गए प्रश्न के समान सटीक निष्पादन योजना देता है।

with C1 as
(
  -- Concatenate the ordering column with the value column
  select T.cat,
        cast(T.t as binary(2)) + cast(T.val1 as binary(4)) as val1,
        cast(T.t as binary(2)) + cast(T.val2 as binary(1)) as val2
  from #tab as T
),
C2 as
(
  -- Get the max concatenated value per group
  select C1.cat,
         max(C1.val1) as val1,
         max(C1.val2) as val2
  from C1
  group by C1.cat
)
-- Extract the value from the concatenated column
select C2.cat,
       cast(substring(C2.val1, 3, 4) as int) as val1,
       cast(substring(C2.val2, 3, 1) as char(1)) as val2
from C2;

यह समाधान इस तथ्य का उपयोग करता है कि एक अशक्त मूल्य को कुछ अशक्त मान के साथ परिणामित करता है। CONCAT_NULL_YIELDS_NULL (Transact-SQL) सेट करें


बहुत अच्छी तरह से आसुत Mikael। इस समाधान ने मुझे कई बार बचाया है, हालांकि मुझे पहली बार में भ्रमित करने वाले इत्ज़िक के लेख का अंत मिला। इसमें उन्होंने इसे "चरण 2" लेबल किया था जब वास्तव में यह चरण 1 के पीछे तर्क को लागू करने की तरह था
pimbrouwers

2

बस विभाजन में NULL के लिए एक चेक जोड़ें

SELECT DISTINCT 
        cat, 
        FIRST_VALUE(val1) OVER(PARTITION BY cat ORDER BY CASE WHEN val1 is NULL then 0 else 1 END DESC, t desc) AS val1,
        FIRST_VALUE(val2) OVER(PARTITION BY cat ORDER BY CASE WHEN val2 is NULL then 0 else 1 END DESC, t desc) AS val2
FROM    #tab

0

यह करना चाहिए। row_number () और एक जॉइन

यदि आपके पास एक अच्छा प्रकार नहीं है, तो आपको आशा है कि केवल Q3 में से एक शून्य नहीं है।

declare @t TABLE (cat CHAR(1), t CHAR(2), val1 INT, val2 CHAR(1));
INSERT INTO @t VALUES 
    ('A','Q1',2,NULL),('A','Q2',NULL,'P'),('A','Q3',1,NULL),('A','Q3',NULL,NULL),
    ('B','Q1',5,NULL),('B','Q2',NULL,'P'),('B','Q3',NULL,'C'),('B','Q3',10,NULL);

--SELECT *
--     , row_number() over (partition by cat order by t) as rn
--FROM   @t
--where val1 is not null or val2 is not null;

select t1.cat, t1.val1, t2.val2 
from  ( SELECT t.cat, t.val1
             , row_number() over (partition by cat order by t desc) as rn
        FROM   @t t
        where val1 is not null 
       ) t1
join   ( SELECT t.cat, t.val2
             , row_number() over (partition by cat order by t desc) as rn
        FROM   @t t
        where val2 is not null 
       ) t2
   on t1.cat = t2.cat
  and t1.rn = 1
  and t2.rn = 1
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.