web-dev-qa-db-fra.com

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

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'orientent vers la combinaison de exp, sum et log

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 les zéros sont passés à la fonction log:

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

Je pensais pouvoir contourner cela en utilisant une expression case, 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 des lignes dans une colonne qui peuvent contenir des nombres négatifs ou nuls?

10
bluetoft

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 Edge, consultez par exemple Le produit agrégé dans T-SQL contre le CLR par 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 expression CASE d'origine n'a pas fonctionné comme prévu, à partir de la documentation de CASE (Transact-SQL) (emphase ajoutée):

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 .

13
Erik Darling