Como usar count e agrupar na mesma instrução select

223

Eu tenho uma consulta SQL Select que tem um grupo por. Quero contar todos os registros após o grupo por declaração. Existe uma maneira de fazer isso diretamente do sql? Por exemplo, tendo uma tabela com usuários, quero selecionar as diferentes cidades e o número total de usuários

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

Quero ter uma coluna com todas as cidades e outra com o número de usuários em todas as linhas.

Um exemplo do resultado de ter 3 cidades e 58 usuários no total é:

Town         Count
Copenhagen   58
NewYork      58
Athens       58
Stavros
fonte
quer dizer que você deseja que seu conjunto de resultados tenha duas contagens, uma para cidades e outra para usuários?
Leslie
2
Então, você deseja uma linha para cada cidade e, em cada linha, a coluna 2 contém a contagem total de todos os usuários? Então a coluna 2 tem o mesmo valor para cada linha? Se você editar para incluir dados de amostra e saída necessária, poderemos fornecer exatamente o que você deseja.
AakashM
Você está certo AakashM! Eu apenas editei.
precisa
2
Pergunta relacionada: stackoverflow.com/questions/5146978/…
milkovsky
1
Advertência aos leitores: a maioria das respostas falha ao fornecer uma resposta para a consulta atualizada.
Rick James

Respostas:

271

Isso fará o que você deseja (lista de cidades, com o número de usuários em cada uma):

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

Você pode usar a maioria das funções agregadas ao usar GROUP BY.

Atualização (após a alteração da pergunta e dos comentários)

Você pode declarar uma variável para o número de usuários e configurá-la para o número de usuários e selecionar com isso.

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

SELECT DISTINCT town, @numOfUsers
FROM user
Oded
fonte
foi exatamente o que o OP escreveu? NVM, eu ver a sua diferença, você está contando cidade não * ....
Leslie
@ Leslie - não há diferença entre as duas consultas. Eles retornarão o mesmo conjunto de resultados. Eu simplesmente não gosto do uso de *... O OP respondeu à sua própria pergunta, mas nem pareceu testá-la, só estou validando se está correta :) fredosaurus.com/notes-db/select/groupby.html
Oded
Mais uma vez não o que eu esperava, mas parece que esta é a melhor solução ..;) Obrigado
Stavros
149

Você pode usar COUNT(DISTINCT ...):

SELECT COUNT(DISTINCT town) 
FROM user
milkovsky
fonte
3
funciona como um encanto ... deve ser escolhida a resposta principal .. exceto alguma sonda de que isso não é bom.
Victor Victor
2
Eu acho que eles querem dizer se você colocar COUNT (DISTINCT town) na cláusula WHERE. Isso ocorre porque é uma função agregada e precisa ser fornecida na cláusula HAVING. Esta consulta SQL é enganosa para alguns, pois SELECT COUNT (cidade DISTINCT) se transforma em um GROUP BY implícito, devido às palavras-chave COUNT e DISTINCT, cada palavra-chave por si só também agruparia implicitamente.
A. Greensmith
Obrigado. Voto a favor. Exatamente o que é necessário - agrupe + conte em uma operação e obtenha uma única linha no resultado.
Verde
Merda .. eu não sabia que era possível!
Hugo S. Mendes
1
@milkovsky - Não, você não precisa excluí-lo. Acho irritante que esta pergunta e as muitas respostas tenham se desviado para resolver dois problemas diferentes . Sua resposta se desvia de duas maneiras - sem towncoluna e é COUNTsa coisa errada (cidade em vez de usuário).
Rick James
37

A outra maneira é:

/* 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
fonte
5
precisa de alias, caso contrário não funcionaria no mysql. select count (*) from () agr
amas
4

Com o Oracle, você pode usar funções analíticas:

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

Suas outras opções são usar uma subconsulta:

select town, count(town), (select count(town) from user) as total_count from user
group by town
Tommi
fonte
algo como o último iria funcionar, mas eu queria ver se há alguma outra solução ..
Stavros
E você não pode usar a primeira opção (função analítica)? Qual plataforma de banco de dados você está usando?
28510 Tommi
@Stavros: O último é lento
Michael Buen
4

Se você deseja ordenar por contagem (parece simples, mas não consigo encontrar uma resposta na pilha de como fazer isso), você pode fazer:

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

Dez respostas não excluídas; a maioria não faz o que o usuário pediu. A maioria das respostas interpretou mal a pergunta, pensando que havia 58 usuários em cada cidade, em vez de 58 no total. Mesmo os poucos que estão corretos não são ótimos.

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

No contexto do PO:

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

Como existe apenas uma linha a partir totdoCROSS JOIN variável não é tão volumosa quanto poderia ser.

O padrão usual é em COUNT(*)vez de COUNT(town). O último implica verificar townse não é nulo, o que é desnecessário neste contexto.

Rick James
fonte
2

Você pode usar DISTINCT dentro da COUNT como o que milkovsky disse

No meu caso:

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

Isso puxará a contagem de votos de resposta considerados o mesmo user_id de uma contagem

Jur P
fonte
2

Eu sei que esta é uma postagem antiga, no 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
fonte
5
Não há nada de errado em responder a postagens antigas. No entanto, inclua uma explicação do seu código e do próprio código.
Shelvacu
O equivalente ao MySQL (em IFNULLvez de ISNULL) leva a números diferentes para cada cidade; o usuário queria o total. De acordo com a pergunta, 58, não 174, é o total.
Rick James
1

Se você deseja selecionar a cidade e a contagem total de usuários, pode usar esta consulta abaixo:

SELECT Town, (SELECT Count(*) FROM User) `Count` FROM user GROUP BY Town;
Violendy Firdaus
fonte
Isso pressupõe (talvez razoavelmente) que não haja "usuários" duplicados User.
Rick James
1

se você deseja usar a opção Selecionar todas as consultas com contagem, tente isso ...

 select a.*, (Select count(b.name) from table_name as b where Condition) as totCount from table_name  as a where where Condition
Prakash
fonte
Obrigado por este trecho de código, que pode fornecer ajuda imediata e limitada. Uma explicação adequada melhoraria bastante seu valor a longo prazo, mostrando por que essa é uma boa solução para o problema e a tornaria mais útil para futuros leitores com outras perguntas semelhantes. Por favor edite sua resposta para adicionar alguma explicação, incluindo as suposições que você fez.
Toby Speight
0

Tente o seguinte código:

select ccode, count(empno) 
from company_details 
group by ccode;
balajibran
fonte
usamos esse código para descobrir quantos funcionários totais para os dias atuais calculam em cada código (exemplo: empresa) exemplo: count (empno) é 1839 para ccode 1 e count (empno) é 9421 para ccode 47.
balajibran