2015-12-10 9 views
6

में दिनांकों का समूह चुनना मेरे पास एक इतिहास तालिका है जो किसी निश्चित ऑब्जेक्ट के अपडेट को कैप्चर करती है और, अन्य जानकारी के अतिरिक्त, यह अद्यतन उस समय को कैप्चर करता है। मैं क्या करना चाहता हूं SELECTMIN(LogDate) एक निश्चित ActionTaken कॉलम से संबंधित है।SQL सर्वर

अधिक विशेष रूप से, इतिहास तालिका अन्य (नवीनतम) पंक्तियों जहां ActionTaken = 1 हो सकता है, लेकिन मैं तारीख ActionTakenबन 1.

उदाहरण कैप्चर करना चाहते हैं:

SELECT MIN(LogDate) AS FirstActionDate 
FROM HistoryTable 
WHERE ID = 123 
    AND FirstActionTaken = 1 

SELECT MIN(LogDate) AS SecondActionDate 
FROM HistoryTable 
WHERE ID = 123 
    AND SecondActionTaken = 1 

SELECT MIN(LogDate) AS ThirdActionDate 
FROM HistoryTable 
WHERE ID = 123 
    AND ThirdActionTaken = 1 

यह अच्छी तरह से काम करता है , और मुझे बिना किसी मुद्दे के उचित तिथियां मिलती हैं। मैं कहां से मुसीबत में चल रहा हूँ तो इस समूह से MAX(LogDate)select जा रहा है:

SELECT MAX(LogDate) AS LastActionDate 
FROM HistoryTable 
WHERE ID = 123 
    AND LogDate IN 
    (
      ( SELECT MIN(LogDate) AS FirstActionDate 
       FROM HistoryTable 
       WHERE ID = 123 
        AND FirstActionTaken = 1 ), 

      ( SELECT MIN(LogDate) AS SecondActionDate 
       FROM HistoryTable 
       WHERE ID = 123 
        AND SecondActionTaken = 1 ), 

      ( SELECT MIN(LogDate) AS ThirdActionDate 
       FROM HistoryTable 
       WHERE ID = 123 
        AND ThirdActionTaken = 1 ) 
    ) 

यह भी काम करता है, लेकिन मैं इसे इस तरह से कर रही नफरत है। मैं पिछले बयान को चर में सहेज सकता हूं और उनसे केवल SELECT MAX(); यह निश्चित रूप से अधिक पठनीय होगा, लेकिन JOIN वाक्यविन्यास इस क्वेरी के लिए कैसा दिखता है?

क्या पहले तीन SELECT कथनों को एक साथ जोड़ने का कोई तरीका है जो सभी तीन तिथियों को लौटाता है और एक अपठनीय गड़बड़ नहीं है?

मैं कैसे प्राप्त कर सकते हैं सबसे हाल ही में LogDate इस परिणाम की स्थापना की और (उचित रूप में अनावश्यक) के बिना SELECT बयान दोहरा से (एक अलग कॉलम के रूप में)?

संपादित करें:

  • Data Normalization
  • Using OUTER/CROSS APPLY
  • UNPIVOT (और दूसरों:

    यहाँ कुछ लिंक मैं जवाब है कि अब तक दिया गया है के संबंध में प्राप्त होते हैं)

हो दृढ़ता से यह दूसरों के साथ समान समस्याओं के समाधान की तलाश में मदद करेगा!

+0

यूनियन का उपयोग करें और फिर आप – JamieD77

उत्तर

2

संपादित 2

नई जानकारी के आधार पर है कि ओ पी के अपने जवाब से बटोरा जा सकता (कैसे नवीनतम एक्शन तारीख को परिभाषित करने के बारे में), क्वेरी कर सकते हैं

select coalesce(
     min(case when ThirdActionTaken = 1 then LogDate end), 
     min(case when SecondActionTaken = 1 then LogDate end), 
     min(case when FirstActionTaken = 1 then LogDate end) 
     ) as LastActionDate 
    from HistoryTable 
where id = 123 

Unpivot भी इस्तेमाल किया जा सकता:

आगे बस इस के लिए सरल किया जा 210
select max(ActionDate) 
    from (select min(case when FirstActionTaken = 1 then LogDate end) as FirstActionDate, 
       min(case when SecondActionTaken = 1 then LogDate end) as SecondActionDate, 
       min(case when ThirdActionTaken = 1 then LogDate end) as ThirdActionDate 
      from HistoryTable 
      where id = 123) t 
unpivot (ActionDate for ActionDates in (FirstActionDate, SecondActionDate, ThirdActionDate)) unpvt 

संपादित करें: छोटी स्पष्टीकरण

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

तो क्वेरी के निम्न भाग:

