【问题标题】:SQL: Making COUNT(*) > 1 EfficientSQL:使 COUNT(*) > 1 高效
【发布时间】:2011-02-10 14:44:59
【问题描述】:

如果你想知道COUNT(*) > 0 那么你可以使用EXISTS 来提高查询效率。当我想知道COUNT(*) > 1 时,有没有一种方法可以提高查询效率?

(需要同时兼容 SQL Server 和 Oracle。)

谢谢,杰米

编辑:

我正在尝试提高某些代码的性能。有几行类似于:

if (SQL('SELECT COUNT(*) FROM table WHERE a = b') > 0) then...

if (SQL('SELECT COUNT(*) FROM table WHERE a = b') > 1) then...

第一行很容易切换到EXISTS 语句,但是我可以让第二行更高效吗?从cmets和我自己的想法我有以下想法,它们中的任何一个会更有效吗?

if (SQLRecordCount('SELECT TOP 2 1 FROM table WHERE a = b') > 1) then...

(我可以将ROWNUM 用于Oracle。)

if (SQL('SELECT 1 FROM table WHERE a = b HAVING COUNT(*) > 1') = 1) then...

以下内容在 SQL Server 中不起作用:

SELECT COUNT(*) FROM (SELECT TOP 2 FROM table WHERE a = b)

但这适用于 Oracle:

SELECT COUNT(*) FROM (SELECT 1 FROM table WHERE a = b AND ROWNUM < 3)

感谢您迄今为止的所有帮助。

【问题讨论】:

  • 我不确定这是否只是针对 MySQL 的优化,但您是否尝试过 COUNT(primaryKeyField) &gt; 1COUNT(1) &gt; 1 看看是否可以更好地利用索引?
  • @Ardman - 我认为问题是关于某些基数的,比如每个组有 1,000 行,并且可以在前 2 场匹配后停止扫描,类似于 EXISTS 不需要 COUNT所有匹配的行。
  • 我可以想到使用TOPCROSS APPLY 的SQL Server 特定方式。
  • @Martin 也发布那个 :-)
  • @pst - 完成。我已经把它写成 cw 并且可能会在稍后删除这个问题的主题......

标签: sql performance count


【解决方案1】:

这样的事情可能会起作用:

select myDate
from myTable
where myColumn = myCondition
group by myDate
having count(*) > 1

虽然如果我有您的确切查询,或合理的传真,我可以为您提供更多帮助。

就实际关键字更有效而言,据我所知,作为 SQL 程序员,您对此无能为力。这将成为您的 RDBMS 处理实际计数的功能。如果它看到如果有 2 次出现它会返回该行并且在 2 处停止计数,那就太好了。如果它不够智能并跟踪另外 1,000 次事件,那就不太好。

如果您在连接或子查询中使用它,您可以控制在查询或存储过程的不同点返回的行数。越早过滤掉注定永远不会返回的行越好。

【讨论】:

  • 我不认为 OP 在询问语法。据我了解,他们问他们是否可以避免计算一组中的所有行,而实际上他们只需要知道是否存在不止一个匹配的行。
  • 我在假设 OP 不知道 HAVING 关键字的情况下工作,这合理地导致他们也不知道语法的假设。此语法提供超过 1 行的值,但不提供实际计数。另外,如果一个值出现超过 1 次,你怎么知道而不是在途中的某个地方计算它们?
【解决方案2】:

你现在的问题有点抽象。你能提供更多的上下文吗?

我在想,如果您在 foo, id 上有一个复合索引,那么两个索引搜索可以满足以下条件。

SELECT CASE WHEN MAX(id)= MIN(id) THEN 0 ELSE 1 END
FROM yourtable 
WHERE foo='bar'

或者也许更明确地强制计划

SELECT CASE WHEN COUNT(*) = 2 THEN 1 ELSE 0 END FROM 
(
    SELECT MAX(id)
    FROM yourtable 
    WHERE foo='bar'
    UNION
    SELECT MIN(id)
    FROM yourtable 
    WHERE foo='bar'
) AS T

