Her grubun ilk 1 sırasını al


527

Her grup için en son girişi almak istediğim bir tablo var. İşte tablo:

DocumentStatusLogs tablo

|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   |

Tablo, azalan sırada gruplanacak DocumentIDve sıralanacaktır DateCreated. Her biri DocumentIDiçin en son durumu almak istiyorum.

Tercih ettiğim çıktı:

| DocumentID | Status | DateCreated |
| 1          | S1     | 8/02/2011   |
| 2          | S3     | 8/01/2011   |
| 3          | S1     | 8/02/2011   |
  • Her gruptan sadece en üstte yer almak için herhangi bir toplama işlevi var mı? Aşağıdaki sahte kodlara GetOnlyTheTopbakın:

    SELECT
      DocumentID,
      GetOnlyTheTop(Status),
      GetOnlyTheTop(DateCreated)
    FROM DocumentStatusLogs
    GROUP BY DocumentID
    ORDER BY DateCreated DESC
  • Böyle bir işlev yoksa, istediğim çıktıyı elde edebilmemin bir yolu var mı?

  • Veya ilk etapta, bunun nedeni normalleştirilmemiş veritabanı olabilir mi? Bence, aradığım şey sadece bir satır olduğundan, statusüst tabloda da yer almalı mı?

Daha fazla bilgi için lütfen üst tabloya bakın:

Geçerli DocumentsTablo

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

Durumuna kolayca erişebilmem için üst tablo böyle olmalı mı?

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

GÜNCELLEME Bu tür sorunları çözmeyi kolaylaştıran "uygula" nın nasıl kullanıldığını öğrendim.


2
Daha ayrıntılı bir tartışma ve olası çözümlerin karşılaştırılması için dba.se: grup başına n satır alma konusundaki benzer soruyu okumanızı tavsiye ederim .
Vladimir Baranov

Gönderiye baktım ve denedim. kullanmaStoreID tarafından grup bir hata oluştu .
UltraJ

Yanıtlar:


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

Günde 2 giriş bekliyorsanız, bu keyfi olarak bir giriş seçer. Bir gün için her iki girişi de almak için, bunun yerine DENSE_RANK kullanın

Normalize edilmiş olsun ya da olmasın, aşağıdakilere göre değişir:

  • durumu 2 yerde koru
  • durum geçmişini koru
  • ...

Durumdayken, durum geçmişini korursunuz. Üst tabloda da son durumu istiyorsanız (denormalizasyon), üst öğede "durumu" korumak için bir tetikleyiciye ihtiyacınız olacaktır. veya bu durum geçmişi tablosunu bırakın.


