什么是SQL聚合函数MAX / MIN / AVG / COUNT的子查询等价物

时间:2014-03-26 19:12:33

标签: mysql sql function subquery aggregate

有人可以告诉我如何在不使用聚合函数的情况下表示以下SQL语句吗?

SELECT COUNT(column) FROM table;

SELECT AVG(column) FROM table;

SELECT MAX(column) FROM table;

SELECT MIN(column) FROM table;

4 个答案:

答案 0 :(得分:2)

MIN()MAX()可以使用简单的子查询完成:

select (select column from table order by column is not null desc, column asc limit 1) as "MIN",
       (select column from table order by column is not null desc, column desc limit 1) as "MAX"
如果您不允许任何汇总,则

COUNT()AVG()需要使用变量:

select rn as "COUNT", sumcol / rnaas "AVG"
from (select t.*
      from (select t.*,
                   (@rn := @rn + 1) as rn,
                   (@rna := @rna + if(column is not null, 1, 0)) as rna,
                   (@sum := @sum + coalesce(column, 0)) as sumcol
            from table t cross join
                 (select @rn := 0, @rna := 0, @sum := 0) const
            order by column
           ) t
      order by rn desc
      limit 1
     ) t

后一种表述仅适用于MySQL。

编辑:

空表是一个挑战。让我们使用左外连接:

select cast(coalesce(rn, 0) as int) as "COUNT",
       (case when rna > 0 then sumcol / rna end) as "AVG"
from (select 1 as n
     ) n left outer join
     (select t.*
      from (select t.*,
                   (@rn := @rn + 1) as rn,
                   (@rna := @rna + if(column is not null, 1, 0)) as rna,
                   (@sum := @sum + coalesce(column, 0)) as sumcol
            from table t cross join
                 (select @rn := 0, @rna := 0, @sum := 0) const
            order by column
           ) t
      order by rn desc
      limit 1
     ) t
     on n.n = 1;

注记。如果表为空,这将返回计数0。那是正确的。如果表格为空,则会返回NULL作为平均值,这也是正确的。

如果表不为空,但值均为NULL,则它也将返回NULL。计数的类型总是整数,所以应该没问题。平均值的类型更有问题,但变量将返回某种通用数字类型,这似乎在精神上是兼容的。

答案 1 :(得分:1)

min / max可以替换为:

select t1.pk_column, 
       t1.some_column
from the_table t1
where t1.some_column < ALL (select t2.some_column
                            from the_table t2
                            where t2.pk_column <> t2.pk_column);

要获取max,您需要将<替换为>pk_column是表的主键列,需要避免将每一行与自身进行比较(它不会 成为PK,它只需要是唯一的)

我认为count()avg()没有替代品(至少我想不到一个)

我使用了the_columnthe_table,因为columntable是保留字

答案 2 :(得分:0)

SET @ t1 = 0,@ t2 = 0,@ t3 = 0,@ T4 = 0;

COUNT:

Select @t1:=@t1+1 as CNT from table
order by @t1:=@t1+1 DESC
LIMIT 1

使用限制可以将相似的方法放在一起用于平均值和最大值/分钟...

还在考虑Min / Max ......

答案 3 :(得分:0)

不要取代Gordon Linoff的优秀答案,但要准确模仿AVG()COUNT()SUM()函数还需要做更多的工作。 (Gordon的答案中MIN和MAX函数的答案都是正确的。)

当桌子为空时有一个角落的情况。为了模拟SQL聚合函数,我们需要查询返回单行。但与此同时,我们需要测试该表是否至少包含一行。

这是一个更精确的仿真查询:

-- create an empty table
CREATE TABLE `foo` (col INT);
-- TRUNCATE TABLE `foo`;

SELECT IF(s.ne IS NULL,0,s.rn)   AS `COUNT(*)`
     , IF(s.cc>0,s.tc,NULL)      AS `SUM(col)`
     , IF(s.cc>0,s.tc/s.cc,NULL) AS `AVG(col)`
  FROM ( SELECT v.rn
              , v.cc
              , v.tc
              , e.ne
           FROM ( SELECT @rn  := @rn + 1                    AS rn
                       , @cc  := @cc + (t.col IS NOT NULL)  AS cc
                       , @tc  := @tc + IFNULL(t.col,0)      AS tc
                    FROM (SELECT @rn := 0, @cc := 0, @tc := 0) c
                    LEFT
                    JOIN `foo` t
                      ON 1=1
                 ) v
            LEFT
            JOIN (SELECT 1 AS ne FROM `foo` z LIMIT 1) e
              ON 1=1
           ORDER BY v.rn DESC
           LIMIT 1
       ) s

备注:

作为e别名的内联视图的目的是为我们提供一种方法来确定表是否包含任何行。如果表包含至少一行,我们将获得一个值为1的列ne(非空)。如果表为空,则该查询不会返回一行,e.ne将为NULL,这是我们可以在外部查询中测试的。

为了返回一行,我们可以为COUNT返回一个值,如0,我们需要确保从内联视图v返回至少一行。由于我们保证内联视图中的一行别名为c(初始化我们的用户定义变量),因此我们将其用作LEFT [OUTER] JOIN操作的“驱动”表。

但是,如果表格为空,我们的@rn出来的行计数器(v)的值为1.但是我们会处理这个,我们有e.ne我们可以检查计数是否真的应该返回0。

为了计算平均值,我们不能除以行计数器,我们必须除以col不为空的行数。我们使用@cc用户定义的变量来跟踪这些行的数量。

类似地,对于SUM(和平均值),我们只需要累积非NULL值。 (如果我们要添加一个NULL,它会将整个总数变为NULL,基本上擦除就是积累。所以,我们要做一个条件测试来检查t.col是否为NULL,以避免意外消除如果没有任何行不是null,我们的累加器将为0。但这不是问题,因为我们将确保检查我们的@cc以查看是否有任何行包括在内。我们无论如何都需要检查它,以避免“除以零”问题。

要测试,请对空表foo运行。它将返回0的计数,并为SUM和AVG返回NULL,相当于我们得到的结果:

SELECT COUNT(*), SUM(col), AVG(col) FROM foo;

我们还可以针对仅包含col的NULL值的表测试查询:

INSERT INTO `foo` (col) VALUES (NULL);

以及一些非NULL值:

INSERT INTO `foo` (col) VALUES (2),(3),(5),(7),(11),(13),(17),(19);

并比较两个查询的结果。


这与Gordon Linoff的答案基本相同,只需稍微精确一点就可以解决NULL值和空表的角点问题。