2012-07-06 3 views
8

DadoIncluir eficientemente columna no en el Grupo A de consultas SQL

Tabla A

Id INTEGER 
Name VARCHAR(50) 

Tabla B

Id INTEGER 
FkId INTEGER ; Foreign key to Table A 

deseo de contar las ocurrencias de cada valor FkId :

SELECT FkId, COUNT(FkId) 
FROM B 
GROUP BY FkId 

Ahora simplemente también quiero dar salida al Nombre de Table A.

Esto no funcionará:

SELECT FkId, COUNT(FkId), a.Name 
FROM B b 
INNER JOIN A a ON a.Id=b.FkId 
GROUP BY FkId 

porque a.Name no está contenido en la cláusula GROUP BY (produce is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause error).

El punto es pasar de salida como esta

FkId Count 
1  42 
2  25 

para salida como esta

FkId Count Name 
1  42  Ronald 
2  22  John 

Hay bastantes partidos en SO para ese mensaje de error, pero algunos, por ejemplo, https://stackoverflow.com/a/6456944/141172 tienen comentarios como "generarán 3 escaneos en la tabla, en lugar de 1, por lo que no se escalarán".

Como puedo eficienteincluir un campo de la Table B unido (que tiene una relación 1: 1 a FkId) en la salida de la consulta?

+0

¿Puede mostrar algunos datos de muestra y los resultados deseados? Me está costando entender si puede haber más de un nombre único por FkId. –

+0

Habría solo un nombre único por FkId. El objetivo es mostrar el nombre junto con FkId para que el usuario que mira el resultado no vea "La persona 1 apareció 42 veces", sino que vea "Persona 1, cuyo nombre es Ronald, apareció 42 veces". –

+1

¿Por qué no solo agrega Nombre al grupo? –

Respuesta

12

Usted puede intentar algo como esto:

;WITH GroupedData AS 
    (
     SELECT FkId, COUNT(FkId) As FkCount 
     FROM B 
     GROUP BY FkId 
    ) 
    SELECT gd.*, a.Name 
    FROM GroupedData gd 
    INNER JOIN dbo.A ON gd.FkId = A.FkId 

Crear un CTE (expresión de tabla común) para manejar la agrupación/contando con su Table B, y luego unirse a ese resultado (una fila por FkId) a Table A y tome algunas columnas más de Table A en su conjunto de resultados finales.

+0

Eso es más o menos lo mismo que la solución a la que me he vinculado, que tenía el comentario 'Y generará 3 escaneos en la tabla, en lugar de 1, así que no escalará'. ¿Realmente esta solución hará 3 escaneos de tablas como sugiere ese comentarista? –

+5

@EricJ .: Bueno, copie la consulta, ejecútela en su SQL Server Mgmt Studio y tenga activado el "Plan de ejecución real", y verá que ** NO ** NO ** hará ** tres escaneos de tabla .....el comentario en la respuesta a la que vinculó es correcto porque la consulta tiene ** dos subconsultas adicionales ** '(SELECCIONAR ...)' - ** esas ** agregarán una exploración de tabla cada una (** NO ** la CTE utilizado para la agrupación!) –

+1

+1 para usar el plan de ejecución real para diagnosticar el rendimiento. :-) –

3

¿Intentó agregar el campo al grupo?

SELECT FkId, COUNT(FkId), a.Name 
FROM B b 
INNER JOIN A a ON a.Id=b.FkId 
GROUP BY FkId,a.Name 
0
select t3.Name, t3.FkId, t3.countedFkId from (a t1 
    join (select t2.FkId, count(FkId) as countedFkId from b t2 group by t2.FkId) 
    on t1.Id = t2.FkId) t3; 
+1

Utilice '{}' not' para resaltar bloques de código. Y no tengas miedo de los retornos de carruajes. Además, no creo que esta consulta analice (la tabla derivada necesita un alias). –

+0

Oh, gracias por el consejo. Todavía nuevo en la sintaxis aquí :) Y supongo que tiene razón sobre la necesidad de un alias para la tabla derivada. – germi

Cuestiones relacionadas