¿Cómo multiplicar filas para una columna que contiene valores negativos y cero?

10

Estoy tratando de obtener el Producto de todas las filas para una columna específica en una consulta agrupada por. La mayoría de los ejemplos que he encontrado me apuntan a combinar exp, sumylog

exp(sum(log([Column A])))

El problema que tengo es que la columna contiene algunos ceros para los valores y, por lo tanto, obtengo este error cuando se pasan los ceros a la logfunción:

Se produjo una operación de coma flotante no válida.

Pensé que podría solucionar esto usando una caseexpresión, pero eso simplemente no funciona de la manera que creo que debería, ya que parece evaluar todos los casos ...

select 
  Name,
  Product = case 
    when min([Value]) = 0 then 0 
    when min([Value]) <> 0 then exp(sum(log(I))) -- trying to get the product of all rows in this column
  end
 from ids
 group by Name

SqlFiddle

Dado el siguiente conjunto de resultados:

Id  Name  Value
_________________________________
1   a     1
2   a     2
3   b     0
4   b     1

Esperaría obtener las siguientes filas:

Name  Product
_____________
a     2
b     0

En resumen ... ¿Cómo se multiplican las filas en una columna que puede contener números con valor negativo o cero?

bluetoft
fuente

Respuestas:

13

La magia de NULLIF parece hacer el truco para el caso de prueba en su pregunta. Como usó un ejemplo diferente al de su Fiddle de SQL, no sé si eso es lo que quiere allí también.

CREATE TABLE dbo.Ids
(
    Id INT NOT NULL IDENTITY(1, 1),
    Value INT,
    Name NVARCHAR(3)
);
INSERT INTO dbo.Ids ( Name, Value )
VALUES ( 'a', 1 );
INSERT INTO dbo.Ids ( Name, Value )
VALUES ( 'a', 2 );
INSERT INTO dbo.Ids ( Name, Value )
VALUES ( 'b', 0 );
INSERT INTO dbo.Ids ( Name, Value )
VALUES ( 'b', 1 );

SELECT   Name,
         CASE WHEN MIN(Value) = 0 THEN 0
              WHEN MIN(Value) > 0 THEN EXP(SUM(LOG(NULLIF(Value, 0)))) -- trying to get the product of all rows in this column
         END AS Product
FROM     Ids
GROUP BY Name;

Devoluciones:

Name    Product
a       2
b       0

Si necesita una solución más general que maneje números negativos y otros casos extremos, vea por ejemplo The Product Aggregate en T-SQL Versus the CLR de Scott Burkow. Una construcción de T-SQL de ese artículo es:

EXP(SUM(LOG(NULLIF(ABS([Value]), 0))))
*
IIF(SUM(IIF([Value] = 0, 1, NULL)) > 0, 0, 1)
*
IIF(SUM(IIF([Value] < 0, 1, 0)) % 2 = 1, -1, 1)

En cuanto a por qué su CASEexpresión original no funcionó como se esperaba, de la documentación de CASE (Transact-SQL) (énfasis agregado):

Solo debe depender del orden de evaluación de las condiciones WHEN para las expresiones escalares (incluidas las subconsultas no correlacionadas que devuelven escalares), no para las expresiones agregadas .

Erik Darling
fuente