测试MySQL表中是否存在行的最佳方法


336

我试图找出表中是否存在行。使用MySQL,最好执行如下查询:

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

并检查总数是否非零,还是执行以下查询更好:

SELECT * FROM table1 WHERE ... LIMIT 1

并检查是否返回任何行?

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

Answers:


469

您也可以尝试EXISTS

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

根据文档,您可以执行SELECT任何操作。

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


30
用测试...EXISTS( SELECT 1/0 FROM someothertable)。对于SQL Server和Oracle,使用*,1或NULL没什么区别,因为EXISTS仅基于1+的WHERE条件匹配来测试布尔值。
OMG小马

77
伙计们,它在该答案第二段链接的文档中说:“传统上,EXISTS子查询以SELECT *开头,但它可以以SELECT 5或SELECT column1或其他任何内容开头。MySQL会忽略此类中的SELECT列表。子查询,因此没有区别。”
mpen

12
@ChrisThompson:执行语句后会发生什么?我的意思是结果集包含什么?
Ashwin,2012年

13
@Ashwin,它包含0(不存在)还是1(存在)。
fedorqui'SO停止伤害

10
经过测试,我认为您的查询是多余的,并且此查询SELECT 1 FROM table1 WHERE col = $var LIMIT 1比您的查询更快。那么查询的优势是什么?
Shafizadeh

182

我最近对此问题做了一些研究。如果该字段是TEXT字段(非唯一字段),则实现方式必须有所不同。

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

  • SELECT * FROM test WHERE texte 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。(快点)

2
感谢您提供其他答案。您是否发现TEXT字段的两个最快选项之间的时间差异相当一致?差异似乎并不大,并且在两种情况下使用SELECT EXISTS(SELECT 1 ... LIMIT 1)似乎都不错。
Bernard Chen

1
没错,就文本字段的其他结果而言,差异并不重要。不过,也许查询会更好用SELECT 1 FROM test WHERE texte LIKE '%something%' LIMIT 1
洛朗W.

我在mysql上尝试过,在您使用的情况下select 1 ... limit 1,用select包围是没有用的
Adrien Horgnies

4
@LittleNooby有区别。SELECT EXISTS ...给出true和false值(1或0),而SELECT 1 ...给出1或为空。伪值和空集之间有细微的差别,具体取决于您的情况。
Quickpick

@LittleNooby提出了一个很好的观点,这很容易忽略。上面的时序测试中缺少SELECT 1 FROM test WHERE ...,没有SELECT EXISTS它。大概是这样的头发更快。
ToolmakerSteve

27

@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)

18

在我的研究中,我发现结果以跟随的速度前进。

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) 

12

我认为值得指出的是,尽管在评论中已经提到了这种情况:

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如果在没有行匹配的情况下需要显式返回,则添加包装可能是有意义的。



2

一个COUNT查询速度更快,虽然可能没有明显的,但至于获得期望的结果,两者都应该是足够的。


4
但是,这是特定于数据库的。在PostgreSQL中,COUNT(*)较慢。最好选择PK列,看看它是否返回任何行。
BalusC

3
COUNT(*)在InnoDB中虽然很慢
2012年

2

有时,获取行的自动递增主键(id)是否存在非常方便0

这是在单个查询中可以完成的方法:

SELECT IFNULL(`id`, COUNT(*)) FROM WHERE ...

为什么不只在IFNULL(id, 0)这里使用而不是COUNT(*)
Ethan Hohensee '17


-1

我愿意去COUNT(1)。它比执行测试要快,COUNT(*)因为要COUNT(*)进行测试以查看该行中的至少一列是否为!= NULL。您不需要它,尤其是因为您已经有一个条件(该WHERE子句)。COUNT(1)而是测试的有效性1,该有效性始终有效,并且花费更少的时间进行测试。


8
-1这是错误的。COUNT(*)不查看列值-只是计算行数。在这里查看我的答案:stackoverflow.com/questions/2876909/…–
马克·拜尔斯

6
COUNT()比EXISTS慢得多,因为EXISTS第一次找到行时就可以返回
Will

-1

或者,您可以将原始sql部分插入条件中,这样我就有了 “ conditions” => array('Member.id NOT IN(SELECT Membership.member_id FROM Memberss AS Membership)')


-2

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


2
您是指MyISAM在选择整个表的计数方面的优化吗?如果出现WHERE条件,我认为这没有帮助。
Bernard Chen
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.