2017-06-25 2 views
0

Nous avons une fonction qui reçoit 4 variables et retourne un résultat int. Nous aimerions utiliser cette fonction pour chaque enregistrement de ma table alors que ces 4 variables sont extraites de chaque enregistrement.comment puis-je additionner une fonction dans une boucle en sql?

Comment dois-je le résoudre?

Ma fonction:

CREATE FUNCTION dbo.occupiedDaysPerListingFunction(@CheckIn date, @CheckOut date, @Email varchar(80), @Title varchar(50)) 
      RETURNS int 
    AS 
    BEGIN 
    DECLARE @nightsInRange int 
    select @nightsInRange= (CASE 
    WHEN DATEDIFF(day,@CheckIn,getdate()) >0 and DATEDIFF(day,@CheckOut,getdate())+100 <0 THEN 100 
    WHEN DATEDIFF(day,@CheckIn,getdate()) >0 and DATEDIFF(day,@CheckOut,getdate())+100 >0 THEN DATEDIFF(day,getdate(),@CheckOut) 
    WHEN DATEDIFF(day,getdate(),@CheckIn) >0 and DATEDIFF(day,getdate(),@CheckIn) <100 and DATEDIFF(day,@CheckOut,getdate())+100 >0 THEN DATEDIFF(day,@CheckIn,@CheckOut) 
    WHEN DATEDIFF(day,getdate(),@CheckIn) >0 and DATEDIFF(day,getdate(),@CheckIn) <100 and DATEDIFF(day,@CheckOut,getdate())+100 <0 THEN DATEDIFF(day,@CheckIn,getdate())+100 
    WHEN DATEDIFF(day,@CheckIn,getdate()) <0 THEN 0 
END) 
FROM [dbo].[ORDERS] o 
     WHERE o.[E-mail] = @Email and o.[title][email protected] 
     RETURN @nightsInRange 
end 

enter image description here

+0

Le titre de votre question mentionne "sum", mais cela n'apparaît pas dans la question. Pourquoi pas? – HABO

Répondre

0

curseur est juste un outil approprié pour cette tâche. Сursor est l'une des options possibles. Vous pouvez également utiliser une boucle while, CTE, etc.

DECLARE @iterator CURSOR 
DECLARE @bufferTable TABLE 
(
    CheckIn date, 
    CheckOut date, 
    Email varchar(80), 
    Title varchar(50) 
) 

SET @iterator = CURSOR FOR 
SELECT 
    CheckIn, 
    CheckOut, 
    Email, 
    Title 
FROM [YourDatabase].[YourSchema].[YourTable]  

OPEN @iterator 

FETCH NEXT FROM @iterator 
INTO @bufferTable 

WHILE @@FETCH_STATUS = 0 
BEGIN 

    /* Your function comes into play */ 
    dbo.occupiedDaysPerListingFunction(@bufferTable.CheckIn, @bufferTable.CheckOut, @bufferTable.Email, @bufferTable.Title) 

    FETCH NEXT FROM @iterator 
    INTO @bufferTable 
END; 

CLOSE @iterator 
DEALLOCATE @iterator 

GO 

EDIT:

Sur la base de la réponse de @Rasmus Dybkjær, vous pourriez restreindre la requête SELECT comme ceci:

SELECT InnerQueryTable.OccupiedDaysPerListing FROM 
(
    SELECT 
     CheckInDate, 
     CheckOutDate, 
     Email, 
     Title, 
     dbo.occupiedDaysPerListingFunction(CheckInDate, CheckOutDate, Email, Title) AS OccupiedDaysPerListing 
    FROM 
     [YourDatabase].[YourSchema].[YourTable] 
) AS InnerQueryTable 
GO 
0

Je commencerais à m'assurer que votre fonction renvoie toujours un nombre entier. Votre fonction actuelle envoie une requête à votre table [Commandes] avec un filtre sur l'email et le titre. Vous devriez considérer ce qui devrait arriver (si) votre table [Orders] contient plusieurs lignes avec le même email et le même titre.

je commencerais par la réécriture de votre fonction comme ceci:

CREATE FUNCTION dbo.occupiedDaysPerListingFunction(@CheckIn DATE, @CheckOut 
DATE, @Email VARCHAR(80), @Title VARCHAR(50)) 
RETURNS INT 
AS 
BEGIN 
    DECLARE @nightsInRange INT = 0; 
    SET @nightsInRange = (SELECT TOP 1 CASE 
     WHEN DATEDIFF(day,@CheckIn,getdate()) >0 and  DATEDIFF(day,@CheckOut,getdate())+100 <0 THEN 100 
     WHEN DATEDIFF(day,@CheckIn,getdate()) >0 and  DATEDIFF(day,@CheckOut,getdate())+100 >0 THEN DATEDIFF(day,getdate(),@CheckOut) 
     WHEN DATEDIFF(day,getdate(),@CheckIn) >0 and  DATEDIFF(day,getdate(),@CheckIn) <100 and DATEDIFF(day,@CheckOut,getdate())+100  >0 THEN DATEDIFF(day,@CheckIn,@CheckOut) 
     WHEN DATEDIFF(day,getdate(),@CheckIn) >0 and  DATEDIFF(day,getdate(),@CheckIn) <100 and DATEDIFF(day,@CheckOut,getdate())+100  <0 THEN DATEDIFF(day,@CheckIn,getdate())+100 
     WHEN DATEDIFF(day,@CheckIn,getdate()) <0 THEN 0 
     ELSE 0 
    END 
    FROM [dbo].[ORDERS] o 
    WHERE o.[E-mail] = @Email and o.[title][email protected]) 

    RETURN @nightsInRange 
END 

Cette fonction est toujours pas parfait, mais il assure que: 1) Votre fonction retourne une valeur dans tous les cas (même si aucun enregistrement ne trouvé) 2) votre fonction ne manque pas en raison de plusieurs [commandes] avec le même courriel et le titre

Ensuite je qualifierais de votre fonction d'une requête comme ceci:

SELECT 
    CheckInDate, 
    CheckOutDate, 
    Email, 
    Title, 
    dbo.occupiedDaysPerListingFunction(CheckInDate, CheckOutDate, Email,  Title) AS OccupiedDaysPerListing 
FROM 
    [YourDatabase].[YourSchema].[YourTable] 

Espérons que cela aide.

+0

En règle générale, «TOP» doit être associé à «ORDER BY». Dans ce cas, où n'importe quelle ligne fera, il vaut la peine d'ajouter un commentaire pour expliquer le comportement attendu. Notez que chaque instance de 'GetDate()' retournera une seule valeur pour toutes les lignes, mais que différentes instances peuvent renvoyer des valeurs différentes. La meilleure pratique serait d'obtenir une seule valeur, par ex. 'declare @Now comme DateTime = GetDate();', et utilise la variable '@ Now' dans le reste de la fonction. – HABO

+0

D'accord avec vos points, @HABO. Ce seraient des améliorations supplémentaires de la requête. Dans ce cas, il serait très probablement vouloir commander par un OrderDate, descendant. C'est à dire. 'COMMANDER PAR [OrderDate] DESC' – dybzon