Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sql Function Issue "The last statement included within a function must be a return statement"

Tags:

sql

sql-server

In the below SQL function I have to return value based on condition but it throws a error.

"The last statement included within a function must be a return statement."

Pls help me to overcome this issue.

ALTER FUNCTION [dbo].[GetBatchReleaseQuantity]   
(
@i_LocationID VARCHAR(50),
    @i_ProductID INT,
    @i_StartDate VARCHAR(50),  
    @i_EndDate VARCHAR(50),  
    @i_ProductInFlow int
)  
RETURNS numeric(18,3)  
 --WITH ENCRYPTION     
AS  
BEGIN  

  IF (@i_ProductInFlow ='2')
  BEGIN

  RETURN (SElECT ISNULL( SUM( BatchReleaseQuantity),0.00)  From  BatchReleaseDetails BRD
  LEFT OUTER JOIN BatchRelease BR ON BR.BatchReleaseID=BRD.BatchReleaseID
  Where  ProductId=@i_ProductID  AND LocationID=@i_LocationID AND BRD.CreatedOn>=convert(datetime,@i_StartDate+' 00:00:00') AND BRD.CreatedOn<=convert(datetime,@i_EndDate+' 23:59:59'))
  END
  ELSE
  BEGIN
  RETURN(SElECT ISNULL( SUM( AcceptedQuantity),0.00)  From  GoodsReceivedNoteDetail GRND
  LEFT OUTER JOIN GoodsReceivedNote GRN ON GRN.LocationID=@i_LocationID
  Where  ProductId=@i_ProductID  AND GRN.LocationID=@i_LocationID AND GRND.CreatedOn>=convert(datetime,@i_StartDate+' 00:00:00') AND GRND.CreatedOn<=convert(datetime,@i_EndDate+' 23:59:59'))
  END 
END
like image 684
Developer Avatar asked Dec 02 '22 15:12

Developer


2 Answers

As the error suggests, the last statement must be a return statement. Unlike some other languages the flow of the IF/ELSE statement is not checked during compilation, so SQL Server is not aware that one of the branches is mandatory (even ELSE). Since this is not checked there is no way of knowing whether the function will return a value unless the last statement is a return statement. Even a simple function like this will fail:

CREATE FUNCTION dbo.FlowTest()
RETURNS INT
AS
BEGIN
    IF 1 = 1
    BEGIN
        RETURN 1;
    END
    ELSE
    BEGIN
        RETURN 0;
    END
END

The solution is to just remove the ELSE:

CREATE FUNCTION dbo.FlowTest()
RETURNS INT
AS
BEGIN
    IF 1 = 1
    BEGIN
        RETURN 1;
    END
    -- ELSE REMOVED
    RETURN 0;

END

The function will stop execution when if reaches the first RETURN, so the ELSE is not required anyway.

So your function would become:

ALTER FUNCTION [dbo].[GetBatchReleaseQuantity]   
(
@i_LocationID VARCHAR(50),
    @i_ProductID INT,
    @i_StartDate VARCHAR(50),  
    @i_EndDate VARCHAR(50),  
    @i_ProductInFlow int
)  
RETURNS numeric(18,3)  
 --WITH ENCRYPTION     
AS  
BEGIN  

  IF (@i_ProductInFlow ='2')
  BEGIN

    RETURN (SElECT  ISNULL( SUM( BatchReleaseQuantity),0.00)  
            FROM    BatchReleaseDetails BRD
                    LEFT OUTER JOIN BatchRelease BR 
                        ON BR.BatchReleaseID=BRD.BatchReleaseID
            WHERE   ProductId = @i_ProductID  
            AND     LocationID = @i_LocationID 
            AND     BRD.CreatedOn >= CONVERT(DATETIME, @i_StartDate+' 00:00:00') 
            AND     BRD.CreatedOn <= CONVERT(DATETIME,@i_EndDate + ' 23:59:59')
        )
  END

  RETURN (  SELECT  ISNULL( SUM( AcceptedQuantity),0.00)  
            FROM    GoodsReceivedNoteDetail GRND
                    LEFT OUTER JOIN GoodsReceivedNote GRN 
                        ON GRN.LocationID=@i_LocationID
            WHERE   ProductId = @i_ProductID  
            AND     GRN.LocationID = @i_LocationID 
            AND     GRND.CreatedOn >= CONVERT(DATETIME, @i_StartDate+' 00:00:00') 
            AND     GRND.CreatedOn <= CONVERT(DATETIME, @i_EndDate+' 23:59:59')
        )
  END 

