Melhor maneira de testar se existe uma linha em uma tabela MySQL

336

Estou tentando descobrir se existe uma linha em uma tabela. Usando o MySQL, é melhor fazer uma consulta como esta:

SELECT COUNT(*) AS total FROM table1 WHERE ...

e verifique se o total é diferente de zero ou se é melhor fazer uma consulta como esta:

SELECT * FROM table1 WHERE ... LIMIT 1

e verifique se alguma linha foi retornada?

Nas duas consultas, a cláusula WHERE usa um índice.

Bernard Chen
fonte

Respostas:

469

Você também pode tentar EXISTS:

SELECT EXISTS(SELECT * FROM table1 WHERE ...)

e pela documentação , você pode SELECTqualquer coisa.

Tradicionalmente, uma subconsulta EXISTS começa com SELECT *, mas pode começar com SELECT 5 ou SELECT column1 ou qualquer outra coisa. O MySQL ignora a lista SELECT em uma subconsulta, portanto, não faz diferença.

Chris Thompson
fonte
30
Teste com ...EXISTS( SELECT 1/0 FROM someothertable). Para SQL Server e Oracle - não faz diferença usar *, 1 ou NULL porque EXISTS apenas testa um booleano com base em mais de 1 dos critérios WHERE correspondentes.
OMG Ponies
77
Pessoal, diz exatamente na documentação vinculada a esta resposta, segundo parágrafo, "Tradicionalmente, uma subconsulta EXISTS começa com SELECT *, mas pode começar com SELECT 5 ou SELECT column1 ou qualquer outra coisa. O MySQL ignora a lista SELECT dessa maneira. uma subconsulta, por isso não faz diferença. "
MPEN
12
@ ChrisThompson: o que acontece quando a instrução é executada? Quero dizer, o que o conjunto de resultados contém?
Ashwin
13
@ Ashwin, ele contém se 0 (não existe) ou 1 (existe).
fedorqui 'Então, pare de prejudicar'
10
Acho que sua consulta é supérflua, testei e SELECT 1 FROM table1 WHERE col = $var LIMIT 1é mais rápida que a sua consulta. Então, qual é a vantagem da sua consulta?
Shafizadeh 28/09/2015
182

Eu fiz algumas pesquisas sobre esse assunto recentemente. A maneira de implementá-lo deve ser diferente se o campo for um campo TEXT, um campo não exclusivo.

Eu fiz alguns testes com um campo de texto. Considerando o fato de termos uma tabela com entradas de 1 milhão. 37 entradas são iguais a 'alguma coisa':

  • SELECT * FROM test WHERE texte LIKE '%something%' LIMIT 1com mysql_num_rows() : 0.039061069488525s. (MAIS RÁPIDO)
  • SELECT count(*) as count FROM test WHERE text LIKE '%something% : 16.028197050095s.
  • SELECT EXISTS(SELECT 1 FROM test WHERE text LIKE '%something%') : 0.87045907974243s.
  • SELECT EXISTS(SELECT 1 FROM test WHERE text LIKE '%something%' LIMIT 1) : 0.044898986816406s.

Mas agora, com um campo BIGINT PK, apenas uma entrada é igual a '321321':

  • SELECT * FROM test2 WHERE id ='321321' LIMIT 1com mysql_num_rows() : 0.0089840888977051s.
  • SELECT count(*) as count FROM test2 WHERE id ='321321' : 0.00033879280090332s.
  • SELECT EXISTS(SELECT 1 FROM test2 WHERE id ='321321') : 0.00023889541625977s.
  • SELECT EXISTS(SELECT 1 FROM test2 WHERE id ='321321' LIMIT 1): 0.00020313262939453s. (MAIS RÁPIDO)
Laurent W.
fonte
2
Obrigado pela resposta adicional. Você achou a diferença de tempo entre as duas opções mais rápidas para um campo TEXT ser bastante consistente? A diferença não parece grande, e o uso de SELECT EXISTS (SELECT 1 ... LIMIT 1) parece ser muito bom nos dois casos.
Bernard Chen
11
Você está certo, a diferença não é tão importante em relação aos outros resultados referentes ao campo de texto. No entanto, talvez seja melhor usar a consulta #SELECT 1 FROM test WHERE texte LIKE '%something%' LIMIT 1
Laurent W.
Tentei no mysql e no caso de você usar select 1 ... limit 1, é inútil para surround com select existe
Adrien Horgnies
4
@LittleNooby há diferença. SELECT EXISTS ... fornece valor verdadeiro e falso (1 ou 0), enquanto SELECT 1 ... fornece 1 ou vazio. Há uma diferença sutil entre valor falso e conjunto vazio, dependendo da sua situação.
Quickpick
O @LittleNooby faz uma excelente observação, fácil de ignorar. Faltam nos testes de tempo acima SELECT 1 FROM test WHERE ..., sem SELECT EXISTScontornos. Presumivelmente, é um cabelo mais rápido assim.
precisa
27

Um pequeno exemplo da resposta de @ ChrisThompson

Exemplo:

