MySQL “gruppo da” e “Order By”
-
21-08-2019 - |
Domanda
Voglio essere in grado di selezionare un gruppo di righe da una tabella di e-mail e di gruppo dal dal mittente. La mia domanda è simile al seguente:
SELECT
`timestamp`, `fromEmail`, `subject`
FROM `incomingEmails`
GROUP BY LOWER(`fromEmail`)
ORDER BY `timestamp` DESC
La query quasi funziona come voglio - è seleziona i record raggruppati per e-mail. Il problema è che il soggetto e timestamp non corrispondono al record più recente per un particolare indirizzo di posta elettronica.
Per esempio, si potrebbe restituire:
fromEmail: john@example.com, subject: hello
fromEmail: mark@example.com, subject: welcome
Quando i record del database sono:
fromEmail: john@example.com, subject: hello
fromEmail: john@example.com, subject: programming question
fromEmail: mark@example.com, subject: welcome
Se la "questione di programmazione" soggetto è la più recente, come posso ottenere MySQL per selezionare il record quando si raggruppa le e-mail?
Soluzione
Una soluzione semplice è quella di avvolgere la query in un sub-SELECT con l'istruzione ORDER prima e applicando il GROUP BY dopo :
SELECT * FROM (
SELECT `timestamp`, `fromEmail`, `subject`
FROM `incomingEmails`
ORDER BY `timestamp` DESC
) AS tmp_table GROUP BY LOWER(`fromEmail`)
Questo è simile ad usare il join, ma sembra molto più bello.
Utilizzando le colonne non di aggregazione in un SELECT con una clausola GROUP BY non è standard. MySQL generalmente restituire i valori della prima fila si trova e scartare il resto. Qualsiasi clausole ORDER BY sarà valido solo per il valore della colonna restituito, non a quelli scartati.
AGGIORNAMENTO IMPORTANTE Selezione delle colonne non di aggregazione utilizzati per lavorare in pratica, ma non dovrebbe essere invocata. Per il MySQL documentazione "questo è utile in primo luogo quando tutti i valori in ogni colonna nonaggregated non nominato nel GROUP BY sono gli stessi per ogni gruppo. il server è libero di scegliere qualsiasi valore di ciascun gruppo, in modo da a meno che non sono la stessa cosa, la I valori scelti sono indeterminati ."
A partire dal 5.6.21 ho notato problemi con GROUP BY nella tabella temporanea ritornando ORDER BY di smistamento.
5.7.5 ONLY_FULL_GROUP_BY è abilitato per default, cioè non è possibile utilizzare le colonne non di aggregazione.
Vedere http://www.cafewebmaster.com/mysql-order-sort-group https://dev.mysql.com/doc/refman /5.6/en/group-by-handling.html https://dev.mysql.com/doc/refman /5.7/en/group-by-handling.html
Altri suggerimenti
Ecco un approccio:
SELECT cur.textID, cur.fromEmail, cur.subject,
cur.timestamp, cur.read
FROM incomingEmails cur
LEFT JOIN incomingEmails next
on cur.fromEmail = next.fromEmail
and cur.timestamp < next.timestamp
WHERE next.timestamp is null
and cur.toUserID = '$userID'
ORDER BY LOWER(cur.fromEmail)
In sostanza, si uniscono al tavolo su se stesso, alla ricerca per le righe successive. Nella clausola in cui si affermi che non ci può essere righe successive. Questo vi dà solo l'ultimo di riga.
Se non ci può essere più e-mail con la stessa data e ora, questa query avrebbe bisogno di raffinazione. Se c'è una colonna ID incrementale nella tabella posta elettronica, modificare il join come:
LEFT JOIN incomingEmails next
on cur.fromEmail = next.fromEmail
and cur.id < next.id
Fare un GROUP BY dopo ORDER BY avvolgendo la query con GROUP BY in questo modo:
SELECT t.* FROM (SELECT * FROM table ORDER BY time DESC) t GROUP BY t.from
Come sottolineato in una risposta già, la risposta corrente è sbagliata, perché il GROUP BY seleziona arbitrariamente il record dalla finestra.
Se si sta usando MySQL 5.6, MySQL 5.7 o con ONLY_FULL_GROUP_BY
, il corretto (deterministico) di query è:
SELECT incomingEmails.*
FROM (
SELECT fromEmail, MAX(timestamp) `timestamp`
FROM incomingEmails
GROUP BY fromEmail
) filtered_incomingEmails
JOIN incomingEmails USING (fromEmail, timestamp)
GROUP BY fromEmail, timestamp
Affinché la query da eseguire in modo efficiente, è necessario corretta indicizzazione.
Si noti che a fini di semplificazione, ho rimosso il LOWER()
, che nella maggior parte dei casi, non saranno utilizzati.
Secondo lo standard SQL non è possibile utilizzare le colonne non di aggregazione nell'elenco di selezione. MySQL permette tale uso (uless modalità ONLY_FULL_GROUP_BY utilizzato), ma risultato non è prevedibile.
Si dovrebbe prima selezionare fromEmail, MIN (leggi), e poi, con la seconda query (o subquery) -. Soggetto
Ho lottato con entrambi questi approcci per le query più complesse rispetto a quelli mostrati, perché l'approccio sottoquery era orribilmente ineficient non importa quello che ho messo su indici, e perché non ho potuto ottenere l'esterno self-join tramite Hibernate
Il meglio (e più facile) modo per farlo è quello di gruppo da qualcosa che è costruito per contenere una concatenazione dei campi richiesti e poi per tirare fuori utilizzando espressioni nella clausola SELECT. Se avete bisogno di fare un MAX () fare in modo che il campo che si desidera MAX () sopra è sempre alla fine più importante del soggetto concatenati.
La chiave di lettura di questo è che la query può senso solo se questi altri campi sono invarianti per qualsiasi entità che soddisfa il Max (), quindi in termini di genere gli altri pezzi di concatenazione può essere ignorato. Spiega come fare questo al fondo di questo legame. http://dev.mysql.com/doc /refman/5.0/en/group-by-hidden-columns.html
Se è possibile ottenere am inserto / evento di aggiornamento (come un trigger) per pre-calcolare la concatenazione dei campi è possibile indicizzarlo e la query sarà veloce come se il gruppo da era finito solo il campo che in realtà voleva MAX (). Si può anche utilizzare per ottenere il massimo di più campi. Io lo uso per fare query contro gli alberi multidimensionali expresssed come insiemi annidati.