5
Ve ... Nedir Partition By? Withbenim için de yeni :( Yine de mssql 2005 kullanıyorum.
dpp

6
@ domanokz: Partition By sayımı sıfırlar. Yani bu durumda, DocumentID başına sayım
söylenir

1
Hm, performanstan endişe ediyorum, milyonlarca satırı sorgulayacağım. SELECT * FROM (SELECT ...) performansı etkiler mi? Ayrıca, ROW_NUMBERher satır için bir tür alt sorgu mu?
dpp

1
@ domanokz: hayır, bu bir alt sorgu değil. Doğru dizinleriniz varsa, milyonlarca sorun olmamalı. Zaten sadece 2 set tabanlı yol var: bu ve agrega (Ariel'in çözümü). Yani ikisini de deneyin ...
gbn

1
@domanokz: Sadece ORDER BY DateCreated
DESC'i

184

Nasıl kullanacağımı öğrendim cross apply. Bu senaryoda nasıl kullanılacağı aşağıda açıklanmıştır:

 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
Sorun hala ele alındığı için bu aslında bir fark yaratmıyor.
dpp

19
Zamanlama testlerimin sonuçlarını tüm önerilen çözümlere karşı yayınladım ve sizinki en üste çıktı. Sana bir oy vermek :-)
John Fairbanks

3
Devasa hız artışı için +1. Bu, ROW_NUMBER () gibi bir pencereleme işlevinden çok daha hızlıdır. SQL, ROW_NUMBER () = 1 sorgusu gibi tanıyıp bunları Uygulamalı olarak optimize etseydi iyi olurdu. Not: Başvuruda bulunmasalar bile sonuçlara ihtiyacım olduğu için OUTER UYGULAMAYI kullandım.
TamusJRoyce

8
@TamusJRoyce, her zaman böyle olduğunda daha hızlı olduğu için bunu tahmin edemezsiniz. Değişir. Burada açıklandığı gibi sqlmag.com/database-development/optimizing-top-n-group-queries
Martin Smith

2
Benim yorumum birden çok satıra sahip olmak ve grup başına yalnızca bu birden çok satıra sahip olmak istiyor. Birleşimler istediğinizde birleşimler içindir. Bire çok var, ancak bire bir hariç tüm filtreleme yapmak için geçerlidir. Senaryo: 100 üye için, her birine en iyi telefon numaralarını verin (her birinin birkaç numaraya sahip olabileceği yerlerde). Bu, Uygulamanın mükemmel olduğu yerdir. Daha az okuma = daha az disk erişimi = daha iyi performans. Benim deneyimim kötü tasarlanmış normalleştirilmemiş veritabanları ile olduğu göz önüne alındığında.
TamusJRoyce

53

Burada çeşitli öneriler üzerinde bazı zamanlamalar yaptım ve sonuçlar gerçekten ilgili tablonun boyutuna bağlı, ancak en tutarlı çözüm ÇAPRAZ UYGULAMAYI kullanıyor Bu testler, bir tablo kullanarak SQL Server 2008-R2'ye karşı çalıştırıldı 6.500 kayıt ve 137 milyon kayıt içeren bir başka (özdeş şema). Sorgulanan sütunlar tablodaki birincil anahtarın bir parçasıdır ve tablo genişliği çok küçüktür (yaklaşık 30 bayt). Saatler, gerçek yürütme planından SQL Server tarafından rapor edilir.

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

Gerçekten şaşırtıcı olan şey, ilgili satır sayısına bakılmaksızın ÇAPRAZ UYGULAMA için zamanın ne kadar tutarlı olduğuydu.


8
Her şey veri dağıtımına ve kullanılabilir dizinlere bağlıdır. Dba.se'de büyük uzunluklarda tartışıldı .
Vladimir Baranov

48

Bu eski bir iplik olduğunu biliyorum ama TOP 1 WITH TIESçözümler oldukça güzel ve çözümler aracılığıyla bazı okuma için yararlı olabilir.

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

TOP maddesi hakkında daha fazla bilgiyi burada bulabilirsiniz .


7
Bu en zarif çözüm imo
George Menoutis

1
kabul - bu en iyi SQL ve diğer diller imo diğer sürümlerinde yapmak çok kolay çoğaltır
Chris Umphlett

27

Performans konusunda endişeleriniz varsa, bunu MAX () ile de yapabilirsiniz:

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

ROW_NUMBER (), SELECT ifadenizdeki tüm satırların bir türünü gerektirir, ancak MAX gerektirmez. Sorgunuzu büyük ölçüde hızlandırmalısınız.


2
ROW_NUMBER () ile ilgili performans sorunları uygun dizine ekleme ile giderilemiyor mu? (Yine de yapılması gerektiğini hissediyorum)
Kristoffer L

8
Datetime ile, aynı tarih ve saatte iki girişin eklenmeyeceğini garanti edemezsiniz. Hassasiyet yeterince yüksek değil.
TamusJRoyce

Basitlik için +1. @TamusJRoyce haklı. Ne dersin? 'DocumentStatusLog D'den * seçin * ID = (DocumentsStatusLog'dan ID seçin; burada D.DocumentID = DateCreated DESC limit 1'e göre DocumentID sırası);'
cibercitizen1

SELECT * FROM EventScheduleTbl D WHERE TarihlerNumarası = (SELECT top 1 dk. (DatesPicked) FROM EventScheduleTbl WHERE EventIDf = D.EventIDf ve DatesPicked> = dönüştürme (tarih, getdate ()))
Arun Prasad ES

Bunun row_number()uygun indeksleme ile bile daha iyi performans göstereceği durumlar kesinlikle vardır . Kendine katılma senaryolarında özellikle değerli buluyorum. Bununla birlikte, bilinmesi gereken şey, bu yöntemin, düşük bir alt ağaç maliyeti bildirmesine rağmen, genellikle daha fazla sayıda mantıksal okuma ve tarama sayısı vermesidir. Gerçekten daha iyi olup olmadığını belirlemek için özel durumunuzdaki maliyet / faydaları tartmanız gerekir.
pimbrouwers

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

Hangi veritabanı sunucusu? Bu kod hepsinde çalışmaz.

Sorunuzun ikinci yarısı ile ilgili olarak, durumu bir sütun olarak eklemek benim için makul görünüyor. AyrılabilirsinDocumentStatusLogsGünlük olarak , ancak en son bilgileri ana tabloda saklayabilirsiniz.

BTW, DateCreatedBelgeler tablosunda zaten sütun varsa bunu DocumentStatusLogskullanarak katılabilirsin ( DateCreatedbenzersiz olduğu sürece DocumentStatusLogs).

Düzenleme: MsSQL KULLANIMI desteklemez, bu yüzden değiştirin:

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

5
İpucu, MSSQL başlığındaydı. SQL Server KULLANMA yok ama fikir TAMAM.
gbn

7
@gbn Aptal moderatörler, burada yaptıkları gibi, genellikle önemli anahtar kelimeleri başlıklardan siler. Arama sonuçlarında veya Google'da doğru yanıtları bulmayı çok zorlaştırıyoruz.
NickG

2
Jus bir kravat varsa bu "çözüm" hala size birden fazla kayıt verebilir işaretmax(DateCreated)
MoonKnight

12

Bu konuda en kolay bulunan sorulardan biri, bu yüzden buna hem modern bir cevap vermek istedim (hem referansım için hem de başkalarına yardım etmek için). By kullanarak first_valueve overyukarıdaki sorgunun kısa çalışma yapabilirsiniz:

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

Bu, Sql Server 2008 ve sonraki sürümlerinde çalışmalıdır. bir madde kullanırken First_valuebaşarmanın bir yolu olarak düşünülebilir . seçme listesinde gruplandırmaya izin verir, böylece iç içe geçmiş alt sorgular yazmak yerine (varolan yanıtların çoğunun yaptığı gibi) bunu daha okunabilir bir şekilde yapar. Bu yardımcı olur umarım.Select Top 1overOver


2
Bu SQL Server 2008 R2'de çalışmaz. Bence first_value 2012 yılında tanıtıldı!
ufo

1
Çok hızlı! @Dpp tarafından sunulan Cross Apply çözümünü kullanıyordum, ama bu daha hızlı waaay.
MattSlay

11

Bu oldukça eski bir konu, ama kabul ettiğim cevabın benim için pek de işe yaramadığı gibi iki sentimi de atacağımı sanıyordum. Geniş bir veri kümesinde gbn çözümünü denedim ve çok yavaş olduğunu gördüm (SQL Server 2012'de 5 milyon artı kayıtta> 45 saniye). Yürütme planına bakıldığında, sorunun önemli ölçüde yavaşlatan bir SORT operasyonu gerektirdiği açıktır.

İşte SORT işlemi gerektirmeyen ve Kümelenmemiş Dizin araması yapan varlık çerçevesinden kaldırdığım bir alternatif. Bu, yukarıda belirtilen kayıt kümesinde yürütme süresini <2 saniyeye düşürür.

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]

Şimdi orijinal soruda tamamen belirtilmeyen bir şey olduğunu varsayıyorum, ancak tablo tasarımınız kimlik sütununuz otomatik olarak artan bir kimlik olacak ve DateCreated her ekleme ile geçerli tarihe ayarlanmışsa Yukarıdaki sorgumla çalıştırmadan, aslında aynı bir sıralama düzeni sunacağı ve daha hızlı bir sıralama olduğu için DateCreated yerine sipariş vermek yerine gbn çözümüne (yürütme süresinin yaklaşık yarısı) büyük bir performans artışı elde edebilirsiniz .


5

Her gruptan ilk 1'i seçmek için kodum

#DocumentStatusLogs a adresinden bir. * seçin 
 oluşturuldu (#DocumentStatusLogs b'den oluşturulmuş ilk 1'i seçin
nerede 
a.documentid = b.documentid
datecreated desc tarafından sipariş
)

3

Clint'in harika ve doğru cevabını yukarıdan doğrulamak:

Aşağıdaki iki sorgu arasındaki performans ilginçtir. % 52 en iyisi. Ve% 48 ikincisi. ORDER BY yerine DISTINCT kullanarak performansta% 4 iyileşme. Ancak ORDER BY birden çok sütuna göre sıralama avantajına sahiptir.

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')

Seçenek 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])

