पिछली बार खोजने की कोशिश कर रहा है कि एक मूल्य बदल गया है


26

मेरे पास एक टेबल है जिसमें एक आईडी, एक मूल्य और एक तारीख है। इस तालिका में कई आईडी, मूल्य और तिथियां हैं।

अभिलेखों को समय-समय पर इस तालिका में डाला जाता है। आईडी हमेशा समान रहेगी लेकिन कभी-कभी मूल्य बदल जाएगा।

मैं एक क्वेरी कैसे लिख सकता हूं जो मुझे आईडी प्लस देगी सबसे हाल ही में मूल्य बदल गया है? नोट: मूल्य हमेशा बढ़ेगा।

इस नमूना डेटा से:

  Create Table Taco
 (  Taco_ID int,
    Taco_value int,
    Taco_date datetime)

Insert INTO Taco 
Values (1, 1, '2012-07-01 00:00:01'),
        (1, 1, '2012-07-01 00:00:02'),
        (1, 1, '2012-07-01 00:00:03'),
        (1, 1, '2012-07-01 00:00:04'),
        (1, 2, '2012-07-01 00:00:05'),
        (1, 2, '2012-07-01 00:00:06'),
        (1, 2, '2012-07-01 00:00:07'),
        (1, 2, '2012-07-01 00:00:08')

परिणाम होना चाहिए:

Taco_ID      Taco_date
1            2012-07-01 00:00:05

(क्योंकि 00:05 आखिरी बार Taco_Valueबदला गया था।)


2
मुझे लगता tacoहै कि भोजन से कोई लेना-देना नहीं है?
कर्मी

5
मुझे भूख लगी है और मैं कुछ टैको खाना चाहूंगा। बस नमूना तालिका के लिए एक नाम की आवश्यकता थी।
स्क्लैंडविच्स

8
क्या आपने इसी आधार पर अपना उपयोगकर्ता नाम चुना था?
मार्टिन स्मिथ

1
काफी संभव है।
स्क्लैंडविचेस

जवाबों:


13

ये दो प्रश्न उस धारणा पर निर्भर करते हैं जो Taco_valueहमेशा समय के साथ बढ़ती है।

;WITH x AS
(
  SELECT Taco_ID, Taco_date,
    dr = ROW_NUMBER() OVER (PARTITION BY Taco_ID, Taco_Value ORDER BY Taco_date),
    qr = ROW_NUMBER() OVER (PARTITION BY Taco_ID ORDER BY Taco_date)
  FROM dbo.Taco
), y AS
(
  SELECT Taco_ID, Taco_date,
    rn = ROW_NUMBER() OVER (PARTITION BY Taco_ID, dr ORDER BY qr DESC)
  FROM x WHERE dr = 1
)
SELECT Taco_ID, Taco_date
FROM y 
WHERE rn = 1;

कम विंडो फ़ंक्शन पागलपन वाला एक विकल्प:

;WITH x AS
(
  SELECT Taco_ID, Taco_value, Taco_date = MIN(Taco_date)
  FROM dbo.Taco
  GROUP BY Taco_ID, Taco_value
), y AS
(
  SELECT Taco_ID, Taco_date, 
    rn = ROW_NUMBER() OVER (PARTITION BY Taco_ID ORDER BY Taco_date DESC)
  FROM x
)
SELECT Taco_ID, Taco_date FROM y WHERE rn = 1;

SQLfiddle पर उदाहरण


अद्यतन करें

ट्रैक रखने वालों के लिए, इस बात पर विवाद था कि क्या होगा अगर Taco_valueकभी दोहरा सकता है। यदि यह 1 से 2 तक जा सकता है और किसी भी दिए गए के लिए 1 से वापस जा सकता है Taco_ID, तो क्वेरीज़ काम नहीं करेंगी। यहाँ उस मामले के लिए एक समाधान है, भले ही यह काफी अंतराल और द्वीप तकनीक नहीं है कि इट्ज़िक बेन-गण जैसे कोई व्यक्ति सपने देखने में सक्षम हो सकता है, और भले ही यह ओपी के परिदृश्य के लिए प्रासंगिक न हो - यह हो सकता है भविष्य के पाठक के लिए प्रासंगिक है। यह थोड़ा अधिक जटिल है, और मैंने एक अतिरिक्त चर भी जोड़ा है - Taco_IDजो केवल एक ही है Taco_value

