测试 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 1
与
mysql_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 1
与
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。 (更快)
【讨论】:
感谢您的额外回答。您是否发现 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 表中是不是存在行的最佳方法的主要内容,如果未能解决你的问题,请参考以下文章