select min(case when FirstActionTaken = 1 then LogDate end) as FirstActionDate, 
     min(case when SecondActionTaken = 1 then LogDate end) as SecondActionDate, 
     min(case when ThirdActionTaken = 1 then LogDate end) as ThirdActionDate 
    from HistoryTable 
where id = 123 

... की तरह कुछ वापस कर सकती है ...

FirstActionDate SecondActionDate ThirdActionDate 
--------------- ---------------- --------------- 
    2015-01-01   2015-12-01   (null) 

फिर, unpivot खंड क्या "unpivots" 3 में कॉलम है परिणाम 3 पंक्तियों के साथ सेट किया गया है लेकिन इसके बजाय एक कॉलम:

ActionDate 
---------- 
2015-01-01 
2015-12-01 
    (null) 

परिणाम इस प्रारूप में हैं, फिर 3 पंक्तियों का अधिकतम मूल्य प्राप्त करने के लिए एक साधारण max कुल फ़ंक्शन (select max(ActionDate)) लागू किया जा सकता है।

+0

का उपयोग कर सकते हैं यह बहुत अच्छा काम करता है, और इसमें अपने स्वयं के प्रयास से बहुत अच्छा लगता है :) क्या आप यहां क्या हो रहा है इसके बारे में कुछ स्पष्टीकरण जोड़ना चाहते हैं? विशेष रूप से 'UNPIVOT' के संबंध में। जवाब के लिए धन्यवाद! – levelonehuman

+0

उत्कृष्ट, उत्तर पर अपडेट के लिए धन्यवाद - और इसके लिए शुरू करने के लिए निर्दिष्ट नहीं करने के लिए माफ़ी! – levelonehuman

1

आप अपने आईएन स्टेटमेंट के लिए 3 प्रश्नों में शामिल होने के लिए यूनियन का उपयोग कर सकते हैं।

कुछ

तरह
SELECT 
    MAX(ht1.LogDate) AS LastActionDate 
FROM 
    HistoryTable ht1 
WHERE 
    ht1.ID = 123 
    AND ht1.LogDate IN (SELECT 
         MIN(LogDate) AS FirstActionDate 
        FROM 
         HistoryTable ht2 
        WHERE 
         ht2.ID = ht1.ID 
         AND ht2.FirstActionTaken = 1 
        UNION 
        SELECT 
         MIN(LogDate) AS FirstActionDate 
        FROM 
         HistoryTable ht2 
        WHERE 
         ht2.ID = ht1.ID 
         AND ht2.SecondActionTaken = 1 
        UNION 
        SELECT 
         MIN(LogDate) AS FirstActionDate 
        FROM 
         HistoryTable ht2 
        WHERE 
         ht2.ID = ht1.ID 
         AND ht2.ThirdActionTaken = 1) 
3

यह एक सामान्यीकृत डेटा संरचना के साथ आसान होगा। यहां एक विधि है जो तीन न्यूनतम तिथियों की गणना करने के लिए सशर्त एकत्रीकरण का उपयोग करती है।तो यह उन मूल्यों की अधिकतम लेता है:

SELECT v.dt 
FROM (SELECT MIN(CASE WHEN FirstActionTaken = 1 THEN LogDate END) AS d1, 
      MIN(CASE WHEN SecondActionTaken = 1 THEN LogDate END) AS d2, 
      MIN(CASE WHEN ThirdActionTaken = 1 THEN LogDate END) AS d3  
    FROM HistoryTable 
    WHERE ID = 123 
    ) ht OUTER APPLY 
    (SELECT MAX(dt) as dt 
    FROM (VALUES (d1), (d2), (d3)) v(dt) 
    ) v; 
0

आप PIVOT का उपयोग किये बिना इस समस्या को हल कर सकते हैं। निम्नलिखित कोड चर में मिन मान संग्रहीत करने के लिए अपने प्रारंभिक कोड प्रदान करता है और उसके बाद उन के बीच में अधिकतम मूल्य की गणना करता है: करने के लिए बस एक ही बयान

DECLARE @FirstActionDate DATETIME = NULL; 
DECLARE @SecondActionDate DATETIME = NULL; 
DECLARE @ThirdActionDate DATETIME = NULL; 
DECLARE @LastActionDate DATETIME = NULL; 

SELECT @FirstActionDate = MIN(LogDate) 
FROM HistoryTable 
WHERE ID = 123 
    AND FirstActionTaken = 1 

SELECT @SecondActionDate = MIN(LogDate) 
FROM HistoryTable 
WHERE ID = 123 
    AND SecondActionTaken = 1 

SELECT @ThirdActionDate = MIN(LogDate) 
FROM HistoryTable 
WHERE ID = 123 
    AND ThirdActionTaken = 1 

