प्रत्येक समूह की शीर्ष 1 पंक्ति प्राप्त करें


527

मेरे पास एक मेज है जिसे मैं प्रत्येक समूह के लिए नवीनतम प्रविष्टि प्राप्त करना चाहता हूं। यहाँ तालिका है:

DocumentStatusLogs तालिका

|ID| DocumentID | Status | DateCreated |
| 2| 1          | S1     | 7/29/2011   |
| 3| 1          | S2     | 7/30/2011   |
| 6| 1          | S1     | 8/02/2011   |
| 1| 2          | S1     | 7/28/2011   |
| 4| 2          | S2     | 7/30/2011   |
| 5| 2          | S3     | 8/01/2011   |
| 6| 3          | S1     | 8/02/2011   |

तालिका समूह में रखा जाएगा DocumentIDऔर के अनुसार क्रमबद्ध DateCreatedअवरोही क्रम में। प्रत्येक के लिए DocumentID, मैं नवीनतम स्थिति प्राप्त करना चाहता हूं।

मेरा पसंदीदा आउटपुट:

| DocumentID | Status | DateCreated |
| 1          | S1     | 8/02/2011   |
| 2          | S3     | 8/01/2011   |
| 3          | S1     | 8/02/2011   |
  • क्या प्रत्येक समूह से केवल शीर्ष पाने के लिए कोई समग्र कार्य है? GetOnlyTheTopनीचे छद्म कोड देखें:

    SELECT
      DocumentID,
      GetOnlyTheTop(Status),
      GetOnlyTheTop(DateCreated)
    FROM DocumentStatusLogs
    GROUP BY DocumentID
    ORDER BY DateCreated DESC
  • यदि ऐसा कोई फ़ंक्शन मौजूद नहीं है, तो क्या कोई ऐसा तरीका है जिससे मैं अपने इच्छित आउटपुट को प्राप्त कर सकता हूं?

  • या पहली जगह पर, क्या यह अप्राकृतिक डेटाबेस के कारण हो सकता है? मैं सोच रहा हूं, क्योंकि मैं जो देख रहा हूं वह सिर्फ एक पंक्ति है, क्या यह statusभी माता-पिता की तालिका में स्थित होना चाहिए ?

कृपया अधिक जानकारी के लिए मूल तालिका देखें:

करंट Documentsटेबल

| DocumentID | Title  | Content  | DateCreated |
| 1          | TitleA | ...      | ...         |
| 2          | TitleB | ...      | ...         |
| 3          | TitleC | ...      | ...         |

क्या पैरेंट टेबल इस तरह का होना चाहिए ताकि मैं आसानी से इसकी स्थिति तक पहुँच बना सकूँ?

| DocumentID | Title  | Content  | DateCreated | CurrentStatus |
| 1          | TitleA | ...      | ...         | s1            |
| 2          | TitleB | ...      | ...         | s3            |
| 3          | TitleC | ...      | ...         | s1            |

अद्यतन मैंने सिर्फ "लागू" का उपयोग करना सीखा, जिससे इस तरह की समस्याओं का समाधान करना आसान हो जाता है।


2
अधिक विस्तृत चर्चा और संभावित समाधानों की तुलना के लिए मैं dba.se पर इसी तरह के प्रश्न को पढ़ने की सलाह देता हूं: प्रति समूह n पंक्तियों को पुनः प्राप्त करना
व्लादिमीर बारानोव

मैंने पोस्ट को देखा और इसे आजमाया। StoreID द्वारा समूह का उपयोग करने से एक त्रुटि उत्पन्न हुई।
अल्ट्रा जेपी

जवाबों:


753
;WITH cte AS
(
   SELECT *,
         ROW_NUMBER() OVER (PARTITION BY DocumentID ORDER BY DateCreated DESC) AS rn
   FROM DocumentStatusLogs
)
SELECT *
FROM cte
WHERE rn = 1

यदि आप प्रति दिन 2 प्रविष्टियों की उम्मीद करते हैं, तो यह मनमाने ढंग से एक को उठाएगा। एक दिन के लिए दोनों प्रविष्टियाँ प्राप्त करने के लिए, इसके बजाय DENSE_RANK का उपयोग करें

सामान्यीकृत या नहीं के रूप में, यह निर्भर करता है कि आप क्या करना चाहते हैं:

  • 2 स्थानों पर स्थिति बनाए रखें
  • स्थिति इतिहास को संरक्षित करें
  • ...

