web-dev-qa-db-fra.com

TSQL - Instruction If..Else dans les fonctions à valeur table - Ne peut pas passer

Avant de poster, j'ai lu quelques articles sur le développement des fonctions USD, mais je n'ai pas trouvé de solutions à mon problème ... qui se présente comme suit:

J'ai une base de données très simple, qui stocke les joueurs de basket et se compose de la colonne ID, Age, Height et Name. Ce que je voudrais faire est d'implémenter une fonction 'height' avec un paramètre @set varchar (10), qui dépend d'un @set pour déclencher différentes instructions de sélection

ce que j'essayais d'implémenter était en psuedo-code:

CREATE FUNCTION [dbo].[age](@set varchar(10))
RETURNS TABLE
AS
BEGIN

    IF  (@set = 'tall')
         SELECT * from player where height > 180

    ELSE IF (@set = 'average')
         SELECT * from player where height >= 155 and height <=175

    ELSE IF (@set = 'low')
         SELECT * from player where height < 155
END

Quelqu'un pourrait-il me donner un indice sur la manière de le mettre en œuvre?

16
Artur

La forme la plus simple est toujours la meilleure

CREATE FUNCTION [dbo].[age](@set varchar(10))
RETURNS TABLE
AS RETURN
SELECT * from player
where ((@set = 'tall' and height > 180)
   or (@set = 'average' AND height >= 155 and height <=175)
   or (@set = 'low' AND height < 155))
GO

Ce formulaire est appelé fonction de table INLINE, ce qui signifie que SQL Server est libre de le développer pour joindre le lecteur directement à d’autres tables en ligne d’une requête plus importante, ce qui lui permet d’exécuter infiniment1 mieux qu'une fonction valorisée de table multi-instructions.

Vous préférerez peut-être cela, afin que vos gammes soient complètes (vous avez un écart entre 175 et 180)

where ((@set = 'tall' and height > 180)
   or (@set = 'average' AND height >= 155 and height <= 180)
   or (@set = 'low' AND height < 155))

SQL Server s'occupe de court-circuiter les branches lorsque la variable @set est analysée.

1 exagération, mais seulement légèrement

20
RichardTheKiwi

Tu étais proche. L'utilisation d'une fonction table multi-instruction nécessite que la table de retour soit spécifiée et renseignée dans la fonction:

CREATE FUNCTION [dbo].[age](@set varchar(10))
RETURNS @Players TABLE
(
    -- Put the players table definition here
) 
AS
BEGIN

    IF  (@set = 'tall')
         INSERT INTO @Players SELECT * from player where height > 180

    ELSE IF (@set = 'average')
         INSERT INTO @Players SELECT * from player where height >= 155 and height <=175

    ELSE IF (@set = 'low')
         INSERT INTO @Players SELECT * from player where height < 155

    RETURN -- @Players (variable only required for Scalar functions)

END

Je recommanderais d'utiliser une TVF intégrée, comme le montre la réponse de Richard. Il peut déduire le retour de table de votre déclaration.

Notez également que les TVF multi-instructions et inline sont vraiment très différents. Un fichier TVF intégré est moins une boîte noire que l'optimiseur et ressemble plutôt à une vue paramétrée en ce sens que l'optimiseur peut réorganiser les éléments avec d'autres tables et vues dans le même plan d'exécution.

16
Cade Roux

Pourquoi codez-vous en dur, créez une table des hauteurs puis saisissez toutes les hauteurs valables pour la plage

SELECT * from player p
join Heights h on p.height between h.heightStart and h.heightEnd 
WHERE h.height  = @set
10
SQLMenace

Cela devrait marcher.

SELECT * FROM player 
WHERE
  height > CASE 
            WHEN @set = 'tall' THEN 180
            WHEN @set = 'average' THEN 154
            WHEN @set = 'low' THEN 0
          END

Je vais laisser le cas pour votre plus grand plaisir.

3
Hogan

Quelque chose comme ça:

CREATE FUNCTION [dbo].[Age](@set VARCHAR(10))  
RETURNS @Players TABLE
(
    playerId INT,
    Name VARCHAR(50)
) 
AS  
BEGIN 

    INSERT INTO @Players
    SELECT playerId, Name
    FROM player 
    WHERE CASE WHEN @set = 'tall' AND height > 180 THEN 1
    WHEN @set = 'average' AND height BETWEEN 155 AND 180 THEN 1
    WHEN @set = 'low' AND height < 155 THEN 1 ELSE 0 END = 1

    RETURN
END
2
Lamak

Nous pouvons utiliser la fonction de valeur Table de la manière suivante avec des conditions IF dessus.

CREATE function [dbo].[AA] 
(
@abc varchar(10)
)
Returns  @mytable table
(
supname nvarchar(10), [add] nvarchar(10)
)
AS
begin
-- lOAD WHATEVER THINGS YOU REQUIRED INTO THIS DYNAMIC TABLE
if (@abc ='hh')
    insert into @mytable (supname, [add]) values ('hh','gg'+ @abc)
else
    insert into @mytable (supname, [add]) values ('else','gg'+ @abc)
return
end

--select * from [dbo].[AA]('SDAASF')
1
user2316060

Selon Itzik Ben-Gan dans son livre "TSQL Querying" (Itzik Ben-Gan et al , (c) 2015, Microsoft Press, ISBN 978-0-7356-8504-8, p. 215) ".. . Je trouve que les TVF intégrés sont un excellent outil, permettant d’encapsuler la logique et de pouvoir être réutilisés sans aucun problème de performance des fonctions UDF ...

Il ajoute que si vous avez besoin de " ... une expression de table réutilisable telle qu'une vue, vous devez également transmettre des paramètres d'entrée à l'expression de table ... TSQL fournit des fonctions de table inline (TVF).

Ce type de 'IF' ( inline function - un type distinct dans sys.objects ) utilise le spécificateur de sortie 'RETURNS TABLE' et ne peut apparemment pas contenir BEGIN/END. La syntaxe et les tolérances sont très restrictives, mais nous constatons une optimisation et des performances satisfaisantes. Ces facteurs sont indiqués par les timings observés par @ryk.

0
CubeSpark