为什么SQL中没有PRODUCT聚合函数?

时间:2010-10-12 06:42:11

标签: sql aggregate

我正在寻找类似于SELECT PRODUCT(table.price) FROM table GROUP BY table.sale工作的SUM之类的内容。

我是否遗漏了文档中的内容,或者确实没有PRODUCT函数?

如果是这样,为什么不呢?

注意:我在postgres,mysql和mssql中查找了函数,发现没有,所以我假设所有的sql都不支持它。

10 个答案:

答案 0 :(得分:49)

对于MSSQL,您可以使用它。它可以用于其他平台:它只是数学和对数的聚合。

SELECT
    GrpID,
    CASE
       WHEN MinVal = 0 THEN 0
       WHEN Neg % 2 = 1 THEN -1 * EXP(ABSMult)
       ELSE EXP(ABSMult)
    END
FROM
    (
    SELECT
       GrpID, 
       --log of +ve row values
       SUM(LOG(ABS(NULLIF(Value, 0)))) AS ABSMult,
       --count of -ve values. Even = +ve result.
       SUM(SIGN(CASE WHEN Value < 0 THEN 1 ELSE 0 END)) AS Neg,
       --anything * zero = zero
       MIN(ABS(Value)) AS MinVal
    FROM
       Mytable
    GROUP BY
       GrpID
    ) foo

取自我的回答:SQL Server Query - groupwise multiplication

答案 1 :(得分:26)

SQL Standard中没有PRODUCT设置功能。它似乎是一个有价值的候选者(不像,例如,CONCATENATE设置函数:它不适合SQL,例如,结果数据类型将涉及多值并且就第一范式形成问题)。

SQL标准旨在整合1990年左右的SQL产品功能,并为未来发展提供“思想领导力”。简而言之,它们记录了SQL的作用以及SQL应该做什么。没有PRODUCT集函数表明,1990年没有供应商,但它值得包含,并且没有学术兴趣将其引入标准。

当然,供应商总是试图添加自己的功能,这些天通常作为标准的扩展而不是切线。我不记得在我使用的任何SQL产品中看到PRODUCT设置功能(甚至需要一个功能)。

在任何情况下,使用logexp标量函数(以及处理负数的逻辑)使用SUM设置函数,可以非常简单地解决这个问题。有关示例代码,请参阅@ gbn的答案。不过,我从来不需要在商业应用程序中这样做。

总之,我最好的猜测是SQL终端用户不需要PRODUCT设置功能;此外,任何具有学术兴趣的人都可能会找到可接受的解决方法(即不会重视PRODUCT集函数提供的语法糖。)

出于兴趣,SQL Server Land确实需要新的集合函数,但对于窗口函数种类(以及标准SQL)也是如此。有关详细信息,包括如何参与进一步的驾驶需求,请参阅Itzik Ben-Gan's blog

答案 2 :(得分:21)

我不知道为什么没有一个,但是(更多地关注负数)你可以使用日志和指数来做: -

select exp (sum (ln (table.price))) from table ...

答案 3 :(得分:8)

您可以执行产品聚合功能,但您必须自己进行数学计算,例如......

SELECT
    Exp(Sum(IIf(Abs([Num])=0,0,Log(Abs([Num])))))*IIf(Min(Abs([Num]))=0,0,1)*(1-2*(Sum(IIf([Num]>=0,0,1)) Mod 2)) AS P
FROM
   Table1

来源:http://productfunctionsql.codeplex.com/

答案 4 :(得分:6)

T-SQL中有一个巧妙的技巧(不确定它是否为ANSI)允许将一组行中的字符串值连接成一个变量。看起来它也适用于倍增:

declare @Floats as table (value float)
insert into @Floats values (0.9)
insert into @Floats values (0.9)
insert into @Floats values (0.9)

declare @multiplier float = null

select 
    @multiplier = isnull(@multiplier, '1') * value
from @Floats

