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?

È stato utile?

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.

ONLY_FULL_GROUP_BY

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.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top