-- calculate @LastActionDate as the greater from @FirstActionDate, @SecondActionDate and @ThirdActionDate. 
SET @LastActionDate = @FirstActionDate; 
IF (@SecondActionDate > @LastActionDate) SET @LastActionDate = @SecondActionDate; 
IF (@ThirdActionDate > @LastActionDate) SET @LastActionDate = @ThirdActionDate; 

SELECT @FirstActionDate AS [FirstActionDate] 
, @SecondActionDate  AS [SecondActionDate] 
, @ThirdActionDate  AS [ThirdActionDate] 
, @LastActionDate  AS [LastActionDate] 

आप निरपेक्ष अंतिम क्रिया तारीख चाहते हैं, आप मूल कोड को बदल सकते हैं , के रूप में इस प्रकार है:

SELECT MAX(LogDate) AS [LastActionDate] 
, MIN(CASE WHEN FirstActionTaken = 1 THEN LogDate ELSE NULL END) AS [FirstActionDate] 
, MIN(CASE WHEN SecondActionTaken = 1 THEN LogDate ELSE NULL END) AS [SecondActionDate] 
, MIN(CASE WHEN ThirdActionTaken = 1 THEN LogDate ELSE NULL END) AS [ThirdActionDate] 
FROM HistoryTable 
WHERE ID = 123 
0

अंतिम SELECT बयान पुनर्रचना पर मेरा अपना प्रयास: 012 के लिए

SELECT MIN(ht2.LogDate) AS FirstActionDate, 
     MIN(ht3.LogDate) AS SecondActionDate, 
     MIN(ht4.LogDate) AS ThirdActionDate, 
     COALESCE (
      MIN(ht4.LogDate), 
      MIN(ht3.LogDate), 
      MIN(ht2.LogDate) 
     ) AS LastActionDate 
FROM HistoryTable ht 
    INNER JOIN HistoryTable ht2 
     ON ht2.ID = ht.ID AND ht2.FirstActionTaken = 1 
    INNER JOIN HistoryTable ht3 
     ON ht3.ID = ht.ID AND ht3.SecondActionTaken = 1 
    INNER JOIN HistoryTable ht4 
     ON ht4.ID = ht.ID AND ht4.ThirdActionTaken = 1 
WHERE ht.ID = 123 
GROUP BY ht.ID 

यह JOINS वापस प्रत्येक xActionTaken कॉलम और SELECTS प्रत्येक के लिए MIN(LogDate) के लिए। फिर, हम परिणामों के माध्यम से पीछे की ओर चलते हैं (ThirdAction, SecondAction, FirstAction) और पहले को वापस करें जिसे हम LastActionTaken के रूप में पाते हैं।

मान्य है कि यह थोड़ा गन्दा है, लेकिन मैंने सोचा कि एक ही डेटा को पुनर्प्राप्त करने के लिए एक और विकल्प दिखाना अच्छा होगा। प्रदर्शन के लिए ध्यान देने योग्य बात

इसके अलावा लायक:

UNPIVOT और OUTER APPLY तरीकों के खिलाफ मेरा उत्तर चलाने के बाद, SSMS Execution Plan पता चलता है कि UNPIVOT और OUTER APPLY मोटे तौर पर (बराबर लगभग ले रहे हैं।प्रत्येक निष्पादन समय के 50%)।

मेरी विधि को इनमें से किसी एक की तुलना करते समय, मेरी विधि लगभग लेती है। निष्पादन समय के 88%, जहां UNPIVOT/OUTER APPLY केवल 12% लें - इसलिए UNPIVOT और OUTER APPLY दोनों तेजी से निष्पादित करें (कम से कम इस उदाहरण में)।

मेरी विधि इतनी अधिक समय लेती है कि SQLHistoryTable का तालिका स्कैन करता है, प्रत्येक बार जब मैं इसमें वापस शामिल होता हूं, तो कुल 4 स्कैन के लिए। अन्य दो तरीकों के साथ, यह क्रिया केवल एक बार की जाती है।

+0

आपके प्रश्न ने कभी भी निर्दिष्ट नहीं किया कि 'थर्डएक्शनडेट> सेकेंडएक्शनडेट> फर्स्टएक्शनडेट', लेकिन आपका उत्तर तात्पर्य है कि यह हमेशा सत्य है। यह मामला है, आपको किसी भी unpivot, बाहरी आवेदन या जुड़ने की जरूरत नहीं है। क्वेरी को केवल एक सहस्राब्दी के साथ सशर्त एकत्रीकरण की आवश्यकता है। मैंने अपना जवाब संपादित किया। आपको सबसे अच्छा प्रदर्शन देना चाहिए। – sstan