2010-12-26 17 views
38

No sé exactamente cómo expresar esto, así que ayúdenme con el título también. :)Seleccione el recuento de filas en otra tabla en una instrucción SELECT de Postgres

Tengo dos tablas. Vamos a llamarlos A y B. La tabla B tiene una clave externa a_id que apunta al A.id. Ahora me gustaría escribir una declaración SELECT que obtenga todos los registros A, con una columna adicional que contenga el recuento de registros B por cada fila A en el conjunto de resultados.

Estoy usando Postgresql 9 en este momento, pero supongo que esta sería una pregunta SQL genérica?

EDIT:

Al final me fui para la solución de disparo-cache, donde se actualiza A.b_count través de una función cada vez B cambios.

+0

Puede ser que sea mejor utilizar un JOIN por razones de rendimiento. –

Respuesta

65
SELECT A.*, (SELECT COUNT(*) FROM B WHERE B.a_id = A.id) AS TOT FROM A 
+4

¿Este tipo de selección anidada tiene una pena de rendimiento que vale la pena preocuparse? – hayavuk

+0

Sí, la hay. La selección anidada se ejecutará para cada fila que se recupera de la tabla A. –

+0

Hm, así que supongo que sería mucho más eficiente crear una columna en una tabla y actualizar el valor con un disparador cuando la tabla B ¿es modificado? – hayavuk

0

para responder a mi propia pregunta:

SELECT a.id, a.other_column, ..., 
(SELECT COUNT(*) FROM b where b.a_id = a.id) AS b_count 
FROM a; 
9

La solución subconsulta dado anteriormente es ineficiente. La solución de activación es probablemente el mejor en una base de datos de lectura en su mayoría de, pero para el registro aquí es un enfoque que se obtienen mejores resultados que una subconsulta de Ingreso:

SELECT a.id, a.xxx, count(*) 
FROM a JOIN b ON (b.a_id = a.id) 
GROUP BY a.id, a.xxx 

Si utiliza ORM de Django simplemente hay que escribir:

res = A.objects.annotate(Count('b')) 
print res[0].b__count # holds the result count 
+0

Hm, parece haber muchas formas de hacerlo.:) He implementado los desencadenantes, y como esta es principalmente la parte de lectura de la aplicación (es una lista de elementos de tipo directorio con recuento de elementos por registro de directorio en el tablero), creo que es la apuesta más segura. – hayavuk

+0

¿y si tienes docenas de columnas en una? –

+2

Dado que PostgreSQL 9.1 es suficiente para hacer "GROUP BY primary_key_column", en versiones anteriores tendría que nombrar todas las columnas elegidas en GROUP BY. – intgr

17

Creo que el comentario de @intgr en otra respuesta es tan valioso que estoy presentando esto como una respuesta alternativa ya que este método le permite filtrar la columna calculada de manera eficiente.

SELECT 
    a.* 
    COUNT(b.id) AS b_count 

FROM a 
INNER JOIN b on b.a_id = a.id 
WHERE a.id > 50 AND b.ID < 100 -- example of filtering joined tabled 

GROUP BY a.id 
HAVING COUNT(b.id) > 10 -- example of filtering calculated column 
ORDER BY a.id 
3

La respuesta aceptada es ineficiente (lenta) según mis pruebas. La subconsulta de la tabla B se ejecuta para cada fila de la tabla A. Estoy usando el siguiente enfoque basado en agrupar y unir. Funciona mucho más rápido:

SELECT A.id, QTY.quantity FROM A 
LEFT JOIN 
    (SELECT COUNT(B.a_id) AS quantity, B.a_id FROM B GROUP BY B.a_id) AS QTY 
ON A.id = QTY.a_id 

Otra variante:

SELECT A.id, COUNT(B.a_id) AS quantity FROM A 
LEFT JOIN B ON B.a_id = A.id 
GROUP BY A.id 
Cuestiones relacionadas