END

I can't see how the function will perform well though, and why you are passing a date as a varchar is beyond me. Do you not care about things that were created between 23:59:59 and midnight?

I would be inclined to refactor this as an inline tablevalued function, and use dates properly, e.g.

CREATE FUNCTION [dbo].[GetBatchReleaseQuantityTVP]   
(
    @i_LocationID VARCHAR(50),
    @i_ProductID INT,
    @i_StartDate DATE,  
    @i_EndDate DATE,  
    @i_ProductInFlow int
)  
RETURNS TABLE
 --WITH ENCRYPTION     
AS  
RETURN 
(   SElECT  ReturnValue = ISNULL( SUM( BatchReleaseQuantity),0.00)  
    FROM    BatchReleaseDetails BRD
            LEFT OUTER JOIN BatchRelease BR 
                ON BR.BatchReleaseID=BRD.BatchReleaseID
    WHERE   ProductId = @i_ProductID  
    AND     LocationID = @i_LocationID 
    AND     BRD.CreatedOn >= @i_StartDate
    AND     BRD.CreatedOn < DATEADD(DAY, 1, @i_EndDate)
    AND     @i_ProductInFlow ='2'
    UNION ALL
    SELECT  ISNULL(SUM( AcceptedQuantity),0.00)  
    FROM    GoodsReceivedNoteDetail GRND
            LEFT OUTER JOIN GoodsReceivedNote GRN 
                ON GRN.LocationID=@i_LocationID
    WHERE   ProductId = @i_ProductID  
    AND     GRN.LocationID = @i_LocationID 
    AND     GRND.CreatedOn >= @i_StartDate
    AND     GRND.CreatedOn < DATEADD(DAY, 1, @i_EndDate)
    AND     ISNULL(@i_ProductInFlow, '') != '2'
);

Then whenever you would call dbo.GetBatchReleaseQuantity(...) simply call (SELECT ReturnValue FROM dbo.GetBatchReleaseQuantityTVP(...)). This will perform significantly better, and will also avoid people passing invalid dates to a varchar parameter.

like image 127
GarethD Avatar answered May 24 '23 04:05

GarethD


Function expect a return at the last line. Just modify your function by assigning the result to variable @nReturn and return it in last line.

create FUNCTION [dbo].[GetBatchReleaseQuantity]   
(
@i_LocationID VARCHAR(50),
    @i_ProductID INT,
    @i_StartDate VARCHAR(50),  
    @i_EndDate VARCHAR(50),  
    @i_ProductInFlow int
)  
RETURNS numeric(18,3)  
 --WITH ENCRYPTION     
AS  
BEGIN  

 DECLARE @nReturn numeric(18,3)

  IF (@i_ProductInFlow ='2')
  BEGIN

  SElECT @nReturn = ISNULL( SUM( BatchReleaseQuantity),0.00)  From  BatchReleaseDetails BRD
  LEFT OUTER JOIN BatchRelease BR ON BR.BatchReleaseID=BRD.BatchReleaseID
  Where  ProductId=@i_ProductID  AND LocationID=@i_LocationID AND BRD.CreatedOn>=convert(datetime,@i_StartDate+' 00:00:00') AND BRD.CreatedOn<=convert(datetime,@i_EndDate+' 23:59:59')
  END
  ELSE
  BEGIN
  SElECT @nReturn = ISNULL( SUM( AcceptedQuantity),0.00)  From  GoodsReceivedNoteDetail GRND
  LEFT OUTER JOIN GoodsReceivedNote GRN ON GRN.LocationID=@i_LocationID
  Where  ProductId=@i_ProductID  AND GRN.LocationID=@i_LocationID AND GRND.CreatedOn>=convert(datetime,@i_StartDate+' 00:00:00') AND GRND.CreatedOn<=convert(datetime,@i_EndDate+' 23:59:59')
  END 

  RETURN  @nReturn
END
like image 29
Veera Avatar answered May 24 '23 05:05

Veera