Ottieni la prima riga di ogni gruppo


530

Ho una tabella che desidero ottenere l'ultima voce per ciascun gruppo. Ecco la tabella:

DocumentStatusLogs tavolo

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

La tabella verrà raggruppata DocumentIDe ordinata DateCreatedin ordine decrescente. Per ciascuno DocumentID, voglio ottenere lo stato più recente.

La mia uscita preferita:

| DocumentID | Status | DateCreated |
| 1          | S1     | 8/02/2011   |
| 2          | S3     | 8/01/2011   |
| 3          | S1     | 8/02/2011   |
  • Esiste una funzione aggregata per ottenere solo il massimo da ciascun gruppo? Vedi pseudo-codice di GetOnlyTheTopseguito:

    SELECT
      DocumentID,
      GetOnlyTheTop(Status),
      GetOnlyTheTop(DateCreated)
    FROM DocumentStatusLogs
    GROUP BY DocumentID
    ORDER BY DateCreated DESC
    
  • Se tale funzione non esiste, è possibile ottenere l'output desiderato?

  • O in primo luogo, ciò potrebbe essere causato da un database non normalizzato? Sto pensando, poiché quello che sto cercando è solo una riga, dovrebbe statustrovarsi anche nella tabella padre?

Per ulteriori informazioni, consultare la tabella principale:

DocumentsTabella corrente

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

La tabella padre dovrebbe essere così in modo che io possa accedere facilmente al suo stato?

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

AGGIORNAMENTO Ho appena imparato a usare "applica" che semplifica la risoluzione di tali problemi.


2
Per una discussione e un confronto più dettagliati delle possibili soluzioni, consiglio di leggere la domanda simile su dba.se: Recupero di n righe per gruppo .
Vladimir Baranov,

Ho guardato il post e l'ho provato. L'uso del gruppo da StoreID ha generato un errore.
UltraJ

Risposte:


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

Se ti aspetti 2 voci al giorno, questa sceglierà arbitrariamente una. Per ottenere entrambe le voci per un giorno, utilizzare invece DENSE_RANK

Quanto a normalizzato o no, dipende se si desidera:

  • mantenere lo stato in 2 posti
  • preservare la cronologia dello stato
  • ...

Allo stato attuale, si conserva la cronologia dello stato. Se si desidera anche lo stato più recente nella tabella padre (che è denormalizzazione), è necessario un trigger per mantenere lo "stato" nel padre. oppure rilasciare questa tabella della cronologia dello stato.


5
E ... che cos'è Partition By? Withè nuovo anche per me :( Sto usando mssql 2005 comunque.
dpp,

6
@domanokz: Partition By ripristina il conteggio. Quindi, in questo caso, dice di contare per DocumentID
gbn

1
Hm, mi preoccupo delle prestazioni, interrogherò milioni di righe. SELECT * FROM (SELECT ...) influisce sulle prestazioni? Inoltre, c'è ROW_NUMBERuna specie di sottoquery per ogni riga?
dpp,

1
@domanokz: no, non è una sottoquery. Se hai indici corretti, milioni non dovrebbero essere un problema. Ci sono comunque solo 2 modi basati su set: questo e l'aggregato (la soluzione di Ariel). Quindi provali entrambi ...
gbn,

1
@domanokz: basta cambiare ORDER BY DateCreated DESC in ORDER BY ID DESC
gbn

184

Ho appena imparato a usare cross apply. Ecco come usarlo in questo scenario:

 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
Questo in realtà non fa differenza poiché il problema è ancora risolto.
dpp,

19
Ho appena pubblicato i risultati dei miei test di cronometraggio rispetto a tutte le soluzioni proposte e le vostre sono risultate al top. Ti dà un voto positivo :-)
John Fairbanks

3
+1 per un enorme miglioramento della velocità. Questo è molto più veloce di una funzione di windowing come ROW_NUMBER (). Sarebbe bello se SQL riconoscesse ROW_NUMBER () = 1 come query e le ottimizzasse in Applies. Nota: ho usato OUTER APPLY perché avevo bisogno di risultati, anche se non esistevano nell'applicazione.
TamusJRoyce,

