380

我试图找出表中是否存在一行。使用 MySQL,做这样的查询是否更好:

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

并检查总数是否非零,或者最好执行如下查询:

SELECT * FROM table1 WHERE ... LIMIT 1

并检查是否返回了任何行?

在这两个查询中,WHERE 子句都使用索引。

4

12 回答 12

515

您也可以尝试EXISTS

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

根据文档,您可以做SELECT任何事情。

传统上,EXISTS 子查询以 SELECT * 开头,但它可以以 SELECT 5 或 SELECT column1 或任何其他开头。MySQL 在这样的子查询中忽略了 SELECT 列表,因此没有区别。

于 2009-11-04T20:59:36.317 回答
210

我最近对这个问题做了一些研究。如果字段是 TEXT 字段,非唯一字段,则实现它的方式必须不同。

我用 TEXT 字段做了一些测试。考虑到我们有一个包含 1M 条目的表这一事实。37 个条目等于“某物”:

  • SELECT * FROM test WHERE text LIKE '%something%' LIMIT 1mysql_num_rows() :0.039061069488525s。(快点)
  • 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。

但是现在,有了 BIGINT PK 字段,只有一个条目等于 '321321' :

  • SELECT * FROM test2 WHERE id ='321321' LIMIT 1mysql_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。(快点)
于 2012-05-21T15:23:13.127 回答
33

@ChrisThompson 回答的一个简短示例

例子:

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)

使用别名:

mysql> SELECT EXISTS(SELECT 1 FROM table_1 WHERE id = 1) AS mycheck;
+---------+
| mycheck |
+---------+
|       1 |
+---------+
1 row in set (0.00 sec)
于 2013-12-19T11:18:26.300 回答
23

在我的研究中,我可以找到跟随速度的结果。

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) 
于 2017-02-15T06:32:30.727 回答
17

我觉得值得指出的是,尽管评论中提到了,但在这种情况下:

SELECT 1 FROM my_table WHERE *indexed_condition* LIMIT 1

优于:

SELECT * FROM my_table WHERE *indexed_condition* LIMIT 1

这是因为索引可以满足第一个查询,而第二个查询需要行查找(除非可能所有表的列都在使用的索引中)。

添加该LIMIT子句允许引擎在找到任何行后停止。

第一个查询应该类似于:

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

它向引擎发送相同的信号(1/* 在这里没有区别),但我仍然会写 1 以加强使用时的习惯EXISTS

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

EXISTS如果在没有行匹配时需要显式返回,则添加包装可能是有意义的。

于 2017-03-08T16:07:01.907 回答
5

建议您不要使用Count,因为 count 总是会为 db 使用带来额外的负载,如果您的记录在那里SELECT 1,它会返回1,否则它会返回 null 并且您可以处理它。

于 2012-11-02T15:21:58.843 回答
2

COUNT查询更快,虽然可能不明显,但就获得所需的结果而言,两者都应该足够了。

于 2009-11-04T20:58:31.703 回答
2

有时,获取行的自动递增主键 ( id)(如果存在或0不存在)非常方便。

以下是如何在单个查询中完成此操作:

SELECT IFNULL(`id`, COUNT(*)) FROM WHERE ...
于 2016-03-22T06:50:37.560 回答
-1

对于非 InnoDB 表,您还可以使用信息模式表:

http://dev.mysql.com/doc/refman/5.1/en/tables-table.html

于 2009-11-04T21:12:33.693 回答
-1

我会去的COUNT(1)。它比COUNT(*)因为COUNT(*)测试该行中的至少一列是否为 != NULL 更快。你不需要那个,特别是因为你已经有了一个条件(WHERE子句)。COUNT(1)而是测试 的有效性1,这始终是有效的,并且需要更少的时间来测试。

于 2009-11-04T21:15:33.680 回答
-1

或者您可以将原始 sql 部分插入条件,所以我有 'conditions'=>array('Member.id NOT IN (SELECT Membership.member_id FROM members AS Membership)')

于 2014-10-29T09:08:02.727 回答
-3

COUNT(*)在 MySQL 中进行了优化,因此一般来说,前者的查询可能会更快。

于 2009-11-04T21:01:53.513 回答