如何在WHERE子句中使用实际行计数(COUNT(*))而不编写与子查询相同的查询?

时间:2012-10-28 09:24:06

标签: mysql sql sqlite count

我有这样的事情:

SELECT id, fruit, pip 
FROM   plant 
WHERE  COUNT(*) = 2;

我猜这个奇怪的查询是不言自明的。 COUNT(*)这里表示plant表中的行数。 我的要求是,只有当表格中的总行数= 2时才需要从指定字段中检索值。这不起作用,但是:invalid use of aggregate function COUNT

我不能这样做:

SELECT COUNT(*) as cnt, id, fruit, pip 
FROM   plant 
WHERE  cnt = 2;

为1,它将输出的行数限制为1和2,它给出了相同的错误:invalid use of aggregate function

我能做的是:

SELECT id, fruit, pip 
FROM   plant 
WHERE  (
        SELECT COUNT(*) 
        FROM   plant
       ) = 2;

但是那个子查询是重新运行的主要查询。我在这里提出了一个问题较大部分的一个小例子,虽然我知道给定示例中的一个额外的COUNT(*)子查询并不是那么大的开销。

编辑:我不知道为什么这个问题被低估了。我想要获取的COUNT(*)来自查询中的一个视图(临时表),这是一个包含5到6个连接和其他where子句的大型查询。要将查询作为子查询重新运行以获取计数是低效的,我也可以看到瓶颈。

以下是实际查询:

SELECT U.UserName, E.Title, AE.Mode, AE.AttemptNo, 
   IF(AE.Completed = 1, 'Completed', 'Incomplete'), 
   (
    SELECT COUNT(DISTINCT(FK_QId)) 
    FROM   attempt_question AS AQ
    WHERE  FK_ExcAttemptId = @excAttemptId
   ) AS Inst_Count, 
   (
    SELECT    COUNT(DISTINCT(AQ.FK_QId)) 
    FROM      attempt_question AS AQ
    JOIN      `question` AS Q 
          ON  Q.PK_Id = AQ.FK_QId                      
    LEFT JOIN actions AS A                             
           ON A.FK_QId = AQ.FK_QId
    WHERE     AQ.FK_ExcAttemptId = @excAttemptId
         AND (
                  Q.Type = @descQtn 
              OR  Q.Type = @actQtn 
              AND A.type = 'CTVI.NotImplemented'       
              AND A.IsDelete = @status                 
              AND (
                    SELECT COUNT(*) 
                    FROM   actions 
                    WHERE  FK_QId = A.FK_QId 
                       AND type != 'CTVI.NotImplemented'
                       AND IsDelete = @status
                   ) = 0
             )
   ) AS NotEvalInst_Count,  
   (
    SELECT COUNT(DISTINCT(FK_QId)) 
    FROM   attempt_question AS AQ
    WHERE  FK_ExcAttemptId = @excAttemptId 
       AND Mark = @mark
   ) AS CorrectAns_Count, 
   E.AllottedTime, AE.TimeTaken
FROM   attempt_exercise AS AE
JOIN   ctvi_exercise_tblexercise AS E 
    ON AE.FK_EId = E.PK_EId
JOIN   ctvi_user_table AS U 
    ON AE.FK_UId = U.PK_Id
JOIN   ctvi_grade AS G 
    ON AE.FK_GId = G.PK_GId
WHERE  AE.PK_Id = @excAttemptId
-- AND COUNT(AE.*) = @number --the portion in contention.

请忽略上述查询,并引导我从我发布的小示例查询中找到正确的方向,谢谢。

3 个答案:

答案 0 :(得分:7)

在MySQL中,你只能做你尝试过的事情:

SELECT id, fruit, pip 
FROM   plant 
WHERE  (
        SELECT COUNT(*) 
        FROM   plant
       ) = 2;

或此变体:

SELECT id, fruit, pip 
FROM   plant 
  JOIN
      (
        SELECT COUNT(*) AS cnt 
        FROM   plant
       ) AS c
    ON c.cnt = 2;

第一个或第二个是否更有效,取决于MySQL(和优化器)的版本。在大多数版本中,我打赌第二个。

在其他具有窗口功能的DBMS中,您也可以执行@Andomar建议的第一个查询。


这是一个建议,以避免计算派生表两次的瓶颈,一次得到行,再一次得到计数。如果派生表的计算成本很高,并且其行数千或数百万,那么计算它们两次只是为了抛弃它们,确实是一个问题。这可以提高效率,因为它将中间(两次)计算的行限制为3:

SELECT  p.*
FROM
    ( SELECT id, fruit, pip 
      FROM   plant 
      LIMIT 3
    ) AS p
  JOIN
    ( SELECT COUNT(*) AS cnt
      FROM   
        ( SELECT 1 
          FROM   plant 
          LIMIT 3
        ) AS tmp
    ) AS c
    ON c.cnt = 2 ;

答案 1 :(得分:5)

重新阅读问题后,只有在整个表格中有2行时才会尝试返回行。在这种情况下,我认为您自己的示例查询已经是最好的了。

在另一个DBMS上,您可以使用窗口函数:

select  *
from    (
        select  *
        ,       count(*) over () as cnt
        from    plant
        ) as SubQueryAlias
where   cnt = 2

但MySQL不支持over子句。

旧的错误的anser

where子句在分组之前有效。它适用于单行,而不适用于行组,因此您无法在count子句中使用maxwhere等聚合。

要设置适用于行组的过滤器,请使用having子句。它在分组后有效,可用于过滤聚合:

SELECT id, fruit, pip 
FROM   plant 
GROUP BY
       id, fruit, pip 
HAVING COUNT(*) = 2;

答案 2 :(得分:1)

其他答案不符合原始问题,即在不使用子查询的情况下过滤结果。

您实际上可以通过在2个连续的MySQL语句中使用变量来实现此目的:

SET @count=0;

SELECT * FROM
(
    SELECT id, fruit, pip, @count:=@count+1 AS count
    FROM   plant 
    WHERE
) tmp
WHERE @count = 2;