Cómo usar count y group by en la misma instrucción select

223

Tengo una consulta de selección SQL que tiene un grupo por. Quiero contar todos los registros después del grupo por declaración. ¿Hay alguna manera para esto directamente desde sql? Por ejemplo, al tener una tabla con usuarios, quiero seleccionar las diferentes ciudades y el número total de usuarios

select town, count(*) from user
group by town

Quiero tener una columna con todas las ciudades y otra con el número de usuarios en todas las filas.

Un ejemplo del resultado de tener 3 ciudades y 58 usuarios en total es:

Town         Count
Copenhagen   58
NewYork      58
Athens       58
Stavros
fuente
¿Quiere decir que desea que su conjunto de resultados tenga 2 recuentos, uno para las ciudades y otro para los usuarios?
Leslie
2
Entonces, ¿desea una fila para cada ciudad, y en cada fila, la columna 2 contiene el recuento total de todos los usuarios? Entonces, ¿la columna 2 tiene el mismo valor para cada fila? Si edita para incluir datos de muestra y la salida requerida, podremos darle exactamente lo que desea.
AakashM
Tienes razón AakashM! Acabo de editarlo.
Stavros
2
Pregunta relacionada: stackoverflow.com/questions/5146978/…
milkovsky
1
Advertencia a los lectores: la mayoría de las respuestas no proporcionan una respuesta para la consulta actualizada.
Rick James

Respuestas:

271

Esto hará lo que quieras (lista de pueblos, con el número de usuarios en cada uno):

select town, count(town) 
from user
group by town

Puede usar la mayoría de las funciones agregadas al usar GROUP BY.

Actualización (siguiente cambio a pregunta y comentarios)

Puede declarar una variable para el número de usuarios y establecerla en el número de usuarios y luego seleccionar con eso.

DECLARE @numOfUsers INT
SET @numOfUsers = SELECT COUNT(*) FROM user

SELECT DISTINCT town, @numOfUsers
FROM user
Oded
fuente
eso es exactamente lo que escribió el OP? nvm, veo tu diferencia, estás contando Town not * ....
Leslie
@ Leslie: no hay diferencia entre las dos consultas. Devolverán el mismo conjunto de resultados. Simplemente no me gusta el uso de *... El OP respondió su propia pregunta, pero ni siquiera parecía probarlo, solo estoy validando que es correcto :) fredosaurus.com/notes-db/select/groupby.html
Oded
De nuevo, no es lo que esperaba, pero parece que esta es la mejor solución ...;) Gracias
Stavros
149

Puedes usar COUNT(DISTINCT ...):

SELECT COUNT(DISTINCT town) 
FROM user
milkovsky
fuente
3
funciona como un encanto ... se debe elegir la respuesta principal ... excepto algunas pruebas de que esto no es bueno.
Victor
2
Creo que significan si pones COUNT (ciudad DISTINTA) en la cláusula WHERE. Esto se debe a que es una función agregada y debe proporcionarse en la cláusula HAVING. Esta consulta SQL es engañosa para algunos, ya que SELECT COUNT (ciudad DISTINCT) se convierte en un GROUP BY implícito, debido a las palabras clave COUNT y DISTINCT, cada palabra clave por sí misma también se agruparía implícitamente.
A. Greensmith
Gracias. Voto a favor. Exactamente lo que se necesita: agrupar + contar en una operación y obtener una sola fila como resultado.
Verde
Mierda ... ¡No sabía que eso fuera posible!
Hugo S. Mendes
1
@milkovsky - No, no tienes que eliminarlo. Me parece irritante que esta pregunta más las muchas respuestas se hayan bifurcado para resolver dos problemas diferentes . Su respuesta se desvía de dos maneras: sin towncolumna, y es COUNTsincorrecta (ciudad en lugar de usuario).
Rick James
37

La otra forma es:

/* Number of rows in a derived table called d1. */
select count(*) from
(
  /* Number of times each town appears in user. */
  select town, count(*)
  from user
  group by town
) d1
ZhenYu Wang
fuente
55
necesita alias de lo contrario no funcionaría en mysql. seleccione recuento (*) de () agr
amas
4

Con Oracle puede usar funciones analíticas:

select town, count(town), sum(count(town)) over () total_count from user
group by town

Sus otras opciones son usar una subconsulta:

select town, count(town), (select count(town) from user) as total_count from user
group by town
Tommi
fuente
algo así como el último que iba a funcionar, pero yo quería ver si hay alguna otra solución ..
Stavros
¿Y no puedes usar la primera opción (función analítica)? ¿Qué plataforma de base de datos estás usando?
Tommi
@Stavros: El último es lento
Michael Buen
4

Si desea ordenar por conteo (suena simple pero no puedo encontrar una respuesta en la pila de cómo hacerlo), puede hacer:

        SELECT town, count(town) as total FROM user
        GROUP BY town ORDER BY total DESC