जैसा कि यह खड़ा है, आप स्थिति इतिहास को संरक्षित करते हैं। यदि आप मूल तालिका में नवीनतम स्थिति चाहते हैं (जो कि अपभ्रंश है) तो आपको अभिभावक में "स्थिति" बनाए रखने के लिए ट्रिगर की आवश्यकता होगी। या इस स्थिति इतिहास तालिका को छोड़ दें।


5
और ... क्या है Partition By? Withमेरे लिए भी नया है :( मैं वैसे भी mssql 2005 का उपयोग कर रहा हूं।
8:27 पर

6
@domanokz: विभाजन गणना को रीसेट करता है। तो इस मामले में, यह प्रलेखित प्रति गिनने के लिए कहता है
gbn

1
हम्म, मुझे प्रदर्शन की चिंता है, मैं लाखों पंक्तियों की क्वेरी करूंगा। क्या SELECT * FROM (SELECT ...) प्रदर्शन को प्रभावित करता है? इसके अलावा, ROW_NUMBERप्रत्येक पंक्ति के लिए किसी प्रकार की एक उपश्रेणी है?
डीपीपी

1
@domanokz: नहीं, यह एक उपश्रेणी नहीं है। यदि आपके पास सही अनुक्रमणिका है तो लाखों को समस्या नहीं होनी चाहिए। वैसे भी केवल 2 सेट आधारित तरीके हैं: यह और समग्र (एरियल समाधान)। तो उन दोनों को आज़माएं ...
gbn

1
@domanokz: बस दिनांक द्वारा डिस्क्राइब किए गए आदेश को डिस्क्राइब करें और आईडी डीआरसी को ऑर्डर करें
gbn

184

मैंने अभी सीखा कि कैसे उपयोग करना है cross apply। इस परिदृश्य में इसका उपयोग कैसे करें:

 select d.DocumentID, ds.Status, ds.DateCreated 
 from Documents as d 
 cross apply 
     (select top 1 Status, DateCreated
      from DocumentStatusLogs 
      where DocumentID = d.DocumentId
      order by DateCreated desc) as ds

2
यह वास्तव में कोई फर्क नहीं पड़ता क्योंकि समस्या अभी भी संबोधित है।
dpp

19
मैंने सभी प्रस्तावित समाधानों के खिलाफ अपने समय परीक्षण के परिणामों को पोस्ट किया और आपका शीर्ष पर आया। आपको एक वोट देते हुए :-)
जॉन फेयरबैंक्स

3
विशाल गति में सुधार के लिए +1। यह ROW_NUMBER () जैसे विंडोिंग फ़ंक्शन की तुलना में बहुत तेज़ है। यह अच्छा होगा यदि एसक्यूएल ROW_NUMBER () = 1 को प्रश्नों की तरह मान्यता देता है और उन्हें एप्लाइड में अनुकूलित करता है। नोट: जब भी वे आवेदन में मौजूद नहीं थे, मैंने परिणामों की आवश्यकता के अनुसार मैंने OPL APPLY का उपयोग किया।
तमसुराजॉयस

8
@TamusJRoyce आप इसे केवल इसलिए नहीं बढ़ा सकते क्योंकि यह हमेशा ऐसा ही होता था। निर्भर करता है। जैसा कि यहाँ बताया गया है sqlmag.com/database-development/optimizing-top-n-group-queries
मार्टिन स्मिथ

2
मेरी टिप्पणी में कई पंक्तियाँ होने के बारे में है, और प्रति समूह उन कई पंक्तियों में से केवल एक को चुनना है। जब आप एक से कई चाहते हैं तो जॉइन होते हैं। आवेदन तब होते हैं जब आपके पास एक से कई होते हैं, लेकिन एक को छोड़कर सभी को फ़िल्टर करना चाहते हैं। परिदृश्य: 100 सदस्यों के लिए, मुझे अपना सर्वश्रेष्ठ फ़ोन नंबर दें (जहाँ प्रत्येक के कई नंबर हो सकते हैं)। यह वह जगह है जहाँ एक्सेल लागू करें। कम रीड = कम डिस्क एक्सेस = बेहतर प्रदर्शन। यह देखते हुए कि मेरा अनुभव खराब डिज़ाइन वाले सामान्यीकृत डेटाबेस के साथ है।
तमसुराजॉय

53