【讨论】:

    【解决方案3】:

    如果被索引应该没关系

    例子:

    200 万行表,相当宽,900MB 磁盘,虚拟 SQL Server 2005。

    这给出了 17,876 行

    SELECT COUNT(*), ThingID FROM dbo.TwoMillion IT GROUP BY ThingID HAVING COUNT(*) > 1
    
      |--Filter(WHERE:([Expr1002]>(1)))
           |--Compute Scalar(DEFINE:([Expr1002]=CONVERT_IMPLICIT(int,[Expr1005],0)))
                |--Hash Match(Aggregate, HASH:([IT].[ThingID]) DEFINE:([Expr1005]=COUNT(*)))
                     |--Index Scan(OBJECT:([MyDB].[dbo].[TwoMillion].[IX_Thing] AS [IT]))
    
    Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0
    Table 'TwoMillion'. Scan count 1, logical reads 8973, physical reads 3, read-ahead reads 8969... all zeroes
    

    第二次运行

     Table 'Worktable'. = same
     Table 'TwoMillion'. Scan count 1, logical reads 8973, ... all zeroes
    
     CPU time = 453 ms,  elapsed time = 564 ms.
    

    【讨论】:

    • +1 但是......根据那个,这仍然是大约半秒:-/也许不是破纪录的,但也不是我认为的超快。
    • @Pst:虚拟服务器。在物理盒子上快 5 倍。这是一张宽大的桌子
    【解决方案4】:

    完全忽略 SQL Server 中的交叉兼容性要求,您可以使用TOP 显式限制扫描的行数。在某些情况下,这可能是有益的,如下面的(有些人为的)示例所示。

    USE tempdb
    
    CREATE TABLE Orders
    (
    OrderId INT IDENTITY(1,1) PRIMARY KEY,
    Blah VARCHAR(10)
    )
    INSERT INTO Orders 
    SELECT TOP 10 LEFT(name,10)
    FROM sys.objects
    
    CREATE TABLE OrderItems
    (
    OrderItemId INT IDENTITY(1,1) PRIMARY KEY,
    OrderId INT REFERENCES Orders(OrderId)
    )
    CREATE NONCLUSTERED INDEX ix ON OrderItems(OrderId)
    
    INSERT INTO OrderItems (OrderId)
    SELECT TOP 1000000 1+ ROW_NUMBER() OVER (ORDER BY (SELECT 0))% 10 
    FROM sys.all_columns c1, sys.all_columns c2
    
    SET STATISTICS IO ON
    SET STATISTICS TIME ON
    
    SELECT o.OrderId, o.Blah
    FROM Orders o JOIN OrderItems oi ON o.OrderId = oi.OrderId
    GROUP BY o.OrderId, o.Blah
    HAVING COUNT(*) > 1
    
    /*
    Table 'Orders'. Scan count 0, logical reads 20
    Table 'OrderItems'. Scan count 1, logical reads 1742
    */
    
    
    SELECT o.OrderId, o.Blah
    FROM Orders o 
    CROSS APPLY 
    (SELECT TOP 2 OrderItemId FROM 
    OrderItems oi WHERE o.OrderId = oi.OrderId) CA
    GROUP BY o.OrderId, o.Blah
    HAVING COUNT(*) > 1
    
    /*
    Table 'OrderItems'. Scan count 10, logical reads 30
    Table 'Orders'. Scan count 1, logical reads 2
    */
    
    DROP TABLE OrderItems
    DROP TABLE Orders
    

    【讨论】:

      【解决方案5】:

      我发现以下行显着提高了 SQL Server 的性能,在我的测试中从大约 40 毫秒提高到大约 5 毫秒。

      SELECT COUNT(*) FROM (SELECT TOP 2 1 AS x FROM table Where a = b) AS y
      

      注意别名,它们是使查询正常工作所必需的。

      不幸的是,以下查询似乎没有提高 Oracle 的性能:

      SELECT COUNT(*) FROM table WHERE a = b AND ROWNUM < 3
      

      【讨论】:

        【解决方案6】:

        首先,如果要优化查询,则不应使用星号。

        也许创建一个有限制的查询会更好?你对计数或类似的东西不感兴趣。您只想知道是否有多个条目:

        select id
        from mytable
        where ...
        limit 2
        

        这应该很快。比调用 countRows 提供给你的答案来获得你需要的答案。

        【讨论】:

        • count()我没说,我说他不应该用。如果他在 COUNT() 中使用星号,则他可能在 SELECT 查询中使用星号。因此我告诉他避免使用星号!就像你说的,在 COUNT() 中使用星号没有区别!因此,您为什么要使用星号?您“可以”从您的 sql 查询中完全禁止它而不会造成伤害。
        猜你喜欢
        • 2012-06-30
        • 1970-01-01
        • 1970-01-01
        • 2015-02-27
        • 1970-01-01
        • 1970-01-01
        • 2011-10-30
        • 2021-09-06
        • 2016-04-17
        相关资源
        最近更新 更多