2012-02-17 15 views
5

Ehi ragazzi mi sto grattando la testa per questo, spero che qualcuno possa spiegarlo per me. Onestamente ho un po 'paura che mi morda il culo più tardi ...MYSQL JOIN fornisce risultati inaspettati (ma piacevoli)?

Quindi, ho tre tavoli che sto tirando da attualmente. reports, berries e melons. Ho impostato la mia domanda in questo modo e mi fa esattamente ciò che voglio.

SELECT 
    rpt.*, 
    ber.shipper, ber.po, ber.commodity, ber.label 

FROM reports rpt 

LEFT JOIN berries ber ON rpt.inspection_number = ber.report_key 
LEFT JOIN melons mel ON rpt.inspection_number = mel.report_key 

WHERE rpt.status='1' OR rpt.status='0' 
ORDER BY rpt.inspection_number DESC 

io ricevo la mia rendimento atteso, che è

key | role | region | inspection_type | inspection_number | shipper | po | commodity  | label 
3 | NULL | Seattle | melons   | 5555    | Shipper1 | PO2 | Commodity2 | Label2 
2 | NULL | Seattle | berries   | 1023    | Shipper1 | PO1 | Commodity1 | Label1 

Se però rimuovo LEFT JOIN melons mel ON rpt.inspection_number = mel.report_key dalla mia dichiarazione ho la stessa cosa .... non ho mai menzionato melons ??

Se io rivedere e uso JOIN invece per le bacche

SELECT 
    rpt.*, 
    ber.shipper, ber.po, ber.commodity, ber.label 

FROM reports rpt 

JOIN berries ber ON rpt.inspection_number = ber.report_key 

WHERE rpt.status='1' OR rpt.status='0' 
ORDER BY rpt.inspection_number DESC 

produce quello che mi aspettavo dovrebbe!

key | role | region | inspection_type | inspection_number | shipper | po | commodity  | label 
2 | NULL | Seattle | berries   | 1023    | Shipper1 | PO1 | Commodity1 | Label1 

Ma cercando di rivedere la mia dichiarazione di SQL in questo modo ....

SELECT 
    rpt.*, 
    ber.shipper, ber.po, ber.commodity, ber.label 
    mel.shipper, mel.po, mel.commodity, mel.label 

FROM reports rpt 

JOIN berries ber ON rpt.inspection_number = ber.report_key 
JOIN melons mel ON rpt.inspection_number = mel.report_key 

WHERE rpt.status='1' OR rpt.status='0' 
ORDER BY rpt.inspection_number DESC 

Nets me ....

MySQL returned an empty result set (i.e. zero rows). (Query took 0.0011 sec) mi dà la grande dito medio. Che diavolo? Qualcuno può spiegare che cosa sono evidentemente sbagliato e come risolverlo?

+1

Avrebbe senso avere invece bacche e meloni nello stesso tavolo? –

+1

Vorrei poterlo fare, ma in realtà abbiamo circa 12 tipi di frutta, che hanno tutti i loro campi separati. Cercare di fonderli e ricorrere a loro sarebbe un incubo .... Buona domanda però! – ehime

+0

Credo che otterrete gli stessi risultati con e senza il 'LEFT JOIN' a' mel', ma non credo ai vostri risultati. Le colonne 'shipper',' po' e altre 'ber' sarebbero NULL per ogni riga di melone. –

risposta

5

Non è così complesso. La tua prima domanda, ti unisci a mel ma non fai mai niente, quindi stai solo ricevendo i dati di Ber. La tua ultima domanda è più vicina, ma perché ti unisci interiore contro bacche e meloni e non hai rapporti che sono entrambi, non ottieni risultati. Ma la risposta è più vicino a quello che stai facendo nella seconda query, e penso che quello che vuoi è questo:

SELECT 
    rpt.*, 
    COALESCE(ber.shipper, mel.shipper) AS shipper, 
    COALESCE(ber.po, mel.po) AS po, 
    COALESCE(ber.commodity, mel.commodity) AS commodity, 
    COALESCE(ber.label, mel.label) AS label 
FROM reports rpt 
LEFT JOIN berries ber ON rpt.inspection_number = ber.report_key 
LEFT JOIN melons mel ON rpt.inspection_number = mel.report_key 
WHERE rpt.status='1' OR rpt.status='0' 
ORDER BY rpt.inspection_number DESC 

Questa query dice, dammi righe in cui c'è un join nelle bacche o di meloni, ma per le colonne che hanno in comune, dammi quello che esiste. Ci stiamo prendendo il primo posto senza una ragione particolare.

Supponendo che queste due tabelle si escludano a vicenda, penso che questo faccia ciò che si desidera.

Edit: costruire su ciò @MarcusAdams sottolinea di seguito, questo potrebbe essere riscritto per utilizzare un UNION se ci sono un numero di tavoli odioso frutta:

SELECT report_key, shipper, po, commodity, label FROM berries 
UNION 
SELECT report_key, shipper, po, commodity, label FROM melons 
UNION 
SELECT report_key, shipper, po, commodity, label FROM ... 
... 

Questa query vi darà qualcosa a portata di mano si può essere usato come sottoquery (o vista) in seguito.È anche possibile codificare un nome di origine in questo modo:

SELECT report_key, shipper, po, commodity, label, 'berries' AS type FROM berries 
UNION 
SELECT report_key, shipper, po, commodity, label, 'melons' FROM melons 
UNION 
SELECT report_key, shipper, po, commodity, label, '...' FROM ... 
... 

quindi di utilizzare questo nella vostra query originale, si dovrebbe incorporare in questo modo:

