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

Posted

技术标签:

【中文标题】测试 MySQL 表中是不是存在行的最佳方法【英文标题】:Best way to test if a row exists in a MySQL table测试 MySQL 表中是否存在行的最佳方法 【发布时间】:2009-11-04 20:55:47 【问题描述】:

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

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

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

SELECT * FROM table1 WHERE ... LIMIT 1

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

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

【问题讨论】:

【参考方案1】:

你也可以试试EXISTS:

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

根据the documentation,你可以SELECT 任何东西。

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

【讨论】:

...EXISTS( SELECT 1/0 FROM someothertable) 测试。对于 SQL Server 和 Oracle - 使用 *、1 或 NULL 没有区别,因为 EXISTS 仅测试基于 WHERE 条件匹配的 1+ 的布尔值。 伙计们,它在链接到此答案的文档第 2 段中说,“传统上,EXISTS 子查询以 SELECT * 开头,但它可以以 SELECT 5 或 SELECT column1 或任何内容开头. MySQL 在这样的子查询中忽略了 SELECT 列表,所以没有区别。” @ChrisThompson :执行语句时会发生什么?我的意思是结果集包含什么? @Ashwin,它包含 0(不存在)或 1(存在)。 我觉得你的查询是多余的,我测试过,这个查询SELECT 1 FROM table1 WHERE col = $var LIMIT 1比你的查询要快。那么你的查询有什么优势呢?【参考方案2】:

我最近对这个主题做了一些研究。如果字段是 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。 (更快)

【讨论】:

感谢您的额外回答。您是否发现 TEXT 字段的两个最快选项之间的时间差异非常一致?差异似乎不大,在这两种情况下使用 SELECT EXISTS(SELECT 1 ... LIMIT 1) 似乎都很好。 你是对的,关于文本字段的其他结果,差异并不那么重要。尽管如此,使用SELECT 1 FROM test WHERE texte LIKE '%something%' LIMIT 1 查询可能会更好 我在mysql上试过,如果你用select 1 ... limit 1,用select exists包围也没用 @LittleNooby 有区别。 SELECT EXISTS ... 给出真值和假值(1 或 0),而 SELECT 1 ... 给出 1 或空值。根据您的情况,false 值和空集之间存在细微差别。 您使用的是哪个版本的 MySQL?至少在 5.5+ 中,EXISTS (SELECT ...)EXISTS (SELECT ... LIMIT 1) 之间没有区别。 MySQL 足够聪明,可以自己插入这个LIMIT 1,因为EXISTS 就是这样工作的:当找到至少一个结果时它会停止。【参考方案3】:

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

【讨论】:

虽然接受的答案很有帮助,但我很欣赏别名语法。谢谢!【参考方案4】:

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

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) 

【讨论】:

除非你完全控制了宇宙,否则这些数字毫无意义。一方面,尝试以相反的顺序进行操作。当然,除非你的观点是没有区别。在那种情况下,你可能是对的。【参考方案5】:

我觉得值得指出的是,尽管在 cmets 中有所提及,但在这种情况下:

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 包装可能有意义。

【讨论】:

【参考方案6】:

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

【讨论】:

【参考方案7】:

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

【讨论】:

这是特定于数据库的。已知 COUNT(*) 在 PostgreSQL 中很慢。最好选择 PK 列并查看它是否返回任何行。 COUNT(*) 在 InnoDB 中虽然很慢【参考方案8】:

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

这是如何在单个查询中完成的:

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

【讨论】:

为什么不在这里使用IFNULL(id, 0) 而不是COUNT(*)【参考方案9】:

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

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

【讨论】:

【参考方案10】:

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

【讨论】:

-1 这是错误的。 COUNT(*) 不查看列值 - 它只计算行数。在这里查看我的答案:***.com/questions/2876909/… COUNT() 比 EXISTS 慢得多,因为 EXISTS 在第一次找到行时会返回【参考方案11】:

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

【讨论】:

【参考方案12】:

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

【讨论】:

您指的是 MyISAM 对选择整个表的计数的优化吗?如果存在 WHERE 条件,我认为这没有帮助。

以上是关于测试 MySQL 表中是不是存在行的最佳方法的主要内容,如果未能解决你的问题,请参考以下文章

检查表中是不是存在行的最有效方法是啥?

在Python中测试变量是不是存在的最佳方法[重复]

检查 DataReader 中是不是存在列的最佳方法

PostgreSQL - 检查反向值行是不是存在的最佳方法

使用 mysql 从结果集中获取最后 4 行的最佳方法

加入求和时限制 SQL 中返回的行的最佳方法