【问题标题】:Errors while Trying to Use a Function Parameter inside an IF Statement尝试在 IF 语句中使用函数参数时出错
【发布时间】:2019-04-05 08:45:42
【问题描述】:

我有以下CREATE FUNCTION

CREATE FUNCTION ufnTotalSales (@StartDate datetime, @EndDate datetime = GETDATE(), @FoodName nvarchar(50) = '')
RETURNS TABLE
AS
    RETURN
    (
        IF @FoodName = '';
        BEGIN
            SELECT f.FoodID, FoodName, (FoodPrice * Quantity) AS TotalSales FROM Food f, OrderFoodRel ofr
            WHERE (Date_Time BETWEEN @StartDate AND @EndDate)
        END

        ELSE
        BEGIN
            SELECT f.FoodID, FoodName, (FoodPrice * Quantity) AS TotalSales FROM Food f, OrderFoodRel ofr
            WHERE (Date_Time BETWEEN @StartDate AND @EndDate) AND @FoodName = FoodName
        END
    );

第一个错误发生在@EndDate datetime = GETDATE(),它显示Incorrect syntax near '()'。如果用户选择使用默认值,我正在尝试为@EndDate 参数分配当前datetime 的默认值,但不知何故我得到了错误。

第二个错误发生在我在IF ... ELSE 块中使用的所有参数(@FoodName@StartDate@EndDate)。它说我Must declare the scalar variable "@..."。它是一个参数而不是一个标量变量,我该如何解决这个问题?

这个函数的想法是返回食品的总销售额,有两个选项:一个是名称为 X 的食品从某个日期到另一个日期的总销售额,如果您指定了食品名称;和两个是从一个日期到另一个日期的食品总销售额,不考虑食品名称。

【问题讨论】:

  • OrderFoodRel 和 Food 是如何关联的
  • @SanalSunny 它们使用来自 Food (FoodID) 的主键相关联。 OrderFoodRel 有一个指向所述主键的外键 FoodID。

标签: sql-server sql-function


【解决方案1】:

这个函数有几个问题

首先,您不能将默认值分配给函数您不能在函数内使用IF .. ELSE 其次。表FoodOrderFoodRel 不是JOINed。你在这里做一个交叉连接

它不喜欢getdate()作为默认值,你可以将@EndDate设置为NULL,并在@EndDate上使用ISNULL()

这将创建函数。我假设这 2 个表与 FoodID 相关联

CREATE FUNCTION ufnTotalSales 
(
    @StartDate datetime,  
    @EndDate   datetime     = NULL, 
    @FoodName  nvarchar(50) = ''
)
RETURNS TABLE
AS
    RETURN
    (
        SELECT  f.FoodID, FoodName, (FoodPrice * Quantity) AS TotalSales 
        FROM    Food f
                INNER JOIN OrderFoodRel ofr on  f.FoodID    = ofr.FoodID
        WHERE   Date_Time BETWEEN @StartDate AND ISNULL(@EndDate, GETDATE())
        AND     (
                    @FoodName   = ''
                OR  f.FoodName  = @FoodName
                )
    );
GO

所以要在输入上使用默认值,需要使用关键字default

select  *
from    dbo.ufnTotalSales('2018-10-01', default , default)

【讨论】:

  • 但是,这里有一篇关于为参数分配默认值的帖子:stackoverflow.com/questions/8358315/…
  • 编辑了查询。好的。它不喜欢 getdate() 作为默认值。只有常数
  • 对于最后一部分 FoodName = '' OR f.FoodName = FoodName,如果我说最后一部分的意思是否正确:如果 FoodName 是一个空字符串(默认值),那么任何 FoodName符合该条件;但如果不是,请检查第二个条件,即将我的 Food 表中的 FoodName 与插入的参数(不再是空字符串)进行比较?
  • 编辑了答案以包含它。是的 ISNULL()COALESCE() 在这种情况下工作相同
  • 或者更具体地说,你不能在表值函数中使用IF..ELSE,在标量函数中就可以了
【解决方案2】:

使用下面的函数

CREATE FUNCTION ufnTotalSales 
(  @StartDate datetime, 
   @EndDate datetime, 
   @FoodName nvarchar(50)
)
RETURNS TABLE
AS
    RETURN
    (
        SELECT f.FoodID, FoodName, (FoodPrice * Quantity) AS TotalSales 
        FROM Food f
        JOIN OrderFoodRel ofr on f.FoodID = ofr.FoodID
        WHERE (Date_Time BETWEEN @StartDate AND ISNULL(@EndDate,GETDATE())) 
         AND ISNULL(@FoodName,'') = FoodName

    );

【讨论】:

  • 但这无助于 ITVF 中存在程序逻辑这一事实。
  • @WealthyPlayer 请立即尝试
【解决方案3】:

您不能在 ITVF 中使用过程逻辑,而只能使用某种返回结果集的查询。

参考:https://docs.microsoft.com/en-us/sql/t-sql/statements/create-function-transact-sql?view=sql-server-2017

希望以下内容与您正在寻找的内容接近:

CREATE FUNCTION ufnTotalSales
(
  @StartDate datetime
  , @EndDate datetime
  , @FoodName nvarchar(50)
)
RETURNS TABLE
AS
RETURN
  SELECT f.FoodID, FoodName, (FoodPrice * Quantity) AS TotalSales FROM Food f, OrderFoodRel ofr
  WHERE (Date_Time BETWEEN @StartDate AND coalesce(@EndDate,getdate()))
  and coalesce(@FoodName,'') = ''
  union all
  SELECT f.FoodID, FoodName, (FoodPrice * Quantity) AS TotalSales FROM Food f, OrderFoodRel ofr
  WHERE (Date_Time BETWEEN @StartDate AND coalesce(@EndDate,getdate()))
  AND coalesce(@FoodName,'') = FoodName

【讨论】:

  • stackoverflow.com/questions/8358315/… 不过有一篇关于默认参数的帖子。还是仅适用于标量值函数?
  • 你说得对,对不起 - 你可以有默认值,但默认值必须是静态值,而不是像 getdate() 这样的函数调用。但是如果您不想传入参数,我会按照我的方式进行操作并传入 null 或将默认值设置为 null。
  • 如果我希望 @EndDate 成为现在的日期时间并且我在没有该参数的情况下调用函数,它不会返回与所述链接中的问题相同的错误('参数数量不足是提供')?
  • 对不起,您(有点)再次正确,最好阅读参考链接,因为它会告诉您具体操作方法。您可以设置默认值(例如 null),然后当您调用该函数时,您必须传递关键字 default 而不是一个值。就我个人而言,我只会传入 null 而不使用默认值 - 但取决于你。
  • 我明白了,谢谢你的回答!你怎么知道我不能在 ITVF 中使用程序逻辑?是否有经验或对该规则有明确的解释?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-02-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多