मैंने यहां विभिन्न सिफारिशों पर कुछ समय दिया है, और परिणाम वास्तव में शामिल तालिका के आकार पर निर्भर करते हैं, लेकिन सबसे संगत समाधान CROSS APPLY का उपयोग कर रहा है। ये परीक्षण SQL Server 2008-R2 के खिलाफ चलाए गए थे, एक तालिका का उपयोग करके 6,500 रिकॉर्ड, और 137 मिलियन रिकॉर्ड के साथ एक और (समान स्कीमा)। स्तंभित किए जा रहे स्तंभ तालिका की प्राथमिक कुंजी का हिस्सा हैं, और तालिका की चौड़ाई बहुत छोटी है (लगभग 30 बाइट्स)। वास्तविक निष्पादन योजना से SQL सर्वर द्वारा समय की सूचना दी जाती है।

Query                                  Time for 6500 (ms)    Time for 137M(ms)

CROSS APPLY                                    17.9                17.9
SELECT WHERE col = (SELECT MAX(COL)…)           6.6               854.4
DENSE_RANK() OVER PARTITION                     6.6               907.1

मुझे लगता है कि वास्तव में आश्चर्यजनक बात यह थी कि शामिल पंक्तियों की संख्या की परवाह किए बिना CROSS APPLY के लिए समय कितना सुसंगत था।


8
यह सब डेटा वितरण और उपलब्ध अनुक्रमित पर निर्भर करता है। इस पर dba.se पर काफी लंबी चर्चा की गई ।
व्लादिमीर बारानोव

48

मुझे पता है कि यह एक पुराना धागा है, लेकिन TOP 1 WITH TIESसमाधान काफी अच्छा है और समाधान के माध्यम से कुछ पढ़ने में मददगार हो सकता है।

select top 1 with ties
   DocumentID
  ,Status
  ,DateCreated
from DocumentStatusLogs
order by row_number() over (partition by DocumentID order by DateCreated desc)

TOP क्लॉज़ के बारे में अधिक जानकारी यहाँ मिल सकती है


7
यह सबसे सुंदर समाधान imo है
जॉर्ज मेनटिस

1
सहमत - यह सबसे अच्छा
बताता है

27

यदि आप प्रदर्शन को लेकर चिंतित हैं, तो आप MAX () के साथ भी ऐसा कर सकते हैं:

SELECT *
FROM DocumentStatusLogs D
WHERE DateCreated = (SELECT MAX(DateCreated) FROM DocumentStatusLogs WHERE ID = D.ID)

ROW_NUMBER () को आपके SELECT स्टेटमेंट में सभी पंक्तियों के एक प्रकार की आवश्यकता होती है, जबकि MAX नहीं करता है। अपनी क्वेरी को बहुत तेज़ करना चाहिए।


2
ROW_NUMBER () के साथ प्रदर्शन समस्याओं को उचित अनुक्रमण के साथ संबोधित नहीं किया जा सकता है? (मुझे लगता है कि किसी भी तरह किया जाना चाहिए)
क्रिस्टोफर एल

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

सादगी के लिए +1। @TamusJRoyce सही है। व्हाट अबाउट? 'का चयन करें * DocumentStatusLog D से जहाँ ID = (DocumentsStatusLog से ID का चयन करें जहाँ D.DocumentID = DateCreated DESC लिमिट 1 द्वारा डॉक्यूमेंट का आदेश);'
cibercitizen1

EventScheduleTbl डी से चयन * कहां DatesPicked = (चयन शीर्ष 1 मिनट (DatesPicked) EventScheduleTbl से कहां EventIDf = D.EventIDf और DatesPicked> = परिवर्तित (तिथि, getdate ()))
अरुण प्रसाद ES

निश्चित रूप से ऐसे मामले हैं जहां यह row_number()उचित अनुक्रमण के साथ भी बेहतर प्रदर्शन करेगा । मुझे यह विशेष रूप से स्व-सम्मिलित परिदृश्यों में मूल्यवान लगता है। हालाँकि, इस बात का संज्ञान होने की बात यह है कि यह विधि अक्सर कम सबट्री लागत की रिपोर्ट करने के बावजूद, तार्किक रीड और स्कैन काउंट दोनों की अधिक संख्या उत्पन्न करेगी। यदि यह वास्तव में बेहतर है, तो यह निर्धारित करने के लिए आपको अपने विशेष मामले में लागत / लाभों को तौलना होगा।
16

26
SELECT * FROM
DocumentStatusLogs JOIN (
  SELECT DocumentID, MAX(DateCreated) DateCreated
  FROM DocumentStatusLogs
  GROUP BY DocumentID
  ) max_date USING (DocumentID, DateCreated)

क्या डेटाबेस सर्वर? यह कोड उन सभी पर काम नहीं करता है।

आपके प्रश्न के उत्तरार्ध के संबंध में, मुझे एक कॉलम के रूप में स्थिति को शामिल करना उचित प्रतीत होता है। आप DocumentStatusLogsलॉग के रूप में छोड़ सकते हैं , लेकिन फिर भी मुख्य तालिका में नवीनतम जानकारी संग्रहीत कर सकते हैं।