select @multiplier

这可能比log / exp解决方案在数值上更稳定。

答案 5 :(得分:3)

我认为这是因为没有编号系统能够容纳许多产品。由于数据库是针对大量记录而设计的,因此1000个数字的乘积将是超大型的,并且在浮点数的情况下,传播的误差将是巨大的。

另请注意,使用日志可能是一个危险的解决方案。虽然在数学上log(a * b)= log(a)* log(b),但它可能不在计算机中,因为我们没有处理实数。如果计算2 ^(log(a)+ log(b))而不是* b,则可能会得到意外结果。例如:

SELECT 9999999999 * 99999999974482,EXP(LOG(9999999999)+ LOG(99999999974482))

在Sql Server中返回

999999999644820000025518,9.99999999644812E + 23

所以我的观点是,当您尝试做产品时,请仔细进行测试并进行大量测试。

答案 6 :(得分:1)

解决此问题的一种方法(如果您使用的是脚本语言)是使用group_concat函数。 例如,SELECT group_concat(table.price) FROM table GROUP BY table.sale

这将返回一个字符串,其中包含相同销售价值的所有价格,以逗号分隔。 然后使用解析器,您可以获得每个价格,并进行乘法运算。 (在php中你甚至可以使用array_reduce函数,实际上在php.net manual你得到一个合适的例子)。

干杯

答案 7 :(得分:0)

可以使用现代SQL功能(例如窗口函数和CTE)解决该问题。一切都是标准SQL,并且与基于对数的解决方案不同,它不需要从整数世界切换到浮点世界,也不需要处理非正数。只需对行编号并在递归查询中评估产品,直到没有剩余行:

   with recursive t(c) as (
     select unnest(array[2,5,7,8])
   ), r(c,n) as (
     select t.c, row_number() over () from t
   ), p(c,n) as (
     select c, n from r where n = 1
     union all
     select r.c * p.c, r.n from p join r on p.n + 1 = r.n
   )
   select c from p where n = (select max(n) from p);

由于您的问题涉及按销售列分组,因此情况有些复杂,但仍然可以解决:

   with recursive t(sale,price) as (
     select 'multiplication', 2 union
     select 'multiplication', 5 union
     select 'multiplication', 7 union
     select 'multiplication', 8 union
     select 'trivial', 1 union
     select 'trivial', 8 union
     select 'negatives work', -2 union
     select 'negatives work', -3 union
     select 'negatives work', -5 union
     select 'look ma, zero works too!', 1 union
     select 'look ma, zero works too!', 0 union
     select 'look ma, zero works too!', 2
   ), r(sale,price,n,maxn) as (
     select t.sale, t.price, row_number() over (partition by sale), count(1) over (partition by sale)
     from t
   ), p(sale,price,n,maxn) as (
     select sale, price, n, maxn
     from r where n = 1
     union all
     select p.sale, r.price * p.price, r.n, r.maxn
     from p
     join r on p.sale = r.sale and p.n + 1 = r.n
   )
   select sale, price
   from p
   where n = maxn
   order by sale;

结果:

sale,price
"look ma, zero works too!",0
multiplication,560
negatives work,-30
trivial,8

在Postgres上测试。

答案 8 :(得分:0)

基于笛卡尔积的基数是特定集合的基数的结果的另一种方法;-)

⚠警告:此示例仅出于娱乐目的,并且颇具学术意义,请勿在生产中使用! (除了它仅用于正整数和实际上很小的整数)⚠

with recursive t(c) as (
  select unnest(array[2,5,7,8])
), p(a) as (
  select array_agg(c) from t
  union all
  select p.a[2:]
  from p
  cross join generate_series(1, p.a[1])
)
select count(*) from p where cardinality(a) = 0;

答案 9 :(得分:0)

一种方法是结合两个内置函数,如下所示:

SELECT table.sale, AVG(table.price) * COUNT(table.price) FROM table GROUP BY table.sale