sagits
fuente
4

Diez respuestas no eliminadas; la mayoría no hace lo que el usuario le pidió. La mayoría de las respuestas malinterpretan la pregunta porque piensan que hay 58 usuarios en cada ciudad en lugar de 58 en total. Incluso los pocos que son correctos no son óptimos.

mysql> flush status;
Query OK, 0 rows affected (0.00 sec)

SELECT  province, total_cities
    FROM       ( SELECT  DISTINCT province  FROM  canada ) AS provinces
    CROSS JOIN ( SELECT  COUNT(*) total_cities  FROM  canada ) AS tot;
+---------------------------+--------------+
| province                  | total_cities |
+---------------------------+--------------+
| Alberta                   |         5484 |
| British Columbia          |         5484 |
| Manitoba                  |         5484 |
| New Brunswick             |         5484 |
| Newfoundland and Labrador |         5484 |
| Northwest Territories     |         5484 |
| Nova Scotia               |         5484 |
| Nunavut                   |         5484 |
| Ontario                   |         5484 |
| Prince Edward Island      |         5484 |
| Quebec                    |         5484 |
| Saskatchewan              |         5484 |
| Yukon                     |         5484 |
+---------------------------+--------------+
13 rows in set (0.01 sec)

SHOW session status LIKE 'Handler%';

+----------------------------+-------+
| Variable_name              | Value |
+----------------------------+-------+
| Handler_commit             | 1     |
| Handler_delete             | 0     |
| Handler_discover           | 0     |
| Handler_external_lock      | 4     |
| Handler_mrr_init           | 0     |
| Handler_prepare            | 0     |
| Handler_read_first         | 3     |
| Handler_read_key           | 16    |
| Handler_read_last          | 1     |
| Handler_read_next          | 5484  |  -- One table scan to get COUNT(*)
| Handler_read_prev          | 0     |
| Handler_read_rnd           | 0     |
| Handler_read_rnd_next      | 15    |
| Handler_rollback           | 0     |
| Handler_savepoint          | 0     |
| Handler_savepoint_rollback | 0     |
| Handler_update             | 0     |
| Handler_write              | 14    |  -- leapfrog through index to find provinces  
+----------------------------+-------+

En el contexto del OP:

SELECT  town, total_users
    FROM       ( SELECT  DISTINCT town  FROM  canada ) AS towns
    CROSS JOIN ( SELECT  COUNT(*) total_users  FROM  canada ) AS tot;

Como solo hay una fila tot, CROSS JOINno es tan voluminosa como podría ser.

El patrón habitual es en COUNT(*)lugar de COUNT(town). Esto último implica verificar townque no sea nulo, lo cual es innecesario en este contexto.

Rick James
fuente
2

Puedes usar DISTINCT dentro de COUNT como lo que dijo milkovsky

en mi caso:

select COUNT(distinct user_id) from answers_votes where answer_id in (694,695);

Esto extraerá el recuento de votos de respuesta considerados el mismo user_id que un recuento

Jur P
fuente
2

Sé que esta es una publicación anterior, en SQL Server:

select  isnull(town,'TOTAL') Town, count(*) cnt
from    user
group by town WITH ROLLUP

Town         cnt
Copenhagen   58
NewYork      58
Athens       58
TOTAL        174
Marcus
fuente
55
No hay nada de malo en responder publicaciones antiguas. Sin embargo, incluya una explicación de su código, así como el código mismo.
Shelvacu
El equivalente de MySQL (en IFNULLlugar de ISNULL) conduce a números diferentes para cada ciudad; El usuario quería el total. Según la pregunta, 58, no 174, es el total.
Rick James
1

Si desea seleccionar la ciudad y el recuento total de usuarios, puede usar esta consulta a continuación:

SELECT Town, (SELECT Count(*) FROM User) `Count` FROM user GROUP BY Town;
Violendy Firdaus
fuente
Esto supone (quizás razonablemente) que no hay "usuarios" duplicados User.
Rick James
1

si desea utilizar la opción Seleccionar todas las consultas con conteo, intente esto ...

 select a.*, (Select count(b.name) from table_name as b where Condition) as totCount from table_name  as a where where Condition
Prakash
fuente
Gracias por este fragmento de código, que puede proporcionar una ayuda limitada e inmediata. Una explicación adecuada mejoraría en gran medida su valor a largo plazo al mostrar por qué esta es una buena solución al problema y la haría más útil para futuros lectores con otras preguntas similares. Por favor, editar su respuesta a añadir un poco de explicación, incluyendo los supuestos realizados.
Toby Speight
0

Prueba el siguiente código:

select ccode, count(empno) 
from company_details 
group by ccode;
balajibran
fuente
utilizamos este código para encontrar cuántos empleados totales para el cálculo actual en cada uno de los ejemplos de ccode (código de compañía): count (empno) es 1839 para ccode 1 y count (empno) es 9421 para ccode 47.
balajibran