BTW, यदि आपके पास पहले से ही DateCreatedदस्तावेज़ तालिका में कॉलम है , तो आप इसका DocumentStatusLogsउपयोग करके जुड़ सकते हैं (जब तक DateCreatedअद्वितीय है DocumentStatusLogs)।

संपादित करें: MsSQL USING का समर्थन नहीं करता है, इसलिए इसे इसमें बदलें:

ON DocumentStatusLogs.DocumentID = max_date.DocumentID AND DocumentStatusLogs.DateCreated = max_date.DateCreated

5
सुराग शीर्षक में था: MSSQL। SQL सर्वर में USING नहीं है लेकिन विचार ठीक है।
जीएनबी

7
@ जीबी बेवकूफ़ मध्यस्थ आमतौर पर महत्वपूर्ण कीवर्ड्स को शीर्षक से हटाते हैं, जैसा कि उन्होंने यहां किया है। खोज परिणामों या Google में सही उत्तर खोजना बहुत मुश्किल है।
NickG

2
जुस ने कहा कि यह "समाधान" अभी भी आपको कई रिकॉर्ड दे सकता है यदि आपके पास टाई हैmax(DateCreated)
मूनकनाइट नोव

12

यह विषय पर सबसे आसानी से पाया जाने वाला प्रश्न है, इसलिए मैं इसे (मेरे संदर्भ के लिए और दूसरों की मदद करने के लिए) एक आधुनिक उत्तर देना चाहता था। का उपयोग करके first_valueऔर overआप उपरोक्त क्वेरी के छोटे काम कर सकते हैं:

Select distinct DocumentID
  , first_value(status) over (partition by DocumentID order by DateCreated Desc) as Status
  , first_value(DateCreated) over (partition by DocumentID order by DateCreated Desc) as DateCreated
From DocumentStatusLogs

यह Sql सर्वर 2008 और ऊपर में काम करना चाहिए। एक खंड का उपयोग करते समय First_valueपूरा करने के तरीके के रूप में सोचा जा सकता है । नेस्टेड सबक्वेरीज़ लिखने की बजाय चुनिंदा सूची में समूहीकरण करने की अनुमति देता है (जैसे मौजूदा उत्तरों में से कई करते हैं), यह इसे अधिक पठनीय अंदाज़ में करता है। उम्मीद है की यह मदद करेगा।Select Top 1overOver


2
यह SQL Server 2008 R2 में काम नहीं करता है। मुझे लगता है कि 2012 में first_value पेश किया गया था!
ufo

1
बहुत तेज़! मैं @ डीपीपी द्वारा पेश किए गए क्रॉस अप्लाई समाधान का उपयोग कर रहा था, लेकिन यह तेजी से वाया है।
मैटसेले

11

यह काफी पुराना धागा है, लेकिन मुझे लगा कि मैं अपने दोनों सेंट को उसी तरह फेंक दूंगा जैसे कि स्वीकृत उत्तर मेरे लिए विशेष रूप से अच्छी तरह से काम नहीं करता है। मैंने एक बड़े डेटासेट पर gbn के समाधान की कोशिश की और इसे बहुत धीमी गति से पाया (> SQL Server 2012 में 5 मिलियन से अधिक रिकॉर्ड पर 45 सेकंड)। निष्पादन योजना को देखते हुए यह स्पष्ट है कि मुद्दा यह है कि इसके लिए एक SORT ऑपरेशन की आवश्यकता है जो चीजों को काफी धीमा कर देता है।

यहां एक विकल्प है कि मैंने इकाई ढांचे से उठा लिया है जिसे किसी प्रकार के संचालन की आवश्यकता नहीं है और एक गैर-क्लस्टर सूचकांक खोज करता है। यह पूर्वोक्त रिकॉर्ड सेट पर निष्पादन समय को घटाकर <2 सेकंड कर देता है।

SELECT 
[Limit1].[DocumentID] AS [DocumentID], 
[Limit1].[Status] AS [Status], 
[Limit1].[DateCreated] AS [DateCreated]
FROM   (SELECT DISTINCT [Extent1].[DocumentID] AS [DocumentID] FROM [dbo].[DocumentStatusLogs] AS [Extent1]) AS [Distinct1]
OUTER APPLY  (SELECT TOP (1) [Project2].[ID] AS [ID], [Project2].[DocumentID] AS [DocumentID], [Project2].[Status] AS [Status], [Project2].[DateCreated] AS [DateCreated]
    FROM (SELECT 
        [Extent2].[ID] AS [ID], 
        [Extent2].[DocumentID] AS [DocumentID], 
        [Extent2].[Status] AS [Status], 
        [Extent2].[DateCreated] AS [DateCreated]
        FROM [dbo].[DocumentStatusLogs] AS [Extent2]
        WHERE ([Distinct1].[DocumentID] = [Extent2].[DocumentID])
    )  AS [Project2]
    ORDER BY [Project2].[ID] DESC) AS [Limit1]

