确定记录是否存在的最快方法


143

就像标题所暗示的那样……我正在尝试以最少的开销找出最快的方法来确定表中是否存在记录。

查询样例:

SELECT COUNT(*) FROM products WHERE products.id = ?;

    vs

SELECT COUNT(products.id) FROM products WHERE products.id = ?;

    vs

SELECT products.id FROM products WHERE products.id = ?;

假设?交换为'TB100'...,第一个查询和第二个查询都将返回完全相同的结果(例如1,此对话...)。最后一个查询将按'TB100'预期返回;如果id表中不存在,则不。

目的是弄清楚是否id在表中。如果不是,则程序将接下来插入记录,如果是,则程序将跳过该记录或基于此问题范围之外的其他程序逻辑执行UPDATE查询。

哪个更快,开销更少?(这将在每个程序运行中重复数万次,并且一天将运行多次)。

(通过提供的M $ JDBC驱动程序从Java对M $ SQL Server运行此查询)


1
这可能取决于数据库。例如,依靠Postgres相当慢。
Mike Christensen

抱歉,这是Java通过jdbc驱动程序与M $ SQL进行通信。我将更新我的OP。
SnakeDoc


@NikolaMarkovinović:在这种情况下,您将如何使用它?
zerkms 2013年

5
@zerkms取决于上下文。如果在存储过程中,它将是if exists(select null from products where id = @id); 如果在客户直接调用的查询中select case when exists (...) then 1 else 0 end
NikolaMarkovinović2013年

Answers:


170

SELECT TOP 1 products.id FROM products WHERE products.id = ?; 将胜过您的所有建议,因为它将在找到第一条记录后终止执行。


5
通过PK(或任何其他唯一键)搜索时,优化器是否不将其自身考虑在内?
zerkms 2013年

3
他nver表示这是PK,但是如果是这样,那么优化器将考虑到这一点。
Declan_K 2013年

3
@Declan_K:在这种情况下,我的魔术领域似乎失败了,标题为as as idnot PK 的专栏似乎失败了。因此,+ 1对您的建议。
zerkms 2013年

4
如果不是PK,我也建议确保该列上有一个索引。否则,查询将必须执行表扫描而不是更快的表查找。
CD Jorgensen

3
我认为我们应该考虑这一问题的@ nenad-zivkovic答案。
朱利奥·卡钦

191

EXISTS(或NOT EXISTS)专为检查是否存在某些东西而设计,因此应该(并且是)最佳选择。它将在匹配的第一行暂停,因此它不需要TOP子句,并且实际上不选择任何数据,因此列大小没有开销。您可以安全地使用SELECT *在这里-没有比不同SELECT 1SELECT NULLSELECT AnyColumn... (你甚至可以用一个无效的语句,比如SELECT 1/0,它不会打破)

IF EXISTS (SELECT * FROM Products WHERE id = ?)
BEGIN
--do what you need if exists
END
ELSE
BEGIN
--do what needs to be done if not
END

这是否不是必须先执行SELECT语句,然后执行IF EXISTS语句……会导致额外的开销,从而导致更多的处理时间?
SnakeDoc

7
@SnakeDoc No.的Exists使用select方式是,一旦发现一行,它就会退出。此外,存在仅表示记录的存在,而不是记录中的实际值,从而节省了从磁盘加载行的需求(当然,假设搜索条件已建立索引)。至于的开销if-无论如何,您将不得不花费这微不足道的时间。
NikolaMarkovinović2013年

1
@NikolaMarkovinović有趣的一点。我不确定该字段上是否存在索引,而我的newbish SQL不知道如何查找。我正在通过JDBC使用Java来处理此数据库,并且数据库位于某个地方的colo中。仅向我提供了“数据库摘要”,其中仅详细说明了每个表中存在哪些字段,它们的类型以及任何FK或PK。这会改变什么吗?
SnakeDoc

3
@SnakeDoc要了解表结构(包括外键和索引),请运行sp_help table_name。要检索多行中的几行,或者使用select topexists,索引是必不可少的。如果它们不存在,则sql引擎将必须执行表扫描。这是最不希望的表搜索选项。如果您无权创建索引,则必须与另一侧的技术人员联系,以了解他们是自动调整索引还是希望您建议索引。
NikolaMarkovinović2013年