mysql> SELECT * FROM table_1;
+----+--------+
| id | col1   |
+----+--------+
|  1 | foo    |
|  2 | bar    |
|  3 | foobar |
+----+--------+
3 rows in set (0.00 sec)

mysql> SELECT EXISTS(SELECT 1 FROM table_1 WHERE id = 1);
+--------------------------------------------+
| EXISTS(SELECT 1 FROM table_1 WHERE id = 1) |
+--------------------------------------------+
|                                          1 |
+--------------------------------------------+
1 row in set (0.00 sec)

mysql> SELECT EXISTS(SELECT 1 FROM table_1 WHERE id = 9);
+--------------------------------------------+
| EXISTS(SELECT 1 FROM table_1 WHERE id = 9) |
+--------------------------------------------+
|                                          0 |
+--------------------------------------------+
1 row in set (0.00 sec)

Usando um alias:

mysql> SELECT EXISTS(SELECT 1 FROM table_1 WHERE id = 1) AS mycheck;
+---------+
| mycheck |
+---------+
|       1 |
+---------+
1 row in set (0.00 sec)
jaltek
fonte
18

Na minha pesquisa, posso encontrar o resultado seguindo a velocidade.

select * from table where condition=value
(1 total, Query took 0.0052 sec)

select exists(select * from table where condition=value)
(1 total, Query took 0.0008 sec)

select count(*) from table where condition=value limit 1) 
(1 total, Query took 0.0007 sec)

select exists(select * from table where condition=value limit 1)
(1 total, Query took 0.0006 sec) 
shihab mm
fonte
12

Acho que vale a pena ressaltar, embora tenha sido abordado nos comentários, que nesta situação:

SELECT 1 FROM my_table WHERE *indexed_condition* LIMIT 1

É superior a:

SELECT * FROM my_table WHERE *indexed_condition* LIMIT 1

Isso ocorre porque a primeira consulta pode ser satisfeita pelo índice, enquanto a segunda exige uma consulta de linha (a menos que possivelmente todas as colunas da tabela estejam no índice usado).

A adição da LIMITcláusula permite que o mecanismo pare após encontrar qualquer linha.

A primeira consulta deve ser comparável a:

SELECT EXISTS(SELECT * FROM my_table WHERE *indexed_condition*)

Que envia os mesmos sinais para o mecanismo (1 / * não faz diferença aqui), mas eu ainda escreveria o 1 para reforçar o hábito ao usar EXISTS:

SELECT EXISTS(SELECT 1 FROM my_table WHERE *indexed_condition*)

Pode fazer sentido adicionar o EXISTSagrupamento se você precisar de um retorno explícito quando nenhuma linha corresponder.

Arth
fonte
4

Sugira que você não use, Countporque count sempre cria cargas extras para uso no banco de dados SELECT 1e retorna 1 se o seu registro estiver ali, caso contrário, ele retornará nulo e você poderá lidar com isso.

Fatih Karatana
fonte
2

Uma consulta COUNT é mais rápida, embora talvez não visivelmente, mas na medida em que obtém o resultado desejado, ambas devem ser suficientes.

jaywon
fonte
4
No entanto, isso é específico do banco de dados. Sabe-se que a COUNT (*) é lenta no PostgreSQL. Melhor seria selecionar a coluna PK e ver se ela retorna alguma linha.
BalusC 04/11/2009
3
COUNT (*) é lento no InnoDB
será
2

Às vezes, é bastante útil obter a chave primária de incremento automático ( id) da linha, se ela existir e0 se não .

Veja como isso pode ser feito em uma única consulta:

SELECT IFNULL(`id`, COUNT(*)) FROM WHERE ...
Zaxter
fonte
Por que não usar IFNULL(id, 0)aqui em vez do COUNT(*)?
Ethan Hohensee 28/08
-1

Eu iria com COUNT(1). É mais rápido do que COUNT(*)porque COUNT(*)testa para ver se pelo menos uma coluna nessa linha é! = NULL. Você não precisa disso, principalmente porque você já tem uma condição em vigor (a WHEREcláusula). COUNT(1)em vez disso, testa a validade de 1, que é sempre válida e leva muito menos tempo para testar.

Felix
fonte
8
-1 Isso está errado. COUNT (*) não analisa os valores da coluna - apenas conta o número de linhas. Veja minha resposta aqui: stackoverflow.com/questions/2876909/…
Mark Byers
6
COUNT () é muito mais lento que EXISTS, pois EXISTS pode retornar quando encontrar uma linha pela primeira vez
Will
-1

Ou você pode inserir parte sql bruta nas condições para que eu tenha 'conditions' => array ('Member.id NOT IN (SELECT Membership.member_id FROM members AS AS Membership)')

user4193303
fonte
-2

COUNT(*) são otimizados no MySQL, portanto a consulta anterior provavelmente será mais rápida, de um modo geral.

Arthur Reutenauer
fonte
2
Você está se referindo à otimização que o MyISAM possui para selecionar a contagem para uma tabela inteira? Eu não acho que isso ajudou se houvesse uma condição WHERE.
Bernard Chen