2012-11-19 13 views
13

La Tavola:php mysql Raggruppa per arrivare ultimo disco, non primo disco

(`post_id`, `forum_id`, `topic_id`, `post_time`) 
(79, 8, 4, '2012-11-19 06:58:08'); 
(80, 3, 3, '2012-11-19 06:58:42'), 
(81, 9, 9, '2012-11-19 06:59:04'), 
(82, 11, 6, '2012-11-19 16:05:39'), 
(83, 9, 9, '2012-11-19 16:07:46'), 
(84, 9, 11, '2012-11-19 16:09:33'), 

La Query:

SELECT post_id, forum_id, topic_id FROM posts 
GROUP BY topic_id 
ORDER BY post_time DESC 
LIMIT 5 

I risultati:

[0] => [post_id] => 84 [forum_id] => 9 [topic_id] => 11 
[1] => [post_id] => 82 [forum_id] => 11 [topic_id] => 6 
[2] => [post_id] => 81 [forum_id] => 9 [topic_id] => 9 
[3] => [post_id] => 80 [forum_id] => 3 [topic_id] => 3 
[4] => [post_id] => 79 [forum_id] => 8 [topic_id] => 4 

Il problema:

Come riscrivere la query in modo che io t restituisce post_id -> 83 invece di post_id -> 81?

Entrambi hanno le stesse ids forum o del topic, ma post_id -> 81 ha una data più vecchio di post_id -> 83.

Ma sembra che Raggruppa ottiene il 'prima' di registrazione e non il 'più recente ' uno.

Ho provato a cambiare la query per

SELECT post_id, forum_id, topic_id, MAX(post_time) 

ma che restituisce sia post_id 81 e 83

risposta

24

Se si selezionano attributi non utilizzati nella clausola di gruppo e non sono aggregati, il risultato non è specificato. I.e non si conosce da quali righe sono selezionati gli altri attributi. (Lo standard sql non consente tali query, ma MySQL è più rilassato).

La query deve quindi essere scritta ad es. come

SELECT post_id, forum_id, topic_id 
FROM posts p 
WHERE post_time = 
    (SELECT max(post_time) FROM posts p2 
    WHERE p2.topic_id = p.topic_id 
    AND p2.forum_id = p.forum_id) 
GROUP BY forum_id, topic_id, post_id 
ORDER BY post_time DESC 
LIMIT 5; 

o

SELECT post_id, forum_id, topic_id FROM posts 
NATURAL JOIN 
(SELECT forum_id, topic_id, max(post_time) AS post_time 
FROM posts 
GROUP BY forum_id, topic_id) p 
ORDER BY post_time 
LIMIT 5; 
+0

Grazie per la spiegazione e il codice. Entrambe le query hanno funzionato. – shanebp

+0

Ho usato la prima query, ma non mi sembra che abbia funzionato prima di cambiare ... WHERE post_time = ... TO ... WHERE post_time IN ... perché ho ottenuto più di un risultato indietro. –

+0

Perché è una tale seccatura? usando SQL sarebbe semplicemente 'SELECT * FROM [table] GROUP BY [column]' che seleziona il più recente invece che il più vecchio, sembra logico vero? –

10

Non è molto bello, ma funziona:

SELECT * FROM (SELECT post_id, forum_id, topic_id FROM posts 
ORDER BY post_time DESC) as temp 
GROUP BY topic_id 
+0

Questo rimuove i duplicati, ma i risultati sono in ordine di argomento id asc – shanebp

+2

L'aggiunta di questo alla tua richiesta ha reso il lavoro: ORDER BY DESC post_time LIMIT 5 – shanebp

5

provare qualcosa di simile

SELECT post_id, forum_id, topic_id 
FROM (SELECT post_id, forum_id, topic_id 
     FROM posts 
     ORDER BY post_time DESC) 
GROUP BY topic_id 
ORDER BY topic_id desc 
LIMIT 0,5 

cambia il order by e limit secondo necessità.

+0

Il risultato è un array vuoto. – shanebp

+0

Ho modificato leggermente la query. Non capisco perché non funzionerebbe. Non l'ho provato, ma è una tecnica comune. query di nidificazione che è. partendo dal presupposto che la query interna restituisca il risultato giusto, la query esterna dovrebbe dare un risultato. – NappingRabbit

+0

se si desidera eseguire il debug, eseguire prima la query interna. – NappingRabbit

0
non

Forse il miglior modo di farlo, ma a volte la group_concat funzione di () può essere userfull, restituirà una stringa di tutti i valori aggregati ordinati come si vuole e separati da virgola (i valori accoppiati sono separati dallo spazio). Quindi utilizzo la funzione SPLIT_STRING() per tagliare il primo id nella stringa.

SELECT 
post_id, 
SPLIT_STRING(group_concat(forum_id, post_time ORDER BY post_time DESC) ,' ',1)as forum_id, 
SPLIT_STRING(group_concat(topic_id, post_time ORDER BY post_time DESC) ,' ',1)as topic_id , 
FROM posts 
GROUP BY topic_id 
ORDER BY post_time DESC 
LIMIT 5 

Quindi la forum_id aggregato, post_time sarà così:

81 2012-11-19 06: 59: 04,83 2012-11-19 16:07:46

quindi è necessario lavorare con una rappresentazione di stringa di numeri interi e coppie datetime, ciascuno coppie separate da una virgola quindi ho usato questa funzione per ottenere il primo INT:

CREATE FUNCTION SPLIT_STRING(str VARCHAR(255), delim VARCHAR(12), pos INT) 
RETURNS VARCHAR(255) 
RETURN REPLACE(SUBSTRING(SUBSTRING_INDEX(str, delim, pos), 
     LENGTH(SUBSTRING_INDEX(str, delim, pos-1)) + 1), 
     delim, ''); 

Nota: la funzione SPLIT_STRING (str, delim, pos) è stata trovata qui: Equivalent of explode() to work with strings in MySQL

0

Anche questo funzionerà correttamente.

SELECT * 
FROM (
    SELECT post_id, forum_id, topic_id FROM posts 
    ORDER BY post_time DESC 
    LIMIT 5 
) customeTable 
GROUP BY topic_id