यदि आप किसी भी ID के लिए पहली पंक्ति को शामिल करना चाहते हैं, जहाँ मान पूरे सेट में बिल्कुल नहीं बदला है:

;WITH x AS
(
  SELECT *, rn = ROW_NUMBER() OVER 
    (PARTITION BY Taco_ID ORDER BY Taco_date DESC)
  FROM dbo.Taco
), rest AS (SELECT * FROM x WHERE rn > 1)
SELECT  
  main.Taco_ID, 
  Taco_date = MIN(CASE 
    WHEN main.Taco_value = rest.Taco_value 
    THEN rest.Taco_date ELSE main.Taco_date 
  END)
FROM x AS main LEFT OUTER JOIN rest
ON main.Taco_ID = rest.Taco_ID AND rest.rn > 1
WHERE main.rn = 1
AND NOT EXISTS 
(
  SELECT 1 FROM rest AS rest2
   WHERE Taco_ID = rest.Taco_ID
   AND rn < rest.rn
   AND Taco_value <> rest.Taco_value
) 
GROUP BY main.Taco_ID;

यदि आप उन पंक्तियों को बाहर करना चाहते हैं, तो यह थोड़ा अधिक जटिल है, लेकिन फिर भी छोटे बदलाव हैं:

;WITH x AS
(
  SELECT *, rn = ROW_NUMBER() OVER 
    (PARTITION BY Taco_ID ORDER BY Taco_date DESC)
  FROM dbo.Taco
), rest AS (SELECT * FROM x WHERE rn > 1)
SELECT 
  main.Taco_ID, 
  Taco_date = MIN(
  CASE 
    WHEN main.Taco_value = rest.Taco_value 
    THEN rest.Taco_date ELSE main.Taco_date 
  END)
FROM x AS main INNER JOIN rest -- ***** change this to INNER JOIN *****
ON main.Taco_ID = rest.Taco_ID AND rest.rn > 1
WHERE main.rn = 1
AND NOT EXISTS
(
  SELECT 1 FROM rest AS rest2
   WHERE Taco_ID = rest.Taco_ID
   AND rn < rest.rn
   AND Taco_value <> rest.Taco_value
)
AND EXISTS -- ***** add this EXISTS clause ***** 
(
  SELECT 1 FROM rest AS rest2
   WHERE Taco_ID = rest.Taco_ID
   AND Taco_value <> rest.Taco_value
)
GROUP BY main.Taco_ID;

अद्यतन SQLfiddle उदाहरण


मैंने OVER के साथ कुछ महत्वपूर्ण प्रदर्शन मुद्दों पर ध्यान दिया है, लेकिन मैंने इसे केवल कुछ बार उपयोग किया है और इसे खराब तरीके से लिख सकता हूं। क्या आपने कुछ देखा है?
केनेथ फिशर

1
@KennethFisher विशेष रूप से कभी नहीं के साथ। किसी भी अन्य चीज़ की तरह, क्वेरी निर्माण सही ढंग से काम करने के लिए अंतर्निहित स्कीमा / इंडेक्स पर बहुत अधिक निर्भर करता है। एक विभाजन से अधिक एक समूह BY के रूप में एक ही समस्याओं का सामना करना पड़ेगा।
हारून बर्ट्रेंड

@ केनेथफिशर कृपया ध्यान रखें कि एकवचन, पृथक प्रेक्षणों से व्यापक, व्यापक निष्कर्ष न निकाले। मैं सीटीई के खिलाफ एक ही तर्क देखता हूं - "ठीक है, मेरे पास एक बार यह पुनरावर्ती सीटीई था, और इसके प्रदर्शन ने चूसा। इसलिए मैं अब सीटीई का उपयोग नहीं करता हूं।"
हारून बर्ट्रेंड

इसीलिए मैंने पूछा। मैंने इसे एक तरह से या किसी अन्य तरीके से कहने के लिए पर्याप्त उपयोग नहीं किया है, लेकिन कुछ ही बार मैंने इसका उपयोग किया है मैं एक सीटीई के साथ बेहतर प्रदर्शन प्राप्त करने में सक्षम था। मैं हालांकि इसके साथ खेलता रहूंगा।
केनेथ फिशर