अब मैं कुछ ऐसा मान रहा हूं जो मूल प्रश्न में पूरी तरह से निर्दिष्ट नहीं है, लेकिन अगर आपकी टेबल का डिज़ाइन ऐसा है कि आपका आईडी कॉलम एक ऑटो-इंक्रीमेंट आईडी है, और DateCreated प्रत्येक प्रविष्टि के साथ वर्तमान तिथि पर सेट है, तो भी ऊपर मेरी क्वेरी के साथ चलने के बिना आप वास्तव में gbn के समाधान (लगभग आधे निष्पादन समय) के लिए एक बड़ा प्रदर्शन बढ़ाने को प्राप्त कर सकते थे, सिर्फ DateCreated पर आदेश देने के बजाय ID पर ऑर्डर करने से क्योंकि यह एक समान क्रम क्रम प्रदान करेगा और यह एक तेज़ क्रम है।


5

प्रत्येक समूह से शीर्ष 1 का चयन करने के लिए मेरा कोड

# कहाँ से #DocumentStatusLogs a। * चुनें 
 में चुना गया (#DocumentStatusLogs b से शीर्ष 1 का चयन करें
कहाँ पे 
a.documentid = b.documentid
डेसक्रिटेड डेस द्वारा ऑर्डर
)

3

ऊपर से क्लिंट का भयानक और सही उत्तर सत्यापित करना:

नीचे दिए गए दो प्रश्नों के बीच प्रदर्शन दिलचस्प है। 52% शीर्ष पर रहा। और 48% दूसरा है। ORDER BY के बजाय DISTINCT का उपयोग करके प्रदर्शन में 4% सुधार। लेकिन ORDER BY में कई कॉलमों को छांटने का फायदा है।

IF (OBJECT_ID('tempdb..#DocumentStatusLogs') IS NOT NULL) BEGIN DROP TABLE #DocumentStatusLogs END

CREATE TABLE #DocumentStatusLogs (
    [ID] int NOT NULL,
    [DocumentID] int NOT NULL,
    [Status] varchar(20),
    [DateCreated] datetime
)

INSERT INTO #DocumentStatusLogs([ID], [DocumentID], [Status], [DateCreated]) VALUES (2, 1, 'S1', '7/29/2011 1:00:00')
INSERT INTO #DocumentStatusLogs([ID], [DocumentID], [Status], [DateCreated]) VALUES (3, 1, 'S2', '7/30/2011 2:00:00')
INSERT INTO #DocumentStatusLogs([ID], [DocumentID], [Status], [DateCreated]) VALUES (6, 1, 'S1', '8/02/2011 3:00:00')
INSERT INTO #DocumentStatusLogs([ID], [DocumentID], [Status], [DateCreated]) VALUES (1, 2, 'S1', '7/28/2011 4:00:00')
INSERT INTO #DocumentStatusLogs([ID], [DocumentID], [Status], [DateCreated]) VALUES (4, 2, 'S2', '7/30/2011 5:00:00')
INSERT INTO #DocumentStatusLogs([ID], [DocumentID], [Status], [DateCreated]) VALUES (5, 2, 'S3', '8/01/2011 6:00:00')
INSERT INTO #DocumentStatusLogs([ID], [DocumentID], [Status], [DateCreated]) VALUES (6, 3, 'S1', '8/02/2011 7:00:00')

विकल्प 1:

    SELECT
    [Extent1].[ID], 
    [Extent1].[DocumentID],
    [Extent1].[Status], 
    [Extent1].[DateCreated]
FROM #DocumentStatusLogs AS [Extent1]
    OUTER APPLY (
        SELECT TOP 1
            [Extent2].[ID], 
            [Extent2].[DocumentID],
            [Extent2].[Status], 
            [Extent2].[DateCreated]
        FROM #DocumentStatusLogs AS [Extent2]
        WHERE [Extent1].[DocumentID] = [Extent2].[DocumentID]
        ORDER BY [Extent2].[DateCreated] DESC, [Extent2].[ID] DESC
    ) AS [Project2]
