8

有时我需要检查是否存在至少一条记录,通常我使用:

IF EXISTS (SELECT TOP 1 1 FROM [SomeTable] WHERE [Fields] = [Values]) BEGIN
-- action
END

有没有一种快速的方法来检查是否存在多个记录?我可以做类似的事情:

IF EXISTS (SELECT 1 FROM [SomeTable] 
                        WHERE [Fields] = [Values] 
                                HAVING Count(*) > 1) 
BEGIN
    -- action
END

但我不确定这是否是最快的方法,因为它将测试集合中的所有记录。有更快的方法吗?

“where”部分可能非常复杂,可能包含多个 AND 和 OR。

4

5 回答 5

8

SQL Server 通常不会短路聚合查询。有时它可以将查询转换为使用与(在此处的评论中讨论的HAVING COUNT(*) > 0)相同的计划,但仅此而已。EXISTS

查询将HAVING COUNT(*) > 1 始终计算所有行,即使理论上它可能会在第 2 行之后停止计数。

考虑到这一点,我会使用

IF EXISTS(
  SELECT * FROM (
                 SELECT TOP 2 *
                 FROM [SomeTable] 
                 WHERE [Fields] = [Values] 
) T
HAVING COUNT(*)=2) 

迭代器将在TOP 2返回第二个行后停止请求行,从而允许内部查询提前短路,而不是全部返回并计算它们。

两个版本的示例计划如下

计划

关于评论中的问题

“你怎么知道哪个最好?是查询成本吗?”

在计划中显示的特定情况下,成本将是一个合理的指示,因为估计的和实际的行数非常准确,并且两个计划非常相似,除了添加了TOP迭代器。因此,计划中显示的额外成本完全表示需要扫描(并且可能从磁盘读取)并计算额外数量的行。

在这种情况下,很明显,这只是代表额外的工作。在其他计划中可能不是。添加TOP 2可能会显着改变它下面的查询树(例如,不喜欢带有阻塞迭代器的计划)

在这种情况下,执行计划中显示的成本可能不是一个可靠的指标。即使在实际执行计划中,显示的成本也是基于估计的,因此仅与那些一样好,即使估计的行数很好,显示的成本仍然只是基于某些建模假设。

SQL Kiwi在 DBA 网站上的这个最近的回答中很好地说明了这一点

优化器成本估算主要仅对内部服务器有用。它们不是用来评估潜在性能的,即使是在“高水平”。该模型是一种抽象,恰好可以很好地用于其设计的内部目的。估计成本与硬件和配置上的实际执行成本有任何明显相似之处的可能性确实非常小。

根据对您来说重要的任何实际问题,选择其他指标来比较性能。

逻辑读取(在 时显示SET STATISTICS IO ON;)是一种可以查看的指标,但再次专注于这一点可能会产生误导。测试查询持续时间可能是唯一可靠的方法,但即使这也不是一门精确的科学,因为性能可能会因服务器上的并发活动而异(等待内存授予、可用 DOP、缓存中相关页面的数量)。

最后,它只是归结为获得一个看起来可以有效利用服务器上资源的查询计划。

于 2012-12-18T12:46:35.103 回答
2

我确信有一些技巧可以让您更快地执行此检查 - 尽管它在很大程度上取决于您的架构(尤其是索引),并且特定检查可能适用于一种情况而不适用于另一种情况。

像下面这样的东西可能对你有用。

IF EXISTS (SELECT * FROM [SomeTable] T1
           INNER JOIN [SomeTable] T2
           ON T1.UniqueID <> T2.UniqueID
           WHERE T1.[Fields] = T1.[Values]
           AND T2.[Fields] = T2.[Values]) 
BEGIN
    -- action
END
于 2012-12-18T12:43:30.907 回答
1

不要打扰topor select 1

if exists (select * ...)

一样快。

于 2012-12-18T12:28:26.897 回答
1

我在具有 1900 万条记录的表中使用此解决方案获得了出色的性能:

IF EXISTS (
        SELECT '1' FROM (
            SELECT TOP(2) '1' AS 'N'
            FROM TBL_KV3) AS Z
            HAVING COUNT(*) > 1     
            ) 
    SELECT '1'
ELSE
    SELECT '0'

在此处输入图像描述

于 2014-08-29T08:17:06.523 回答
0

不确定性能,但您可以使用CTEand COUNT(*)OVER

WITH Match AS
(
    SELECT t1.*, COUNT(*)OVER(PARTITION BY t1.Fields)AS CountFields
    FROM SomeTable t1
    WHERE t1.Fields=@Values
)
SELECT m1.* 
FROM Match m1
WHERE CountFields >= 2

演示

于 2012-12-18T12:36:52.663 回答