1
@Konstantin您可以做类似的事情SELECT CASE WHEN EXISTS(..) THEN 1 ELSE 0 END;
Nenad Zivkovic

21

无所不能-

SELECT TOP 1 1 FROM products WHERE id = 'some value';

您无需计数即可知道表中是否有数据。并且在不需要时不要使用别名。


5
尽管其名称id不是主键。因此,即使您不计算在内,您仍然需要查找所有匹配的记录,可能有数千条记录。关于别名-代码是持续不断的工作。您永远都不知道什么时候该回去。别名有助于防止愚蠢的运行时错误;例如,不需要别名的唯一列名称不再是唯一的,因为有人在另一个连接的表中创建了相同名称的列。
NikolaMarkovinović2013年

是的,您绝对正确。别名有很大帮助,但是我不认为在不使用联接时没有任何区别。所以,我说如果没有必要不要使用它。:)您可以在此处找到有关检查存在性的漫长讨论。:)
AgentSQL

3
我不知道为什么接受这个词aliasing。正确的名词是qualifying。这是Alex Kuznetzov的详细解释。关于单表查询-它是单一的表。但是后来,当发现错误并试图控制洪水时,客户很紧张,您加入另一个表只是为了面对错误消息-易于纠正的消息,但此刻不冒汗,只是轻敲了一下-您就纠正了错误记住永远不要离开专栏...
NikolaMarkovinović13年

1
现在不能忽略它。谢谢!!:)
AgentSQL

15
SELECT CASE WHEN EXISTS (SELECT TOP 1 *
                         FROM dbo.[YourTable] 
                         WHERE [YourColumn] = [YourValue]) 
            THEN CAST (1 AS BIT) 
            ELSE CAST (0 AS BIT) END

这种方法为您返回一个布尔值。


1
可能会省略Top语句和*语句,使其更快一些,因为Exist会在找到记录后退出,因此如下所示:SELECT CASE WHEN EXISTS(SELECT 1 FROM dbo。[YourTable] WHERE [YourColumn] = [YourValue])然后,然后投放(按位分配1位),然后按投放(按位
分配

该建议没有提及为什么它比SQL Server中的内置exist / not exist语句更快。如果没有任何基准测试,我很难相信一个案例陈述会比立即的对/错响应产生更快的结果。
Bonez024,19年

8

您也可以使用

 If EXISTS (SELECT 1 FROM dbo.T1 WHERE T1.Name='Scot')
    BEGIN
         --<Do something>
    END 

ELSE    
     BEGIN
       --<Do something>
     END

7

不要以为还没有人提到它,但是如果您确定数据在您下面不会发生变化,您可能还希望应用NoLock提示以确保读取时不会被阻塞。

SELECT CASE WHEN EXISTS (SELECT 1 
                     FROM dbo.[YourTable] WITH (NOLOCK)
                     WHERE [YourColumn] = [YourValue]) 
        THEN CAST (1 AS BIT) 
        ELSE CAST (0 AS BIT) END

3
SELECT COUNT(*) FROM products WHERE products.id = ?;

这是适用于所有数据库的跨关系数据库解决方案。


6
然而,你强制分贝遍历所有记录,速度很慢上大表
AMD

@amd关心解释原因?
UmNyobe

@amd您的评论很有道理。该查询比FIND ANY更像是FIND ALL。
UmNyobe

1

下面是确定记录是否存在于数据库中的最简单,最快的方法,好在所有关系数据库中都可以使用

SELECT distinct 1 products.id FROM products WHERE products.id = ?;

0
create or replace procedure ex(j in number) as
i number;
begin
select id into i from student where id=j;
if i is not null then
dbms_output.put_line('exists');
end if;
exception
   when no_data_found then
        dbms_output.put_line(i||' does not exists');

end;

2
可能您的代码可以很好地工作,但是如果您添加一些其他信息以更好地理解,那会更好。
idmean 2014年

0

我过去曾使用过此方法,不需要进行全表扫描即可查看是否存在某些内容。超级快...

UPDATE TableName SET column=value WHERE column=value
IF @@ROWCOUNT=0
BEGIN
     --Do work
END             

0

对于那些从MySQL或Oracle后台跌跌撞撞的人-MySQL支持LIMIT子句来选择数量有限的记录,而Oracle使用ROWNUM。

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.