2011-01-02 16 views
9

Ho una tabellaMySQL LEFT JOIN, GROUP BY e ORDER BY non funziona come richiesto

'products' => ('product_id', 'name', 'description') 

e un tavolo

'product_price' => ('product_price_id', 'product_id', 'price', 'date_updated') 

voglio eseguire un qualcosa di domanda come

SELECT `p`.*, `pp`.`price` 
FROM `products` `p` 
LEFT JOIN `product_price` `pp` ON `pp`.`product_id` = `p`.`product_id` 
GROUP BY `p`.`product_id` 
ORDER BY `pp`.`date_updated` DESC 

Come si può probabilmente intuire il cambiamento dei prezzi spesso e ho bisogno di tirare fuori l'ultimo. Il problema è che non riesco a capire come ordinare il tavolo LEFT JOINed. Ho provato ad utilizzare alcune delle funzioni di GROUP BY come MAX(), ma questo avrebbe tirato fuori solo la colonna e non la riga.

Grazie.

+0

si può spiegare che cosa vorreste per accadere? Vuoi ordinare dal massimo del gruppo? Vuoi partecipare solo con l'ultimo aggiornamento? Forse alcuni dati di esempio e l'output previsto per tali dati potrebbero aiutare a chiarire la tua domanda. –

+0

Perché non unirsi interiore? –

+0

Ho un prodotto con 5 prezzi diversi, con datetimes diversi 'date_updated'. Ho bisogno di unire e tirare fuori l'ultimo prezzo. – Simon

risposta

15

sembra che sia impossibile utilizzare un ORDINE BY in una ricapitolazione GROUP BY. La mia logica fondamentale è imperfetta. Avrò bisogno di eseguire la seguente subquery.

SELECT `p`.*, `pp`.`price` FROM `products` `p` 
LEFT JOIN (
    SELECT `price` FROM `product_price` ORDER BY `date_updated` DESC 
) `pp` 
ON `p`.`product_id` = `pp`.`product_id` 
GROUP BY `p`.`product_id`; 

questo richiederà un calo di prestazioni, ma in quanto è lo stesso subquery per ogni riga non dovrebbe essere troppo male.

+0

Ho fatto una domanda simile proprio ora e ho ricevuto questa risposta da @Gordon Linoff http://stackoverflow.com/a/18763815/570796 – superhero

1

è necessario impostare alias correttamente penso e anche impostare quello che si stanno unendo a:

SELECT p.*, pp.price 
FROM products AS p 
LEFT JOIN product_price AS pp 
ON pp.product_id = p.product_id 
GROUP BY p.product_id 
ORDER BY pp.date_updated DESC 
+0

Grazie. Ho perso la parte ON. Ho modificato la mia richiesta. Credo che la parte AS sia opzionale. – Simon

-1

Questo vi darà l'ultimo prezzo aggiornamento:

select 
    p.*, pp.price 
from 
    products p, 
    -- left join this if products may not have an entry in prodcuts_price 
    -- and you would like to see a null price with the product 
    join 
    (
     select 
     product_price_id, 
     max(date_updated) 
     from products_price 
     group by product_price_id 
    ) as pp_max 
    on p.product_id = pp.product_id 
    join products_price pp on 
    pp_max.prodcuts_price_id = pp.products_price_id 
+0

Probabilmente funzionerebbe comunque, ma mi piacerebbe davvero evitare di eseguire una sottoquery poiché ho un grande set di dati. – Simon

+0

Questo è il modo in cui dà risultati corretti. Altre domande forniranno ** tutti i ** prezzi. Ma hey se funzionano più velocemente basta usare quello giusto ...? @! –

+0

Apprezzo il tuo aiuto ma la mia domanda non fornisce tutti i prezzi, mi dà solo un prezzo casuale che spero di risolvere ordinando correttamente. Il gruppo – Simon

-2

Mysqlism:

SELECT p.*, MAX(pp.date_updated), pp.price 
FROM products p 
LEFT JOIN product_price pp ON pp.product_id = p.product_id 
GROUP BY p.product_id 

Lavoreranno su alcuni RDBMS:

SELECT p.*, pp.date_updated, pp.price 
FROM products p 
LEFT JOIN product_price pp ON pp.product_id = p.product_id 
WHERE (p.product_id, pp.date_updated) 

    in (select product_id, max(date_updated) 
     from product_price 
     group by product_id) 

lavorerà sulla maggior parte dei RDBMS:

SELECT p.*, pp.date_updated, pp.price 
FROM products p 
LEFT JOIN product_price pp ON pp.product_id = p.product_id 

WHERE EXISTS 
(
    select null -- inspired by Linq-to-SQL style :-) 
    from product_price 

    WHERE product_id = p.product_id 

    group by product_id 

    HAVING max(date_updated) = pp.date_updated 
) 

funziona su tutti i RDBMS:

SELECT p.*, pp.date_updated, pp.price 
FROM products p 
LEFT JOIN product_price pp ON pp.product_id = p.product_id 

LEFT JOIN 
(
    select product_id, max(date_updated) as recent 
    from product_price 
    group by product_id 
) AS latest 
ON latest.product_id = p.product_id AND latest.recent = pp.date_updated 

E se l'intento del codice nate c è di ottenere solo una riga da product_price, non è necessario derivante dalla tabella (ad es. join (select product_price_id, max(date_updated) from products_price) as pp_max), si potrebbe anche solo di semplificare (cioè senza necessità di utilizzare la chiave primaria surrogata product_price_id) è simile al seguente:.

SELECT p.*, pp.date_updated, pp.price 
FROM products p 
LEFT JOIN product_price pp ON pp.product_id = p.product_id 
WHERE pp.date_updated = (select max(date_updated) from product_price) 
+0

Il tuo Mysqlism non funziona. Questo è il punto centrale di questo post. Il prezzo selezionato non ** deve ** sebbene possa scegliere il prezzo che coincide con la data massima. http://dev.mysql.com/doc/refman/5.5/en/group-by-hidden-columns.html –

+0

L'ultima query non ha nulla a che fare con il mio intento. –

+0

Questo è il punto, manca la clausola GROUP BY nella sottoquery; così naturalmente raccoglierà solo una riga. Quindi aggiungi un GROUP BY sulla sottoquery, utilizzando il product_price_id; Posso azzardare un'ipotesi che product_price_price sia una chiave primaria surrogata, e inoltre, è un ID sequenziale, whatdyathink? Perché utilizzare GROUP BY su univoci su ogni riga su una tabella? è assurdo. Per quanto riguarda il Mysqlism, non sono sicuro della mia risposta, non ho MySQL sulla mia scatola, ho pensato che MySQL GROUP BY abbia superpoteri, ho pensato che abbia la funzionalità DISTINCT ON di Postgresql, la tua prima risposta mi ha portato a pensare : -/ – Hao