8
@TamusJRoyce non puoi estrapolarlo solo perché era più veloce una volta che è sempre così. Dipende. Come descritto qui sqlmag.com/database-development/optimizing-top-n-group-queries
Martin Smith

2
Il mio commento riguarda avere più righe e desiderare solo una di quelle più righe per gruppo. Le iscrizioni sono per quando ne vuoi uno a molti. Le domande valgono per quando ne hai una o più, ma vuoi filtrare tutto tranne uno a uno. Scenario: per 100 membri, dammi a ciascuno il loro miglior numero di telefono (dove ognuno potrebbe avere più numeri). Questo è dove applicare eccelle. Meno letture = meno accesso al disco = prestazioni migliori. Data la mia esperienza è con database non normalizzati mal progettati.
TamusJRoyce,

53

Ho fatto alcune tempistiche sui vari consigli qui, e i risultati dipendono molto dalle dimensioni della tabella coinvolta, ma la soluzione più coerente è usare CROSS APPLY Questi test sono stati eseguiti su SQL Server 2008-R2, usando una tabella con 6.500 record e un altro (schema identico) con 137 milioni di record. Le colonne interrogate fanno parte della chiave primaria della tabella e la larghezza della tabella è molto piccola (circa 30 byte). I tempi sono riportati da SQL Server dal piano di esecuzione effettivo.

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

Penso che la cosa davvero sorprendente sia stata la coerenza dei tempi per il CROSS APPLY indipendentemente dal numero di righe coinvolte.


8
Tutto dipende dalla distribuzione dei dati e dagli indici disponibili. Si è discusso a grandi lunghezze su dba.se .
Vladimir Baranov,

48

So che questo è un vecchio thread, ma le TOP 1 WITH TIESsoluzioni sono piuttosto carine e potrebbero essere utili per leggere alcune soluzioni.

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

Maggiori informazioni sulla clausola TOP sono disponibili qui .


7
Questa è la soluzione più elegante imo
George Menoutis,

1
concordato - questo meglio replica ciò che è molto facile da fare in altre versioni di SQL e altre lingue imo
Chris Umphlett,

27

Se sei preoccupato per le prestazioni, puoi farlo anche con MAX ():

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

ROW_NUMBER () richiede una sorta di tutte le righe nell'istruzione SELECT, mentre MAX no. Dovrebbe accelerare drasticamente la tua query.


2
Non è possibile risolvere i problemi di prestazioni con ROW_NUMBER () con l'indicizzazione corretta? (Sento che dovrebbe essere fatto comunque)
Kristoffer L

8
Con datetime, non è possibile garantire che due voci non vengano aggiunte nella stessa data e ora. La precisione non è abbastanza elevata.
TamusJRoyce,

+1 per semplicità. @TamusJRoyce ha ragione. Che dire? 'seleziona * da DocumentStatusLog D dove ID = (seleziona ID da DocumentsStatusLog dove D.DocumentID = DocumentID ordina per DateCreated DESC limite 1);'
cibercitizen1,

SELEZIONA * DA EventScheduleTbl D DOVE DatesPicked = (SELEZIONA top 1 min (DatePicked) DA EventScheduleTbl DOVE EventIDf = D.EventIDf e DatesPicked> = convert (date, getdate ()))
Arun Prasad ES

Ci sono sicuramente casi in cui questo supererà row_number()anche con una corretta indicizzazione. Lo trovo particolarmente utile negli scenari di auto-join. La cosa di cui bisogna essere consapevoli, tuttavia, è che questo metodo produrrà spesso un numero più elevato di letture logiche e conteggi di scansioni, nonostante la segnalazione di un costo di sottostruttura basso. Dovrai valutare i costi / benefici nel tuo caso particolare per determinare se è effettivamente migliore.
pimbrouwers,

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

Quale server di database? Questo codice non funziona su tutti.

Per quanto riguarda la seconda metà della domanda, mi sembra ragionevole includere lo stato come colonna. Puoi lasciare DocumentStatusLogsun registro, ma conservare comunque le informazioni più recenti nella tabella principale.

A proposito, se hai già la DateCreatedcolonna nella tabella Documenti puoi semplicemente unirti DocumentStatusLogsusando quello (purché DateCreatedsia univoco in DocumentStatusLogs).