@AaronBertrand मुझे नहीं लगता कि ये फिर से काम करेंगे तो value: Fiddle
ypercube13

13

मूल रूप से, यह @ टैरिन का सुझाव है "एक एकल चयन के लिए" संघनित "जिसका कोई व्युत्पन्न तालिका नहीं है:

SELECT DISTINCT
  Taco_ID,
  Taco_date = MAX(MIN(Taco_date)) OVER (PARTITION BY Taco_ID)
FROM Taco
GROUP BY
  Taco_ID,
  Taco_value
;

नोट: यह समाधान Taco_valueकेवल उस वृद्धि को ध्यान में रखता है जो केवल बढ़ सकती है। (अधिक सटीक रूप से, यह मानता है कि Taco_valueपिछले मान में वापस नहीं बदल सकता है - लिंक किए गए उत्तर के समान, वास्तव में।)

क्वेरी के लिए एक SQL फिडेल डेमो: http://sqlfiddle.com/# -3/91368/2


7
वाह, नेस्टेड मैक्स / मिनट। दिमाग उड़ा +1
हारून बर्ट्रेंड

7

आपको परिणाम प्राप्त करने के लिए दोनों min()और max()कुल कार्यों का उपयोग करने में सक्षम होना चाहिए :

select t1.Taco_ID, MAX(t1.taco_date) Taco_Date
from taco t1
inner join
(
    select MIN(taco_date) taco_date,
        Taco_ID, Taco_value
    from Taco
    group by Taco_ID, Taco_value
) t2
    on t1.Taco_ID = t2.Taco_ID
    and t1.Taco_date = t2.taco_date
group by t1.Taco_Id

SQL फिडेल को डेमो के साथ देखें


5

एक और उत्तर जो इस धारणा पर आधारित है कि मान फिर से प्रकट नहीं होते हैं (यह मूल रूप से @ हारून की क्वेरी 2 है, एक कम घोंसले में संघनित):

;WITH x AS
(
  SELECT 
    Taco_ID, Taco_value, 
    Rn = ROW_NUMBER() OVER (PARTITION BY Taco_ID
                            ORDER BY MIN(Taco_date) DESC),
    Taco_date = MIN(Taco_date) 
  FROM dbo.Taco
  GROUP BY Taco_ID, Taco_value
)
SELECT Taco_ID, Taco_value, Taco_date
FROM x 
WHERE Rn = 1 ;

टेस्ट: एसक्यूएल-फिडल


और अधिक सामान्य समस्या का जवाब, जहां मान फिर से प्रकट हो सकते हैं:

;WITH x AS
(
  SELECT 
    Taco_ID, Taco_value, 
    Rn = ROW_NUMBER() OVER (PARTITION BY Taco_ID
                            ORDER BY MAX(Taco_date) DESC),    
    Taco_date = MAX(Taco_date) 
  FROM dbo.Taco
  GROUP BY Taco_ID, Taco_value
)
SELECT t.Taco_ID, Taco_date = MIN(t.Taco_date)
FROM x
  JOIN dbo.Taco t
    ON  t.Taco_ID = x.Taco_ID
    AND t.Taco_date > x.Taco_date
WHERE x.Rn = 2 
GROUP BY t.Taco_ID ;

(या CROSS APPLYऐसा करने से संबंधित सभी संबंधित पंक्ति का उपयोग करके valueदिखाया गया है):

;WITH x AS
(
  SELECT 
    Taco_ID, Taco_value, 
    Rn = ROW_NUMBER() OVER (PARTITION BY Taco_ID
                            ORDER BY MAX(Taco_date) DESC),    
    Taco_date = MAX(Taco_date) 
  FROM dbo.Taco
  GROUP BY Taco_ID, Taco_value
)
SELECT t.*
FROM x
  CROSS APPLY 
  ( SELECT TOP (1) *
    FROM dbo.Taco t
    WHERE t.Taco_ID = x.Taco_ID
      AND t.Taco_date > x.Taco_date
    ORDER BY t.Taco_date
  ) t
