2010-07-02 10 views
13

Non è possibile (non dovrebbe) inserire non aggregati nella riga SELECT di una query GROUP BY.Posso utilizzare colonne non aggregate con gruppo per?

Vorrei comunque accedere a uno dei non aggregati associati al max. In parole povere, voglio un tavolo con l'id più vecchio di ogni tipo.

CREATE TABLE stuff (
    id int, 
    kind int, 
    age int 
); 

Questa interrogazione mi dà le informazioni che sto cercando:

SELECT kind, MAX(age) 
FROM stuff 
GROUP BY kind; 

ma non è nella forma più utile. Voglio davvero che lo id sia associato a ciascuna riga in modo da poterlo utilizzare nelle query successive.

Sto cercando qualcosa di simile:

SELECT id, kind, MAX(age) 
FROM stuff 
GROUP BY kind; 

Che Risulterà:

SELECT stuff.* 
FROM 
    stuff, 
    (SELECT kind, MAX(age) 
    FROM stuff 
    GROUP BY kind) maxes 
WHERE 
    stuff.kind = maxes.kind AND 
    stuff.age = maxes.age 

sembra davvero che ci dovrebbe essere lontano per ottenere queste informazioni senza la necessità di aderire. Ho solo bisogno del motore SQL per ricordare le altre colonne quando calcola il massimo.

+1

Abbastanza sicuro che si sta andando ad avere bisogno unirti per ottenere quello che vuoi. Sarebbe bello se ci fosse un modo per dire "tirare i valori da questa riga hai appena preso il massimo da" ma senza un join non è a mia conoscenza. – heisenberg

+2

Tuttavia, potrebbe non esserci un solo ID con l'età massima. Quale dovrebbe essere restituito? O dovresti prenderne uno per fila? – Blorgbeard

+0

O nel caso in cui si abbiano più aggregati che selezionano righe diverse, dovrebbe esserci una sorta di zucchero sintattico che consente di indicare quale aggregato ogni campo deve essere associato. – heisenberg

risposta

10

Non è possibile ottenere l'ID della riga trovata da MAX, perché potrebbe non esserci un solo ID con l'età massima.

1

È necessario disporre di un join perché la funzione di aggregazione massima recupera molte righe e sceglie il numero massimo. Quindi è necessario un join per scegliere quello che la funzione aggregata ha trovato.

Per dirla in altro modo come ti aspetteresti che la query si comporti se hai sostituito il massimo con la somma?

Un join interno potrebbe essere più efficiente della query secondaria.

4

Non è possibile (non dovrebbe) inserire non aggregati nella riga SELECT di una query GROUP BY.

È possibile, e necessario, definire cosa si sta raggruppando per la funzione di aggregazione per restituire il risultato corretto.

MySQL (e SQLite) hanno deciso nella loro infinita saggezza che sarebbero andati contro le specifiche e consentirebbero alle query di accettare le clausole GROUP BY che mancano le colonne citate in SELECT - rende effettivamente queste query non portabili.

Sembra davvero che ci dovrebbe essere via per ottenere queste informazioni senza dover aderire.

Senza l'accesso alle funzioni di analisi/classifica/windowing che MySQL non supporta, l'auto join a una tabella derivata/vista in linea è il mezzo più portatile per ottenere il risultato desiderato.

+0

Hai scritto: "Puoi, e devi, definire cosa stai raggruppando per la funzione di aggregazione per restituire il risultato corretto." Sembra che tu stia rispondendo all'inverso di ciò che @deft_code ha chiesto. Stava parlando di un caso in cui è stato definito GROUP BY ma non * non è aggregato *. – Quuxplusone

1

Penso che sia davvero allettante chiedere al sistema di risolvere il problema in un unico passaggio piuttosto che dover fare il lavoro due volte (trova il massimo e trova l'id corrispondente). Si può fare usando CONCAT (come suggerito in Naktibalda refered articolo), non sono sicuro che sarebbe più effeciant

SELECT MAX(CONCAT(LPAD(age, 10, '0'), '-', id) 
FROM STUFF1 
GROUP BY kind; 

dovrebbe funzionare, è necessario dividere la risposta per ottenere l'età e l'id. (È davvero brutto)

+0

il '-' è di aggiungere un separatore tra età e id, non una sottrazione – mb14

2

Nei database recenti e 'possibile utilizzare sum() su (parition da ...) per risolvere questo problema:

select id, kind, age as max_age from (
    select id, kind, age, max(age) over (partition by kind) as mage 
    from table) 
where age = mage 

Questo può quindi essere unico passaggio

Problemi correlati