SELECT * 
FROM reports rpt, 
JOIN (SELECT report_key, shipper, po, commodity, label, 'berries' AS type FROM berries 
     UNION 
     SELECT report_key, shipper, po, commodity, label, 'melons' FROM melons 
     UNION 
     SELECT report_key, shipper, po, commodity, label, '...' FROM ... 
     ...) fruits ON rpt.inspection_number = fruits.report_key 
WHERE rpt.status='1' OR rpt.status='0' 
ORDER BY rpt.inspection_number DESC 
+0

Sembra che funzioni, ma ho notato che il mio 'rpt.status' sta sovrascrivendo il mio' mel.status' e 'ber.status'? Penso di volere qualcosa di più come 'WHERE mel.status> 2 AND ber.status> 2'? Suona bene, o ha anche senso? – ehime

+0

Non stai selezionando 'mel.status' o' ber.status' - devi aggiungerlo alla query proprio come le altre quattro colonne sopra. –

+0

quindi 'COALESCE (ber.status, mel.status) AS fruitstatus, COALESCE (rpt.status, rpt.status) AS reportstatus' e quindi' WHERE reportstatus = '1' OR reportstatus = '0' AND fruitstatus = '1' OR fruitstatus = '0'' Giusto? – ehime

0

Quindi prima di tutto, ti si noti che questa query SELECT non sta selezionando alcun dato dalla tabella meloni. Pertanto, non è necessario unirsi alla tabella meloni perché non si seleziona alcun dato da esso, e semplicemente non ha importanza cosa contiene o come lo si sta facendo riferimento perché non si sta selezionando nulla da esso.

SELECT 
    rpt.*, 
    ber.shipper, ber.po, ber.commodity, ber.label 

Avanti, io non sono troppo familiarità con le differenze tra un LEFT JOIN, un INNER JOIN, e solo un JOIN, quindi non posso rispondere a questa domanda. Tuttavia, dalla mia esperienza con MySQL, prova a modificare l'ultima query su INNER JOIN anziché su JOIN e controlla se funziona. C'è documentation sul sito MySQL per quanto riguarda le differenze tra i diversi tipi di join.

Fammi sapere se i problemi persistono e cercherò di aiutarti ulteriormente. Spero di aver almeno risposto alle domande alcune.

+1

'JOIN' senza alcun prefisso _is_' INNER JOIN'. –

3

La query di seguito fornita da Daniel Lyons funziona benissimo, ma mi piacerebbe discuterne un po 'e, rigorosamente a fini accademici, offrire un'altra soluzione, che è probabilmente più ottimizzata.

Ecco interrogazione di Daniel:

SELECT 
    rpt.*, 
    COALESCE(ber.shipper, mel.shipper) AS shipper, 
    COALESCE(ber.po, mel.po) AS po, 
    COALESCE(ber.commodity, mel.commodity) AS commodity, 
    COALESCE(ber.label, mel.label) AS label 
FROM reports rpt 
LEFT JOIN berries ber ON rpt.inspection_number = ber.report_key 
LEFT JOIN melons mel ON rpt.inspection_number = mel.report_key 
WHERE rpt.status='1' OR rpt.status='0' 
ORDER BY rpt.inspection_number DESC 

Questa query funziona alla grande, e solo per due di frutta, è abbastanza ottimizzato. Tuttavia, poiché i report si escludono a vicenda, la query sta tentando un join aggiuntivo del necessario. Ad esempio, se un record di report è già unito a un record di bacca, sappiamo che non verrà associato a un record di melone, ma MySQL non lo sa. Invece, MySQL eseguirà un'altra ricerca per tentare un join alla tabella melon, anche se non verrà trovato un record corrispondente.

Con solo due join, la metà dei tentativi di unione viene sprecata. Tuttavia, con tre frutti, i due terzi dei tentativi di unione vengono sprecati, con quattro frutti, i tre quarti dei tentativi di unione vengono sprecati e così via.

Per evitare aderire al più tentativi, siamo in grado di invertire l'ordine dei join, in questo modo:

(SELECT rpt.*, ber.shipper, ber.po, ber.commondity, ber.label 
FROM berries ber 
JOIN reports rpt 
    ON rpt.inspection_number = ber.report_key 
WHERE rpt.status = '1' OR rpt.status = '0') 
UNION ALL 
(SELECT rpt.*, mel.shipper, mel.po, mel.commondity, mel.label 
FROM melons mel 
JOIN reports rpt 
    ON rpt.inspection_number = mel.report_key 
WHERE rpt.status = '1' OR rpt.status = '0') 
ORDER BY inspection_number DESC 

Qui, stiamo iniziando dalla direzione opposta (con la frutta), e unendo di nuovo a i rapporti. Questo ci consente di creare un solo join per report.

Si noti che stiamo usando ora INNER JOIN invece di LEFT JOIN per ogni frutto, e stiamo usando UNION ALL per unire i risultati per ogni frutto in una più grande set di risultati.

Per ulteriore ottimizzazione, a volte MySQL non riconosce due costanti come 1 e 0 come intervallo, soprattutto se non è un campo intero. Le ricerche Range sono più veloci di due ricerche individuali, quindi per suggerire a MySQL che il rpt.status di 1 e 0 sono un intervallo, utilizzare BETWEEN anziché OR, supponendo che si disponga di un indice di copertura di (rpt.inspection_number, rpt.status).

+0

La mente si unisce a me in http://chat.stackoverflow.com/rooms/7868/pleasant-sql-results per andare oltre UNIONS? – ehime

+0

Scusa, ho dovuto fare un incontro. Ora, vado per il fine settimana. –