Modifica: MsSQL non supporta USARE, quindi cambiarlo in:

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

5
L'indizio era nel titolo: MSSQL. SQL Server non ha USING ma l'idea è OK.
gbn,

7
@gbn Gli stupidi moderatori di solito eliminano parole chiave importanti dai titoli, come hanno fatto qui. Rendere molto difficile trovare le risposte corrette nei risultati di ricerca o in Google.
NickG,

2
Jus per sottolineare che questa "soluzione" può ancora darti più record se hai un pareggio sulmax(DateCreated)
MoonKnight

12

Questa è una delle domande più facilmente reperibili sull'argomento, quindi ho voluto dare una risposta moderna ad esso (sia per il mio riferimento che per aiutare gli altri). Usando first_valuee overpuoi fare brevemente la query sopra:

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

Questo dovrebbe funzionare in SQL Server 2008 e versioni successive. First_valuepuò essere pensato come un modo per realizzare Select Top 1quando si usa una overclausola. Overconsente il raggruppamento nell'elenco di selezione, quindi invece di scrivere sottoquery nidificate (come fanno molte delle risposte esistenti), lo fa in modo più leggibile. Spero che sia di aiuto.


2
Questo non funziona in SQL Server 2008 R2. Penso che first_value sia stato introdotto nel 2012!
ufo

1
Molto veloce! Stavo usando la soluzione Cross Apply offerta da @dpp, ma questa è molto più veloce.
MattSlay

11

Questo è piuttosto un vecchio thread, ma ho pensato di buttare i miei due centesimi nello stesso modo in cui la risposta accettata non funzionava particolarmente bene per me. Ho provato la soluzione di gbn su un set di dati di grandi dimensioni e l'ho trovata terribilmente lenta (> 45 secondi su oltre 5 milioni di record in SQL Server 2012). Guardando il piano di esecuzione è ovvio che il problema è che richiede un'operazione SORT che rallenta le cose in modo significativo.

Ecco un'alternativa che ho estratto dal framework di entità che non ha bisogno di operazioni SORT e fa una ricerca dell'indice NON cluster. Ciò riduce il tempo di esecuzione fino a <2 secondi sul set di record di cui sopra.

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]

Ora sto assumendo qualcosa che non è completamente specificato nella domanda originale, ma se il design della tabella è tale che la colonna ID è un ID con incremento automatico e DateCreated è impostato sulla data corrente con ogni inserto, allora anche senza eseguire la mia query sopra potresti effettivamente ottenere un notevole aumento delle prestazioni della soluzione di gbn (circa la metà del tempo di esecuzione) solo dall'ordinare su ID invece che su DateCreated in quanto fornirà un ordinamento identico ed è un ordinamento più veloce.


5

Il mio codice per selezionare i primi 1 di ciascun gruppo

selezionare un. * da #DocumentStatusLogs un dove 
 datecreated in (seleziona i primi 1 datecreated da #DocumentStatusLogs b
dove 
a.documentid = b.documentid
ordina per descrizione data
)

3

Verifica la risposta fantastica e corretta di Clint dall'alto:

Le prestazioni tra le due query seguenti sono interessanti. Il 52% è il primo. E il 48% è il secondo. Un miglioramento del 4% delle prestazioni utilizzando DISTINCT anziché ORDER BY. Ma ORDER BY ha il vantaggio di ordinare per più colonne.

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

Opzione 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])

Opzione 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 $'s Management Studio: dopo aver evidenziato ed eseguito il primo blocco, evidenziare sia Opzione 1 che Opzione 2, fare clic con il pulsante destro del mouse -> [Visualizza piano di esecuzione stimato]. Quindi esegui l'intera cosa per vedere i risultati.

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

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

Nota:

Tendo a usare APPLY quando voglio che un join sia 1-a- (1 di molti).

Uso un JOIN se voglio che il join sia 1-a-molti o molti-a-molti.

Evito CTE con ROW_NUMBER () a meno che non debba fare qualcosa di avanzato e non accetto la penalità per le prestazioni del finestrino.