WHERE ([Project2].[ID] IS NULL OR [Project2].[ID] = [Extent1].[ID])

विकल्प 2:

SELECT 
    [Limit1].[DocumentID] AS [ID], 
    [Limit1].[DocumentID] AS [DocumentID], 
    [Limit1].[Status] AS [Status], 
    [Limit1].[DateCreated] AS [DateCreated]
FROM (
    SELECT DISTINCT [Extent1].[DocumentID] AS [DocumentID] FROM #DocumentStatusLogs AS [Extent1]
) AS [Distinct1]
    OUTER APPLY  (
        SELECT TOP (1) [Project2].[ID] AS [ID], [Project2].[DocumentID] AS [DocumentID], [Project2].[Status] AS [Status], [Project2].[DateCreated] AS [DateCreated]
        FROM (
            SELECT 
                [Extent2].[ID] AS [ID], 
                [Extent2].[DocumentID] AS [DocumentID], 
                [Extent2].[Status] AS [Status], 
                [Extent2].[DateCreated] AS [DateCreated]
            FROM #DocumentStatusLogs AS [Extent2]
            WHERE [Distinct1].[DocumentID] = [Extent2].[DocumentID]
        )  AS [Project2]
        ORDER BY [Project2].[ID] DESC
    ) AS [Limit1]

एम $ का प्रबंधन स्टूडियो: पहले ब्लॉक को उजागर करने और चलाने के बाद, विकल्प 1 और विकल्प 2 दोनों को हाइलाइट करें, राइट क्लिक करें -> [प्रदर्शन अनुमानित योजना]। फिर परिणाम देखने के लिए पूरी बात चलाएं।

विकल्प 1 परिणाम:

ID  DocumentID  Status  DateCreated
6   1   S1  8/2/11 3:00
5   2   S3  8/1/11 6:00
6   3   S1  8/2/11 7:00

विकल्प 2 के परिणाम:

ID  DocumentID  Status  DateCreated
6   1   S1  8/2/11 3:00
5   2   S3  8/1/11 6:00
6   3   S1  8/2/11 7:00

ध्यान दें:

जब मैं 1-टू- (कई में से 1) होना चाहता हूं, तो मैं APPLY का उपयोग करता हूं।

मैं एक जॉइन का उपयोग करता हूं अगर मैं चाहता हूं कि इसमें शामिल होने के लिए 1-से-कई, या कई-से-कई हो।

मैं RTE_NUMBER () के साथ CTE से बचता हूं, जब तक कि मुझे कुछ उन्नत करने की आवश्यकता नहीं है और विंडो प्रदर्शन के दंड के साथ ठीक है।

मैं WHIS या ON क्लॉज में EXISTS / IN सबक्वेरी से भी बचता हूं, क्योंकि मैंने इसे कुछ भयानक निष्पादन योजनाओं का अनुभव किया है। लेकिन माइलेज अलग-अलग होता है। निष्पादन योजना और प्रोफ़ाइल प्रदर्शन की समीक्षा करें कि कहाँ और कब ज़रूरत है!


3

इस समाधान का उपयोग प्रत्येक विभाजन के लिए TOP N सबसे हाल की पंक्तियाँ प्राप्त करने के लिए किया जा सकता है (उदाहरण के लिए, WHERE कथन में N 1 है और विभाजन doc_id है):

SELECT doc_id, status, date_created FROM 
(
    SELECT a.*, ROW_NUMBER() OVER (PARTITION BY doc_id ORDER BY date_created DESC) AS rnk FROM doc a
)
WHERE rnk = 1;

2
SELECT o.*
FROM `DocumentStatusLogs` o                   
  LEFT JOIN `DocumentStatusLogs` b                   
  ON o.DocumentID = b.DocumentID AND o.DateCreated < b.DateCreated
 WHERE b.DocumentID is NULL ;

यदि आप DateCreated द्वारा केवल हाल ही के दस्तावेज़ क्रम को वापस करना चाहते हैं, तो यह DocumentID द्वारा केवल शीर्ष 1 दस्तावेज़ लौटाएगा


2

CROSS APPLYमैं अपने समाधान के लिए जिस पद्धति का उपयोग करता था, वह मेरे लिए काम करती थी, और मेरे ग्राहकों की जरूरतों के लिए। और जो मैंने पढ़ा है, उसमें से सर्वश्रेष्ठ समग्र प्रदर्शन प्रदान करना चाहिए, उनका डेटाबेस पर्याप्त रूप से बढ़ना चाहिए।


1

