2012-09-17 16 views
18

Tengo 3 tablas:Dos SQL IZQUIERDA UNE producir un resultado incorrecto

users(id, account_balance) 
grocery(user_id, date, amount_paid) 
fishmarket(user_id, date, amount_paid) 

Tanto fishmarket y grocery tablas pueden tener múltiples ocurrencias de la misma user_id con diferentes fechas y las cantidades pagadas o no tienen nada en absoluto por cualquier usuario dado . cuando intento la siguiente consulta:

SELECT 
    t1."id" AS "User ID", 
    t1.account_balance AS "Account Balance", 
    count(t2.user_id) AS "# of grocery visits", 
    count(t3.user_id) AS "# of fishmarket visits" 
FROM users t1 
LEFT OUTER JOIN grocery t2 ON (t2.user_id=t1."id") 
LEFT OUTER JOIN fishmarket t3 ON (t3.user_id=t1."id") 
GROUP BY t1.account_balance,t1.id 
ORDER BY t1.id 

produce un resultados incorrectos: "1", "12", "12".
Pero cuando trato de LEFT JOIN a una sola tabla, produce un resultado correcto para las visitas grocery o fishmarket, que son "1", "3", "4".

¿Qué estoy haciendo mal aquí?
Estoy usando PostgreSQL 9.1.

Respuesta

37

Las uniones se procesan de izquierda a derecha (a menos que los paréntesis indiquen lo contrario). Si LEFT JOIN (o solo JOIN, efecto similar) tres comestibles a un usuario, obtiene 3 filas (1 x 3). Si luego se une a 4 fishmarkets para el mismo usuario, obtiene 12 (3 x 4) filas, multiplicando el recuento anterior en el resultado, no añadiéndole, como puede haber esperado.
multiplicando así las visitas de comestibles y mercados de pescado por igual.

Se debe trabajar de esta manera:

SELECT u.id 
    , u.account_balance 
    , g.grocery_visits 
    , f.fishmarket_visits 
FROM users u 
LEFT JOIN (
    SELECT user_id, count(*) AS grocery_visits 
    FROM grocery 
    GROUP BY user_id 
    ) g ON g.user_id = u.id 
LEFT JOIN (
    SELECT user_id, count(*) AS fishmarket_visits 
    FROM fishmarket 
    GROUP BY user_id 
    ) f ON f.user_id = u.id 
ORDER BY u.id; 

Para buscar valores agregados para uno o pocos usuarios, subconsultas correlacionadaslike @Vince provided están bien. Para una tabla completa o partes principales de ella, es (mucho) más eficiente agregar las tablas ny unirse al resultado una vez. De esta forma, tampoco necesitamos otro GROUP BY en la consulta externa.

+2

Felicitaciones por la realidad responder a la pregunta no sólo dar una solución. – xception

+0

Código de trabajo en este [enlace] (http://rextester.com/ZFFE32806). – HeyJude

+0

@ErwinBrandstetter He aprendido mucho sobre Postgres de sus publicaciones. ¿Alguna vez has considerado escribir un libro sobre el tema? –

2

Es porque cuando la tabla de usuario se une a la mesa de compras, hay 3 registros coincidentes. Luego, cada uno de esos tres registros coincide con los 4 registros en el mercado de pescado, produciendo 12 registros. Necesitas subconsultas para obtener lo que estás buscando.

7

Para su consulta original, si quita al grupo para ver el resultado agrupado previamente, verá por qué se crearon los conteos que estaba recibiendo.

Tal vez la siguiente consulta que utiliza subconsultas sería conseguir el resultado previsto:

SELECT 
t1."id" AS "User ID", 
t1.account_balance AS "Account Balance", 
(SELECT count(*) FROM grocery  t2 ON (t2.user_id=t1."id")) AS "# of grocery visits", 
(SELECT count(*) FROM fishmarket t3 ON (t3.user_id=t1."id")) AS "# of fishmarket visits" 
FROM users t1 
ORDER BY t1.id 
Cuestiones relacionadas