Evito anche le sottoquery EXISTS / IN nella clausola WHERE o ON, poiché ho riscontrato questo causando alcuni piani di esecuzione terribili. Ma il chilometraggio varia. Rivedi il piano di esecuzione e le prestazioni del profilo dove e quando necessario!


3

Questa soluzione può essere utilizzata per ottenere le TOP N righe più recenti per ogni partizione (nell'esempio, N è 1 nell'istruzione WHERE e la partizione è 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 ;

Se si desidera restituire solo gli ordini di documenti recenti di DateCreated, verrà restituito solo 1 documento in cima a DocumentID


2

CROSS APPLYera il metodo che ho usato per la mia soluzione, poiché ha funzionato per me e per le esigenze dei miei clienti. E da quello che ho letto, dovrebbe fornire le migliori prestazioni complessive se il loro database dovesse crescere in modo sostanziale.


1

Qui ci sono 3 approcci separati al problema in mano insieme alle migliori scelte di indicizzazione per ognuna di quelle query (prova tu stesso gli indici e vedi la lettura logica, il tempo trascorso, il piano di esecuzione. Ho fornito i suggerimenti della mia esperienza su tali query senza eseguire questo problema specifico).

Approccio 1 : utilizzo di ROW_NUMBER (). Se l'indice rowstore non è in grado di migliorare le prestazioni, è possibile provare l'indice columnstore non cluster / cluster come per le query con aggregazione e raggruppamento e per le tabelle ordinate sempre in colonne diverse, l'indice columnstore di solito è la scelta migliore.

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

Approccio 2 : utilizzo di FIRST_VALUE. Se l'indice rowstore non è in grado di migliorare le prestazioni, è possibile provare l'indice columnstore non cluster / cluster come per le query con aggregazione e raggruppamento e per le tabelle ordinate sempre in colonne diverse, l'indice columnstore di solito è la scelta migliore.

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;

Approccio 3 : utilizzo CROSS APPLY. La creazione di un indice rowstore sulla tabella DocumentStatusLogs che copre le colonne utilizzate nella query dovrebbe essere sufficiente per coprire la query senza la necessità di un indice columnstore.

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

Credo che questo possa essere fatto proprio così. Questo potrebbe richiedere qualche ritocco, ma puoi semplicemente selezionare il massimo dal gruppo.

Queste risposte sono eccessive.

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

Negli scenari in cui si desidera evitare l'utilizzo di row_count (), è anche possibile utilizzare un join sinistro:

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 

Per lo schema di esempio, è possibile utilizzare anche un "non nella sottoquery", che generalmente viene compilato nello stesso output del join sinistro:

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)

Nota, il modello di subquery non funzionerebbe se la tabella non avesse almeno una chiave / vincolo / indice univoci a colonna singola, in questo caso la chiave primaria "Id".

Entrambe queste query tendono ad essere più "costose" della query row_count () (misurata da Query Analyzer). Tuttavia, è possibile che si verifichino scenari in cui restituiscono risultati più rapidamente o abilitano altre ottimizzazioni.


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

Prova questo:

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]

Dovresti sempre descrivere la tua istruzione SQL su come funzionerà e risolvere la query del PO.
Suraj Kumar

-1

Questo è il TSQL più vanigliato che posso inventare

    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

Purtroppo MaxDate non è unico. È possibile inserire due date contemporaneamente. Quindi questo può comportare duplicati per gruppo. Tuttavia, è possibile utilizzare una colonna di identità o un GUID. La colonna Identità ti procurerà l'ultimo immesso (viene utilizzato il calcolo dell'identità predefinito, 1 ... x passaggio 1).
TamusJRoyce,

Beh, sono abbastanza d'accordo, ma l'autore ha chiesto l'ultima voce, che a meno che tu non includa una colonna di identità con incremento automatico, significa che due elementi aggiunti esattamente nello stesso momento sono ugualmente "gli ultimi"
rich s

L'ultimo record sarà un record. Quindi sì. È necessario considerare la colonna dell'identità con incremento automatico.
TamusJRoyce,

-2

In SQLite è verificato che è possibile utilizzare la seguente semplice query con GROUP BY

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

Qui MAX aiuta a ottenere il massimo DateCreated DA ciascun gruppo.

Ma sembra che MYSQL non associ * -columns al valore di max DateCreated :(

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.