यहां उन प्रश्नों में से प्रत्येक के लिए अनुक्रमण के सर्वोत्तम विकल्पों के साथ हाथ में समस्या के लिए 3 अलग-अलग दृष्टिकोण हैं (कृपया अपने स्वयं के अनुक्रमित प्रयास करें और तार्किक पढ़ें, बीता हुआ समय, निष्पादन योजना देखें। मैंने अपने अनुभव से सुझाव प्रदान किए हैं। इस विशिष्ट समस्या के निष्पादन के बिना ऐसी क्वेरी)।

दृष्टिकोण 1 : ROW_NUMBER () का उपयोग करना। यदि रोस्टोरेंट इंडेक्स प्रदर्शन को बढ़ाने में सक्षम नहीं हो रहा है, तो आप गैर-संकुलित / क्लस्टर किए गए कॉलमस्टोर इंडेक्स को अलग-अलग करने और समूहन के साथ प्रश्नों के लिए और हर समय विभिन्न कॉलमों में ऑर्डर किए गए टेबल्स के लिए प्रयास कर सकते हैं, आमतौर पर कॉलमस्टोर स्कोर सबसे अच्छा विकल्प है।

;WITH CTE AS
    (
       SELECT   *,
                RN = ROW_NUMBER() OVER (PARTITION BY DocumentID ORDER BY DateCreated DESC)
       FROM     DocumentStatusLogs
    )
    SELECT  ID      
        ,DocumentID 
        ,Status     
        ,DateCreated
    FROM    CTE
    WHERE   RN = 1;

दृष्टिकोण 2 : FIRST_VALUE का उपयोग करना। यदि रोस्टोरेंट इंडेक्स प्रदर्शन को बढ़ाने में सक्षम नहीं हो रहा है, तो आप गैर-संकुलित / क्लस्टर किए गए कॉलमस्टोर इंडेक्स को अलग-अलग करने और समूहन के साथ प्रश्नों के लिए और हर समय विभिन्न कॉलमों में ऑर्डर किए गए टेबल्स के लिए प्रयास कर सकते हैं, आमतौर पर कॉलमस्टोर स्कोर सबसे अच्छा विकल्प है।

SELECT  DISTINCT
    ID      = FIRST_VALUE(ID) OVER (PARTITION BY DocumentID ORDER BY DateCreated DESC)
    ,DocumentID
    ,Status     = FIRST_VALUE(Status) OVER (PARTITION BY DocumentID ORDER BY DateCreated DESC)
    ,DateCreated    = FIRST_VALUE(DateCreated) OVER (PARTITION BY DocumentID ORDER BY DateCreated DESC)
FROM    DocumentStatusLogs;

दृष्टिकोण 3 : CROSS APPLY का उपयोग करना। DocumentStatusLogs टेबल पर rowstore इंडेक्स बनाना क्वेरी में उपयोग किए गए कॉलम को कवर करने के लिए पर्याप्त होना चाहिए, ताकि कॉलमस्टोर इंडेक्स की आवश्यकता के बिना क्वेरी को कवर किया जा सके।

SELECT  DISTINCT
    ID      = CA.ID
    ,DocumentID = D.DocumentID
    ,Status     = CA.Status 
    ,DateCreated    = CA.DateCreated
FROM    DocumentStatusLogs D
    CROSS APPLY (
            SELECT  TOP 1 I.*
            FROM    DocumentStatusLogs I
            WHERE   I.DocumentID = D.DocumentID
            ORDER   BY I.DateCreated DESC
            ) CA;

1

मेरा मानना ​​है कि यह इस तरह से किया जा सकता है। इसके लिए कुछ ट्विकिंग की आवश्यकता हो सकती है लेकिन आप समूह से अधिकतम का चयन कर सकते हैं।

ये जवाब है ओवरकिल ।।

SELECT
  d.DocumentID,
  MAX(d.Status),
  MAX(d1.DateCreated)
FROM DocumentStatusLogs d, DocumentStatusLogs d1
USING(DocumentID)
GROUP BY d.DocumentID
ORDER BY DateCreated DESC

0

उन परिदृश्यों में, जहाँ आप row_count () का उपयोग करने से बचना चाहते हैं, आप बाईं ओर का उपयोग भी कर सकते हैं:

select ds.DocumentID, ds.Status, ds.DateCreated 
from DocumentStatusLogs ds
left join DocumentStatusLogs filter 
    ON ds.DocumentID = filter.DocumentID
    -- Match any row that has another row that was created after it.
    AND ds.DateCreated < filter.DateCreated
-- then filter out any rows that matched 
where filter.DocumentID is null 

