是否有类似Oracle SQL中的SUM函数的PRODUCT函数?

时间:2008-12-31 19:55:56

标签: sql oracle

我有一位同事在寻找这个,我不记得曾经遇到过这样的事情。

是否有合理的技术可以让你模拟它?

SELECT PRODUCT(X)
FROM
(
    SELECT 3 X FROM DUAL
    UNION ALL 
    SELECT 5 X FROM DUAL
    UNION ALL
    SELECT 2 X FROM DUAL
)

将产生30

7 个答案:

答案 0 :(得分:26)

select exp(sum(ln(col)))
  from table;

编辑:

如果总是> 0

答案 1 :(得分:3)

DECLARE @a int
SET @a = 1
-- re-assign @a for each row in the result
-- as what @a was before * the value in the row
SELECT @a = @a * amount
FROM theTable

有一种类似字符串连接的方法:

DECLARE @b varchar(max)
SET @b = ""

SELECT @b = @b + CustomerName
FROM Customers

答案 2 :(得分:3)

这是另一种方法。这绝对是更长的方式,但它是一个有趣的项目的一部分。

你必须回到学校这一个,哈哈。他们要记住的关键是LOG是Exponent的倒数。

LOG10(X * Y)= LOG10(X)+ LOG10(Y)

ln(X * Y)= ln(X)+ ln(Y)(ln =自然对数,或简称Log base 10)

实施例
如果X = 5且Y = 6

X * Y = 30

ln(5)+ ln(6)= 3.4

ln(30)= 3.4

e ^ 3.4 = 30,5 x 6

也是如此

EXP(3.4)= 30

如上所述,如果5和6各占一个表中的一行,我们取每个值的自然对数,总结行,然后取总和的指数得到30。

以下是SQL Server的SQL语句中的代码。可能需要进行一些编辑才能使其在Oracle上运行。希望这不是一个很大的区别,但我怀疑至少CASE声明在Oracle上是不一样的。你会注意到那里有一些额外的东西来测试行的符号是否为负数。

CREATE TABLE DUAL (VAL INT NOT NULL)
INSERT DUAL VALUES (3)
INSERT DUAL VALUES (5)
INSERT DUAL VALUES (2)

    SELECT 
           CASE SUM(CASE WHEN SIGN(VAL) = -1 THEN 1 ELSE 0 END) % 2 
               WHEN 1 THEN -1 
               ELSE 1 
           END
         * CASE 
                WHEN SUM(VAL) = 0           THEN 0 
                WHEN SUM(VAL) IS NOT NULL   THEN EXP(SUM(LOG(ABS(CASE WHEN SIGN(VAL) <> 0 THEN VAL END)))) 
                ELSE NULL 
           END
         * CASE MIN(ABS(VAL)) WHEN 0 THEN 0 ELSE 1 END
        AS PRODUCT 
      FROM DUAL

答案 3 :(得分:0)

“SQL”有许多不同的含义。当您说“sql have”时,您指的是特定的ANSI版本的SQL或供应商特定的实现。 DavidB的答案是在我测试的几个不同环境中工作的答案,但根据您的环境,您可以编写或找到与您要求的完全相同的功能。假设您使用的是Microsoft SQL Server 2005,那么可能的解决方案是在名为PRODUCT的.net代码中编写自定义聚合器,这样可以使您的原始查询完全按照您编写的方式工作。

答案 4 :(得分:0)

在c#中你可能需要这样做:

SELECT EXP(SUM(LOG([col]))) 
  FROM table;

答案 5 :(得分:0)

accepted answer by tuinstoel是正确的,当然:

select exp(sum(ln(col)))
  from table;

但是请注意,如果col的类型为NUMBER,当您使用BINARY_DOUBLE时,将会发现巨大的性能改进。理想情况下,您的表中将有一个BINARY_DOUBLE列,但是如果不可能,您仍然可以将col强制转换为BINARY_DOUBLEI got a 100x improvement in a simple test that I documented here,为此演员:

select exp(sum(ln(cast(col as binary_double))))
  from table;

答案 6 :(得分:0)

<块引用>

是否有一种合理的技术可以让您模拟它?

一种技术可能是使用 LISTAGG 生成 product_expression 字符串并使用 XMLTABLE + GETXMLTYPE 来评估它:

WITH cte AS (
  SELECT grp, LISTAGG(l, '*') AS product_expression
  FROM t
  GROUP BY grp
)
SELECT c.*, s.val AS product_value
FROM cte c
CROSS APPLY(
    SELECT *
    FROM XMLTABLE('/ROWSET/ROW/*' 
         PASSING dbms_xmlgen.getXMLType('SELECT ' || c.product_expression || ' FROM dual')
         COLUMNS val NUMBER PATH '.')
) s;

db<>fiddle demo

输出:

+------+---------------------+---------------+
| GRP  | PRODUCT_EXPRESSION  | PRODUCT_VALUE |
+------+---------------------+---------------+
| b    | 2*6                 |            12 |
| a    | 3*5*7               |           105 |
+------+---------------------+---------------+

处理组中单个 NULL 值的更健壮的版本:

WITH cte AS (
  SELECT grp, LISTAGG(l, '*') AS product_expression
  FROM t
  GROUP BY grp
)
SELECT c.*, s.val AS product_value
FROM cte c
OUTER APPLY(
   SELECT *
   FROM XMLTABLE('/ROWSET/ROW/*' 
        passing dbms_xmlgen.getXMLType('SELECT ' || c.product_expression || ' FROM dual')
        COLUMNS val NUMBER PATH '.')
   WHERE c.product_expression IS NOT NULL
) s;

db<>fiddle demo

*CROSS/OUTER APPLY(Oracle 12c) 是为了方便起见,可以替换为嵌套子查询。


这种方法可用于生成不同的聚合函数。