TSQL - 检查多条记录的最快方法是什么?

时间:2012-12-18 12:20:01

标签: sql-server performance tsql

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

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。

5 个答案:

答案 0 :(得分:7)

SQL Server通常不会使聚合查询短路。有时,它可以转换HAVING COUNT(*) > 0查询以使用与EXISTSdiscussed in the comments here)相同的计划,但这是最直接的。

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

考虑到这一点,我会使用

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

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

两个版本的示例计划如下

Plans

关于

的评论中的问题
  

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

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

在这种情况下,很明显这只是代表了额外的工作。在其他计划中它可能不是。添加TOP 2可能会显着改变其下方的查询树(例如,使用blocking iterators对计划进行攻击)

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

SQL Kiwi在this recent answer on the DBA site

中做得很好
  

优化器成本估算主要仅对内部服务器有用   目的。它们不打算用于评估潜力   表现,即使是在“高水平”。该模型是一种抽象   恰好在内部目的下工作得相当好   专为。估计成本承担任何合理的可能性   与您的硬件和配置上的实际执行成本相似   确实很小。

     

根据实际情况选择其他指标来比较效果   问题对你很重要。

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

最后,它只是得到一个查询计划,该计划可以有效地利用服务器上的资源。

答案 1 :(得分: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

答案 2 :(得分:1)

不要打扰topselect 1

if exists (select * ...)

同样快。

答案 3 :(得分: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'

enter image description here

答案 4 :(得分:0)

不确定效果,但您可以使用CTECOUNT(*)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

Demo