उदाहरण के लिए स्कीमा, आप एक "सबक्लेरी में नहीं" का भी उपयोग कर सकते हैं, जो आमतौर पर उसी आउटपुट के लिए संकलित होता है:

select ds.DocumentID, ds.Status, ds.DateCreated 
from DocumentStatusLogs ds
WHERE ds.ID NOT IN (
    SELECT filter.ID 
    FROM DocumentStatusLogs filter
    WHERE ds.DocumentID = filter.DocumentID
        AND ds.DateCreated < filter.DateCreated)

ध्यान दें, यदि तालिका में कम से कम एक एकल-स्तंभ अद्वितीय कुंजी / बाधा / अनुक्रमणिका नहीं है, तो उपकुंजी प्रतिमान काम नहीं करेगा, इस मामले में प्राथमिक कुंजी "Id" है।

ये दोनों क्वेरीज़ row_count () क्वेरी (क्वेरी एनालाइज़र द्वारा मापी गई) की तुलना में अधिक "महंगी" होती हैं। हालाँकि, आप उन परिदृश्यों से मुठभेड़ कर सकते हैं जहाँ वे तेजी से परिणाम लौटाते हैं या अन्य अनुकूलन सक्षम करते हैं।


0
SELECT documentid, 
       status, 
       datecreated 
FROM   documentstatuslogs dlogs 
WHERE  status = (SELECT status 
                 FROM   documentstatuslogs 
                 WHERE  documentid = dlogs.documentid 
                 ORDER  BY datecreated DESC 
                 LIMIT  1) 

0

इसे इस्तेमाल करे:

SELECT [DocumentID]
    ,[tmpRez].value('/x[2]', 'varchar(20)') AS [Status]
    ,[tmpRez].value('/x[3]', 'datetime') AS [DateCreated]
FROM (
    SELECT [DocumentID]
        ,cast('<x>' + max(cast([ID] AS VARCHAR(10)) + '</x><x>' + [Status] + '</x><x>' + cast([DateCreated] AS VARCHAR(20))) + '</x>' AS XML) AS [tmpRez]
    FROM DocumentStatusLogs
    GROUP BY DocumentID
    ) AS [tmpQry]

आपको हमेशा अपने एसक्यूएल स्टेटमेंट का वर्णन करना चाहिए कि यह कैसे काम करेगा और ओपी की क्वेरी को हल करेगा।
सूरज कुमार

-1

यह सबसे वैनिला TSQL है जिसके साथ मैं आ सकता हूं

    SELECT * FROM DocumentStatusLogs D1 JOIN
    (
      SELECT
        DocumentID,MAX(DateCreated) AS MaxDate
      FROM
        DocumentStatusLogs
      GROUP BY
        DocumentID
    ) D2
    ON
      D2.DocumentID=D1.DocumentID
    AND
      D2.MaxDate=D1.DateCreated

दुर्भाग्य से मैक्सडेट अद्वितीय नहीं है। एक ही सटीक समय पर दो तिथियों का प्रवेश संभव है। तो इसका परिणाम प्रति समूह डुप्लिकेट हो सकता है। हालाँकि, आप पहचान स्तंभ या GUID का उपयोग कर सकते हैं। आइडेंटिटी कॉलम आपको वह नवीनतम मिलेगा जो दर्ज किया गया है (डिफ़ॉल्ट पहचान कैल्क का उपयोग किया जा रहा है, 1 ... x चरण 1)।
तमसुसरॉयस

अच्छी तरह से मैं सहमत हूँ, लेकिन लेखक ने नवीनतम प्रविष्टि के लिए कहा - जब तक कि आप एक ऑटो-इन्क्रीमेंट पहचान कॉलम शामिल नहीं करते हैं, इसका मतलब है कि एक ही समय में जोड़े गए दो आइटम समान रूप से 'नवीनतम' हैं
अमीर एस

नवीनतम रिकॉर्ड एक रिकॉर्ड होगा। तो हाँ। आपको ऑटो-इन्क्रीमेंट पहचान कॉलम पर विचार करने की आवश्यकता है।
TamusJRoyce

-2

यह SQLite में जाँच की जाती है कि आप ग्रुप BY के साथ निम्नलिखित सरल क्वेरी का उपयोग कर सकते हैं

SELECT MAX(DateCreated), *
FROM DocumentStatusLogs
GROUP BY DocumentID

यहां MAX प्रत्येक समूह से अधिकतम DateCreated प्राप्त करने में मदद करता है ।

लेकिन ऐसा लगता है कि MYSQL अधिकतम DateCreated के मूल्य के साथ * -columns को संबद्ध नहीं करता है :(

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