WHERE x.Rn = 2 ;

टेस्ट: एसक्यूएल-फिडल -2


अधिक सामान्य समस्या के लिए सुझाव उन आईडी के लिए काम नहीं करते हैं जिनमें कोई बदलाव नहीं है। डमी प्रविष्टियों को मूल सेट (जैसे कुछ dbo.Taco UNION ALL SELECT DISTINCT Taco_ID, NULL AS Taco_value, '19000101' AS Taco_date) में जोड़ने के साथ तय किया जा सकता है ।
एंड्री एम

@AndriyM मुझे पता है। मैंने माना कि "परिवर्तन" का अर्थ है कि वे परिणाम चाहते हैं जब कम से कम 2 मान हों, ओपी ने स्पष्ट नहीं किया है (और क्योंकि यह लिखना आसान था :)
ypercubeᵀᴹ

2

नमूना संरचना और डेटा प्रदान करने के लिए FYI +1। केवल एक चीज जो मैं पूछ सकता था, वह उस डेटा के लिए अपेक्षित आउटपुट है।

संपादित करें: यह एक मुझे पागल ड्राइव करने के लिए जा रहा था। मैं सिर्फ नया था ऐसा करने का एक "सरल" तरीका था। मैंने गलत समाधान से छुटकारा पा लिया और मुझे विश्वास है कि सही है। यहां @bluefeets के समान एक समाधान है, लेकिन इसमें उन परीक्षणों को शामिल किया गया है जो @AaronBertrand ने दिए थे।

;WITH TacoMin AS (SELECT Taco_ID, Taco_value, MIN(Taco_date) InitialValueDate
                FROM Taco
                GROUP BY Taco_ID, Taco_value)
SELECT Taco_ID, MAX(InitialValueDate)
FROM TacoMin
GROUP BY Taco_ID

2
ओपी अधिक हाल की तारीख के लिए नहीं पूछता है, वह पूछता है कि valueपरिवर्तन कब होता है।
ypercube y

आह, मैं अपनी गलती देखता हूं। मैंने एक उत्तर दिया, लेकिन यह बहुत हद तक @ आरोन के समान है और इसे पोस्ट करने का कोई मतलब नहीं है।
केनेथ फिशर

1

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

-- example gives the times the value changed in the last 24 hrs
SELECT
    LastUpdated, [DiffValue]
FROM (
  SELECT
      LastUpdated,
      a.AboveBurdenProbe1TempC - coalesce(lag(a.AboveBurdenProbe1TempC) over (order by ProcessHistoryId), 0) as [DiffValue]
  FROM BFProcessHistory a
  WHERE LastUpdated > getdate() - 1
) b
WHERE [DiffValue] <> 0
ORDER BY LastUpdated ASC

lag...विश्लेषणात्मक समारोह केवल "हाल ही में" एसक्यूएल सर्वर 2012 में पेश किया गया था मूल प्रश्न एसक्यूएल सर्वर 2008 R2 पर एक समाधान के लिए पूछ रहा है। SQL Server 2008 R2 के लिए आपका समाधान काम नहीं करेगा।
जॉन उर्फ ​​हॉट 2use

-1

यह निम्नलिखित के रूप में सरल हो सकता है?

       SELECT taco_id, MAX(
             CASE 
                 WHEN taco_value <> MAX(taco_value) 
                 THEN taco_date 
                 ELSE null 
             END) AS last_change_date

यह देखते हुए कि taco_value हमेशा बढ़ता है?

पीएस मैं काफी शुरुआती एसक्यूएल हूं, हालांकि, धीरे-धीरे लेकिन निश्चित रूप से सीख रहा हूं।


1
SQL सर्वर पर यह त्रुटि देता है। Cannot perform an aggregate function on an expression containing an aggregate or a subquery
मार्टिन स्मिथ

2
मार्टिन की टिप्पणी के लिए एक बिंदु जोड़ना: आप सुरक्षित पक्ष पर हैं यदि आप कभी भी केवल परीक्षण कोड पोस्ट करते हैं। यदि आप अपने सामान्य खेल के मैदान से दूर हैं, तो एक आसान तरीका sqlfiddle.com पर जा सकता है ।
dezso
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.