2012-02-17 16 views
5

Hola chicos, estoy rascándome la cabeza con esto, espero que alguien pueda explicarlo por . Honestamente, tengo un poco de miedo de que pueda morderme en el culo más tarde ...¿MYSQL JOIN da resultados inesperados (pero agradables)?

Así que tengo tres mesas de las que estoy sacando actualmente. reports, berries y melons. Configuré mi consulta así, y me da exactamente lo que quiero.

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 

Yo recibo mi rendimiento esperado que es

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 

Si embargo elimino LEFT JOIN melons mel ON rpt.inspection_number = mel.report_key de mi declaración me da exactamente lo mismo .... Nunca mencioné melons ??

Si yo utilizo revisar y JOIN lugar para las bayas

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 

Se produce lo que se esperaba que debe!

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

Pero tratar de revisar mi declaración SQL como tal ....

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 

me Nets ....

MySQL returned an empty result set (i.e. zero rows). (Query took 0.0011 sec) me da el gran dedo medio. ¿Que demonios? ¿Alguien puede explicar lo que estoy evidentemente haciendo mal, y cómo solucionarlo?

+1

¿Tendría sentido tener bayas y melones en la misma mesa? –

+1

Ojalá pudiera, pero en realidad tenemos alrededor de 12 tipos de frutas, que todas tienen sus propios campos separados. Tratar de unirlos, y recurrir a ellos sería una pesadilla ... ¡Buena pregunta! – ehime

+0

Creo que obtendrás los mismos resultados con y sin el 'LEFT JOIN' para' mel', pero no creo tus resultados. Las columnas 'shipper',' po' y otras 'ber' serían NULL para cada fila de melón. –

Respuesta

5

No es tan complejo. Tu primera consulta, te unes contra mel pero nunca haces nada con él, por lo que solo obtienes los datos de ber. Su última consulta está más cerca, pero debido a que está uniéndose internamente a bayas y melones y no tiene ningún informe que sea ambos, no obtiene ningún resultado. Pero la respuesta está más cerca de lo que está haciendo en la segunda consulta, y creo que lo que quiere es la siguiente:

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 

Esta consulta dice, dame filas donde hay una combinación en bayas o melones, pero para las columnas que tienen en común, dame cualquiera que exista. Estamos tomando ber primero sin ninguna razón en particular.

Suponiendo que estas dos tablas son mutuamente excluyentes, creo que esto hace lo que quiere.

Editar: construyendo sobre lo @MarcusAdams señala más abajo, puede ser reescrito para utilizar un UNION si hay un número de mesas desagradable frutales:

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 ... 
... 

Esta consulta le dará algo útil que puede usar como una subconsulta (o vista) más adelante.También puede hacer difícil que el código un nombre de origen de este 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 ... 
... 

Luego de utilizar esto en su búsqueda original, le incrustarlo así:

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

Parece que funciona, pero noté que mi 'rpt.status' está sobrescribiendo mi' mel.status' y 'ber.status'? Creo que quiero algo más como 'WHERE mel.status> 2 AND ber.status> 2'? ¿Eso suena bien, o incluso tiene sentido? – ehime

+0

No está seleccionando 'mel.status' o' ber.status'; debe agregarlo a la consulta al igual que las otras cuatro columnas anteriores. –

+0

tan 'COALESCE (ber.status, mel.status) COMO fruitstatus, COALESCE (rpt.status, rpt.status) AS reportstatus' y luego' WHERE reportstatus = '1' OR reportstatus = '0' AND fruitstatus = '1' O fruitstatus = '0'' ¿Correcto? – ehime

0

Así que en primer lugar, se le observe que esta consulta SELECT no está seleccionando ningún dato de la tabla de melones. Por lo tanto, no es necesario que se una a la tabla de melones porque no está seleccionando ningún dato de ella, y simplemente no importa qué contenga ni cómo lo haga porque no está seleccionando nada.

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

A continuación, no estoy muy familiarizado con las diferencias entre un LEFT JOIN, un INNER JOIN, y sólo un JOIN, así que no puedo responder a esa pregunta. Sin embargo, desde mi experiencia con MySQL, intente cambiar la última consulta a INNER JOIN en lugar de JOIN y vea si eso funciona. Hay documentation en el sitio MySQL con respecto a las diferencias entre los diferentes tipos de uniones.

Avíseme si todavía tiene problemas y trataré de ayudarlo más. Espero que al menos haya respondido algunas preguntas.

+1

'JOIN' sin ningún prefijo _is_' INNER JOIN'. –

3

La siguiente consulta que Daniel Lyons proporcionó funciona muy bien, pero me gustaría hablar un poco, y estrictamente para fines académicos, ofrecer otra solución, que es probable que esté más optimizada.

Aquí está la pregunta de 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 

Esta consulta funciona muy bien, y por sólo dos fruta, que es bastante optimizado. Sin embargo, dado que los informes son mutuamente excluyentes, la consulta está intentando una unión extra de la necesaria. Por ejemplo, si un registro de informe ya está unido a un registro de baya, sabemos que no se va a unir a un registro de melón, pero MySQL no lo sabe. En cambio, MySQL realizará otra búsqueda para intentar unirse a la tabla de melones, aunque no se encuentre el registro correspondiente.

Con solo dos uniones, la mitad de los intentos de unión se desperdician. Sin embargo, con tres frutas, dos tercios de los intentos de unión se desperdician, con cuatro frutas, tres cuartas partes de los intentos de unión se desperdician, y así sucesivamente.

Para evitar el extra se unen a los intentos, podemos invertir el orden de las uniones, así:

(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 

Aquí, estamos empezando desde la otra dirección (la fruta), y unirse de nuevo a los informes. Esto nos permite hacer una sola unión por informe.

Observe que ahora estamos usando INNER JOIN en lugar de LEFT JOIN para cada fruta, y estamos usando UNION ALL para unir los resultados de cada fruta en un conjunto de resultados mayor.

Para una mayor optimización, a veces MySQL no reconoce dos constantes como 1 y 0 como un rango, especialmente si no es un campo entero. Las búsquedas de rango son más rápidas que dos búsquedas individuales, así que para insinuarle a MySQL que el rpt.status de 1 y 0 son un rango, use BETWEEN en lugar de OR, asumiendo que tiene un índice de cobertura de (rpt.inspection_number, rpt.status).

+0

¿Te importaría unirte a mí en http://chat.stackoverflow.com/rooms/7868/pleasant-sql-results para repasar UNIONES? – ehime

+0

Lo siento, tuve que intervenir en una reunión. Ahora, me voy para el fin de semana. –

Cuestiones relacionadas