如何使用 GETDATE 解决 SQL 标量变量的错误

Posted

技术标签:

【中文标题】如何使用 GETDATE 解决 SQL 标量变量的错误【英文标题】:How to resolve error with SQL Scalar variable using GETDATE 【发布时间】:2021-07-14 04:26:31 【问题描述】:

我正在用 SQL 编写一个标量变量来定义季节年(从日历年的中间开始)。它需要随着日期的变化而变化,即此日期为 2021 年,2022 年 6 月 1 日则应更改为 2022 年。

它正在生成这个错误代码:

“引用函数'GETDATE'时出错:用户定义的函数中不允许调用非确定性、安全性和元数据函数。”

我在网上看不到任何可以详细处理该问题的解决方法。有什么想法吗?

代码是:

CREATE FUNCTION CDP.fn_SeasonYear
(
)
RETURNS INT
       AS 
       BEGIN
              DECLARE @ThisSeason INT
       SET @ThisSeason = 
       CASE
              WHEN DatePart(Month, GETDATE()) < 6
              THEN CONVERT(int,DatePart(Year, GETDATE()) -1)
              ELSE CONVERT(int,DatePart(Year, GETDATE()))
       End

RETURN @ThisSeason
END;

【问题讨论】:

您不能在用户定义的函数中使用 GETDATE。原因正如错误所说,它的不确定性意味着您将使用相同的输入获得不同的结果。可能可行的方法是创建一个充满 GETDATE 的视图并在您的函数中使用该视图 请记住,UDF 通常不是正确的答案,并且会引入性能问题。对于这种事情,我总是会直接使用日历表。 【参考方案1】:

GETDATE() 是 MS SQL Server。您应该这样标记问题。您可能应该指出您拥有的 SQL Server 版本,因为该功能在 SQL Server 2017 中运行良好:

SET NOCOUNT ON;
SELECT @@VERSION;
GO
DROP FUNCTION IF EXISTS dbo.fn_SeasonYear;
GO
CREATE FUNCTION dbo.fn_SeasonYear()
RETURNS INT
AS 
BEGIN
  DECLARE @ThisSeason INT;
  SET @ThisSeason = 
    CASE
      WHEN DatePart(Month, GETDATE()) < 6
        THEN CONVERT(INT, DatePart(Year, GETDATE()) -1)
      ELSE CONVERT(INT, DatePart(Year, GETDATE()))
    END;
RETURN @ThisSeason;
END;
GO
SELECT  dbo.fn_SeasonYear();

/*
OUTPUT:
----------------------------------------------------------------------------
Microsoft SQL Server 2017 (RTM) - 14.0.1000.169 (X64) 
    Aug 22 2017 17:04:49 
    Copyright (C) 2017 Microsoft Corporation
    Express Edition (64-bit) on Windows Server 2016 Datacenter 10.0 <X64> (Build 14393: ) (Hypervisor)

-----------
2021
*/

【讨论】:

+1 我对此感到惊讶。在 Sql 2000 中你绝对不能这样做,所以我自动避免在 UDF 中使用 getdate()。显然他们放宽了 Sql 2005 中的规则。我没有 Sql 2000,所以我无法测试它的行为,但从 Sql 2005 开始,在查询/更新语句中,每列对 getdate() 进行一次评估并使用该值对于所有行。我怀疑 getdate() 是伪确定性的,即它在任何执行上下文中被调用一次并使用缓存的值。 @JohnD 是的,每个上下文只评估一次。您真正想要使用的是 SYSUTCDATETIME( )。与 SYSDATE() 一样,它每次都会被评估,并且您希望在 SYSDATE() 上使用它,因为您永远不应该将任何时间戳值(日期时间)存储在 UTC 以外的任何地方(并且您的服务器应该是 UTC ......总是。没有例外)。【参考方案2】:

这并不理想,但这是我使用过的一种方法。如前所述,在给定相同输入的情况下,函数需要返回相同的值。您将 getdate() 作为参数传递给函数。

CREATE FUNCTION dbo.fn_SeasonYear( @currdate datetime )

RETURNS INT
       AS 
       BEGIN
              DECLARE @ThisSeason INT
       SET @ThisSeason = 
       CASE
              WHEN DatePart(Month, @currdate) < 6
              THEN CONVERT(int,DatePart(Year, @currdate) -1)
              ELSE CONVERT(int,DatePart(Year, @currdate))
       End

RETURN @ThisSeason
END;

【讨论】:

以上是关于如何使用 GETDATE 解决 SQL 标量变量的错误的主要内容,如果未能解决你的问题,请参考以下文章

sql如何取得当前日期

SQL Server:必须声明标量变量

sql必须声明标量变量错误

vb.net SQL 导致 - “必须声明标量变量”

尝试在 SQL 中运行 UPDATE 语句时“必须声明标量变量”

SQL Server如何定位自定义标量函数被那个SQL调用次数最多浅析