Seçenek 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]

M $ 'ın Management Studio: İlk bloğu vurgulayıp çalıştırdıktan sonra, Seçenek 1 ve Seçenek 2'yi vurgulayın, Sağ tıklayın -> [Tahmini Yürütme Planını Görüntüle]. Sonra sonuçları görmek için her şeyi çalıştırın.

1. Seçenek Sonuçları:

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. Seçenek Sonuçları:

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

Not:

Bir birleştirmenin 1'den-1'e (çoğunun 1'i) olmasını istediğimde UYGULAMA işlevini kullanma eğilimindeyim.

Birleşmenin 1'den çoğa veya çoktan çoğa olmasını istiyorsam bir JOIN kullanıyorum.

Gelişmiş bir şey yapmam ve pencereleme performansı cezası ile tamam olmadıkça ROW_NUMBER () ile CTE önlemek.

WHERE veya ON yan tümcesinde EXISTS / IN alt sorgularından da kaçınırım, çünkü bunu bazı korkunç yürütme planlarına neden olarak yaşadım. Ancak kilometre değişir. Gerektiğinde ve gerektiğinde yürütme planını ve profil performansını gözden geçirin!


3

Bu çözüm, her bölüm için TOP N en son satırları almak için kullanılabilir (örnekte, N WHERE deyiminde 1 ve bölüm doc_id'dir):

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 tarafından yalnızca son belge siparişini döndürmek istiyorsanız, DocumentID tarafından yalnızca ilk 1 belge döndürülür


2

CROSS APPLYbenim için ve müşterilerimin ihtiyaçları için çalıştığı için çözümüm için kullandığım yöntemdi. Ve okuduğum kadarıyla, veritabanları önemli ölçüde büyürse en iyi genel performansı sağlamalıdır.


1

İşte bu soruların her biri için en iyi indeksleme seçenekleri ile birlikte soruna 3 ayrı yaklaşım var (lütfen endeksleri kendiniz deneyin ve mantıksal okuma, geçen zaman, yürütme planını görün. söz konusu sorgular, bu belirli sorun için yürütülmeden).

Yaklaşım 1 : ROW_NUMBER () kullanımı. Satır deposu dizini performansı artıramıyorsa, kümelenmiş / kümelenmiş sütun deposu dizinini toplama ve gruplama içeren sorgular ve her zaman farklı sütunlarda sıralanan tablolar için deneyebilirsiniz, sütun deposu dizini genellikle en iyi seçimdir.

;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;

Yaklaşım 2 : FIRST_VALUE kullanma. Satır deposu dizini performansı artıramıyorsa, kümelenmiş / kümelenmiş sütun deposu dizinini toplama ve gruplama içeren sorgular ve her zaman farklı sütunlarda sıralanan tablolar için deneyebilirsiniz, sütun deposu dizini genellikle en iyi seçimdir.

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;

Yaklaşım 3 : ÇAPRAZ UYGULAMA Kullanın. Sorguda kullanılan sütunları kapsayan DocumentStatusLogs tablosunda satır mağaza dizini oluşturmak, bir sütun mağaza dizinine gerek kalmadan sorguyu kapsamak için yeterli olmalıdır.

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

Bunun böyle yapılabileceğine inanıyorum. Bunun için biraz ayar yapılması gerekebilir, ancak gruptan maksimum değeri seçebilirsiniz.

Bu cevaplar aşırı.

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 () kullanmaktan kaçınmak istediğiniz senaryolarda, sol birleşimi de kullanabilirsiniz:

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 

Örnek şema için, genellikle sol birleşimle aynı çıktıyı derleyen bir "alt sorguda değil" ifadesini de kullanabilirsiniz:

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)

Tablonun en az bir tek sütunlu benzersiz anahtar / kısıtlama / dizin, bu durumda birincil anahtar "Id" yoksa alt sorgu deseni çalışmaz.

Bu sorguların her ikisi de (Sorgu Çözümleyicisi tarafından ölçüldüğü gibi) row_count () sorgusundan daha "pahalı" olma eğilimindedir. Ancak, sonuçları daha hızlı döndürdükleri veya diğer optimizasyonları etkinleştirdikleri senaryolarla karşılaşabilirsiniz.


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

Bunu dene:

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]

