web-dev-qa-db-fra.com

Comment utiliser une instruction case dans une fonction à valeur scalaire en SQL?

Je veux obtenir une valeur d'une fonction en utilisant une instruction case. J'ai essayé ce qui suit mais cela ne fonctionne pas:

CREATE FUNCTION [FATMS].[fnReturnByPeriod]
(

    @Period INT

)
RETURNS int
AS
BEGIN

    SELECT CASE @Period 
             when 1 then 1
             when @Period >1 and @Period <=7 then 1
             when @Period >7 and @Period <=30 then 1
             when @Period >30 and @Period<=90 then 1
             when @Period >90 and @Period <=180 then 1
             when @Period >180 and @Period <=360 then 1
             else 0
           END

    RETURN @Period
END
15
HAJJAJ

Il existe deux types de expression CASE : simple et recherché. Vous devez choisir l'un ou l'autre - vous ne pouvez pas utiliser un mélange des deux types dans une expression.

Essaye ça:

SELECT CASE
    WHEN @Period = 1 THEN 1
    WHEN @Period > 1 AND @Period <= 7 THEN 2
    WHEN @Period > 7 AND @Period <= 30 then 3
    -- etc...
    ELSE 0
END

En outre, vous devez attribuer le résultat à quelque chose comme d'autres l'ont déjà souligné.

17
Mark Byers

Lorsque vous utilisez RETURN @Period, vous devez affecter une valeur à @Period. L'exemple suivant montre comment structurer votre code de sorte qu'il n'est pas nécessaire de déclarer une variable locale.

CREATE FUNCTION [FATMS].[fnReturnByPeriod]
(
    @Period INT
)
RETURNS INT
AS
BEGIN
    RETURN
        CASE 
            WHEN @Period = 1 THEN 1
            WHEN @Period > 1 AND @Period <=7 THEN 1
            WHEN @Period > 7 AND @Period <=30 THEN 1
            WHEN @Period > 30 AND @Period<=90 THEN 1
            WHEN @Period > 90 AND @Period <=180 THEN 1
            WHEN @Period > 180 AND @Period <=360 THEN 1
            ELSE 0
        END 
END
16
bernd_k

Déclarez une deuxième variable, puis définissez cette valeur car vous ne réinitialisez pas @Period.

Par exemple:

DECLARE @Output AS INT

SELECT @Output = CASE @Period 
                 WHEN 1 then 1     
                 WHEN @Period > 1 AND @Period <= 7 THEN 1      -- Should be 2 
                 WHEN @Period > 7 AND @Period <= 30 THEN 1     -- Should be 3
                 WHEN @Period > 30 AND @Period<= 90 THEN 1     -- Should be 4
                 WHEN @Period > 90 AND @Period <= 180 THEN 1   -- Should be 5  
                 WHEN @Period > 180 AND @Period <= 360 THEN 1  -- Should be 6   
                 ELSE 0 END;

RETURN @Output;

Je l'ai laissé comme ça car je suppose que vous allez changer vos valeurs pour chacune de ces instructions CASE.

5
Neil Knight

Vous n'affectez @Period nulle part. Essayez avec when @Period >1 and @Period <=7 then SET @Period = 1 et par la suite pour toutes les autres lignes.

SQL n'a pas de retours implicites et vous devez donc toujours définir les variables avant de les renvoyer.

1
PedroC88

Voici une approche basée sur SET pour écrire votre fonction dans SQL Server 2008

CREATE FUNCTION [FATMS].[fnReturnByPeriod]
(
    @Period INT
)
RETURNS int
AS
BEGIN
    return isnull((SELECT min(v)
    from (values
     (1,1),
     (7,2),
     (30,3),
     (90,4),
     (180,5),
     (360,6)) t(n,v)
    where n >= @Period and @period>0), 0)
END

De la façon dont vous l'avez écrit, toutes les branches CASE renvoient 1, donc vous pourriez aussi bien utiliser

return case when @period between 1 and 360 then 1 else 0 end
1
RichardTheKiwi

En quoi cela "ne fonctionne pas"? Cela produit-il une erreur lorsque vous essayez de l'exécuter? Retourne-t-il un résultat inattendu?

Du haut de ma tête, il y a plusieurs problèmes:

Votre déclaration de cas renvoie 1 dans tous les cas.

L'instruction case combine également deux types différents de sémantique CASE:

CASE @Period 
WHEN <condition> THEN <result>
WHEN <other condition> THEN <result>
ELSE <result>
END

ou

CASE
WHEN @Period = <value> THEN <result>
WHEN @Period = <other value> THEN <result>
ELSE <result>
END

Le second formulaire vous permet d'utiliser des conditions indépendantes, tandis que le premier ne peut vérifier que différentes valeurs de @Period.

En outre, vous renvoyez la valeur de @Period, pas la valeur générée par l'instruction CASE.

1
Dan J