Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/sql/72.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在SQL中,我是否应该始终选择EXISTS而不是COUNT()>0?_Sql_Database_Performance_Sqlite_Query Performance - Fatal编程技术网

在SQL中,我是否应该始终选择EXISTS而不是COUNT()>0?

在SQL中,我是否应该始终选择EXISTS而不是COUNT()>0?,sql,database,performance,sqlite,query-performance,Sql,Database,Performance,Sqlite,Query Performance,我经常遇到这样的建议:当检查子查询中是否存在任何行时,出于性能考虑,应该使用EXISTS而不是COUNT*>0。具体地说,前者可以短路,并在查找单个行后不存在的情况下返回TRUE或FALSE,而COUNT需要实际计算每一行以返回一个数字,只需与零进行比较 在简单的情况下,这一切对我来说都很有意义。然而,我最近遇到了一个问题,我需要根据GROUPBY的HAVING子句中的所有值是否都为NULL来筛选组 为了清楚起见,让我们看一个例子。假设我有以下模式: 创建表配置文件 id整数主键, 用户id整数

我经常遇到这样的建议:当检查子查询中是否存在任何行时,出于性能考虑,应该使用EXISTS而不是COUNT*>0。具体地说,前者可以短路,并在查找单个行后不存在的情况下返回TRUE或FALSE,而COUNT需要实际计算每一行以返回一个数字,只需与零进行比较

在简单的情况下,这一切对我来说都很有意义。然而,我最近遇到了一个问题,我需要根据GROUPBY的HAVING子句中的所有值是否都为NULL来筛选组

为了清楚起见,让我们看一个例子。假设我有以下模式:

创建表配置文件 id整数主键, 用户id整数不为空, google\u帐户\u id整数为空, facebook\u帐户\u id整数为空, 外键用户id引用用户id, 检查 google\u帐户\u id不为空+facebook\u帐户\u id不为空=1 即,为简洁起见,未显示的每个用户表都有0个或更多配置文件。每个个人资料要么是谷歌账户,要么是Facebook账户。这是子类或sum类型与一些相关数据的转换-在我的实际模式中,帐户ID也是保存相关数据的不同表的外键,但这与我的问题无关

现在,假设我想统计所有没有谷歌个人资料的用户的Facebook个人资料

首先,我使用COUNT=0编写了以下查询:

选择用户\u id、Facebook\u帐户\u id 从侧面 按用户id分组 拥有COUNTgoogle\u帐户\u id=0; 但是我突然想到HAVING子句中的条件实际上只是一个存在性检查。因此,我随后使用子查询重新编写了查询,但不存在:

选择用户\u id、Facebook\u帐户\u id 从配置文件中选择p 按用户id分组 不存在 选择1 从配置文件中选择q 其中p.user\u id=q.user\u id 并且q.google_id不为空 我的问题有两个方面:

我是否应该保留第二个重新制定的查询,并将NOT EXISTS与子查询一起使用,而不是COUNT=0?这真的更有效率吗?我认为,由于WHERE p.user\u id=q.user\u id条件导致的索引查找有一些额外的成本。这一额外成本是否会被EXISTS的短路行为所吸收也可能取决于组的平均基数,不是吗

或者DBMS可能足够聪明,能够识别分组键被比较的事实,并完全优化这个子查询,方法是用当前组替换它,而不是实际为每个组执行索引查找?我严重怀疑DBMS是否能够优化掉这个子查询,而无法将COUNT=0优化为notexists

撇开效率不谈,第二个查询对我来说似乎要复杂得多,而且不太明显是正确的,所以即使它碰巧更快,我也不愿意使用它。你觉得有没有更好的办法?我可以用一种更简单的方式使用notexists,比如从HAVING子句中直接引用当前的组来吃蛋糕吗


第一个查询似乎是实现所需操作的正确方法

这已经是一个聚合查询了,因为您要计算facebook帐户。处理having子句的开销应该很小,having子句计算了谷歌的账户

另一方面,第二种方法需要重新打开表并扫描它,这可能更昂贵。

在子查询中,与计数相比,您应该更喜欢EXISTS/notexists。因此,不是:

select t.*
from t
where (select count(*) from z where z.x = t.x) > 0
您应改为使用:

select t.*
from t
where exists (select 1 from z where z.x = t.x)
原因是子查询可以在第一次匹配时停止处理

这种推理不适用于聚合后的HAVING子句——所有行都必须生成,因此在第一次匹配时停止没有什么价值

但是,如果您有一个users表并且不需要facebook计数,那么聚合可能就没有必要了。您可以使用:

select u.*
from users u
where not exists (select 1
                  from profiles p
                  where p.user_id = u.user_id and p.google_id is not null
                 );
此外,如果在聚合之前进行筛选,则聚合可能会更快:

SELECT user_id, COUNT(facebook_account_id)
FROM profile AS p
WHERE NOT EXISTS (
    SELECT 1
    FROM profile p2
    WHERE p2.user_id = p.user_id AND p2.google_id IS NOT NULL
)
GROUP BY user_id;
它是否真的更快取决于许多因素,包括实际过滤掉的行数