SQL ifadenizi her zaman nasıl çalışacağını açıklamalı ve OP sorgusunu çözmelisiniz.
Suraj Kumar

-1

Bu gelebileceğim en vanilya 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

Ne yazık ki MaxDate benzersiz değil. Aynı tarihte iki tarih girilmesi mümkündür. Bu, grup başına yinelenmelere neden olabilir. Ancak, bir kimlik sütunu veya GUID kullanabilirsiniz. Kimlik Sütunu size girilen en son sütunu getirir (varsayılan kimlik hesaplaması kullanılır, 1 ... x adım 1).
TamusJRoyce

Kabul ediyorum, ancak yazar en son girişi istedi - bu, otomatik artan bir kimlik sütunu eklemezseniz, aynı anda eklenen iki öğenin eşit olarak 'en son' olduğu anlamına gelir
zengin s

En son kayıt bir kayıt olacaktır. Yani evet. Otomatik artan kimlik sütununu göz önünde bulundurmanız gerekir.
TamusJRoyce

-2

Aşağıdaki basit sorgu kullanabilmesi SQLite'ta kontrol edilir GROUP BY

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

Burada , her gruptan maksimum DateCreated elde etmek için MAX yardım .

Ancak, MYSQL *-sütunlarını max DateCreated :( ile ilişkilendirmiyor gibi görünüyor :(

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.