Comment multiplier les lignes d'une colonne contenant des valeurs négatives et nulles?

10

J'essaie d'obtenir le produit de toutes les lignes pour une colonne spécifique dans une requête groupée. La plupart des exemples que j'ai trouvés m'indiquent vers la combinaison exp, sumetlog

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

Le problème que j'ai est que la colonne contient des zéros pour les valeurs et donc j'obtiens cette erreur lorsque des zéros sont passés à la logfonction:

Une opération à virgule flottante non valide s'est produite.

J'ai pensé que je pouvais contourner cela en utilisant une caseexpression, mais cela ne fonctionne tout simplement pas comme je le pense, car il semble évaluer tous les cas ...

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

Étant donné l'ensemble de résultats suivant:

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

Je m'attendrais à obtenir les lignes suivantes:

Name  Product
_____________
a     2
b     0

Donc en résumé ... Comment multipliez-vous les lignes d'une colonne qui peuvent contenir des nombres négatifs ou nuls?

bluetoft
la source

Réponses:

13

La magie de NULLIF semble faire l'affaire pour le cas de test dans votre question. Puisque vous avez utilisé un exemple différent de celui de votre SQL Fiddle, je ne sais pas si c'est ce que vous voulez là aussi.

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;

Retour:

Name    Product
a       2
b       0

Si vous avez besoin d'une solution plus générale qui gère les nombres négatifs et autres cas marginaux , consultez par exemple The Product Aggregate in T-SQL Versus the CLR de Scott Burkow. Une construction T-SQL de cet article est:

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)

Quant à savoir pourquoi votre CASEexpression d' origine n'a pas fonctionné comme prévu, à partir de la documentation de CASE (Transact-SQL) (non souligné dans l'original):

Vous ne devez dépendre que de l'ordre d'évaluation des conditions WHEN pour les expressions scalaires (y compris les sous-requêtes non corrélées qui renvoient des scalaires), pas pour les expressions agrégées .

Erik Darling
la source