将 Access 函数转换为 SQL Server 时出现问题

Trouble converting an Access Function to SQL Server

提问人:John 提问时间:9/7/2023 最后编辑:John 更新时间:9/9/2023 访问量:51

问:

SQL学习者在这里。我在通过 SQL Server 转换 Microsoft Access 中内置的函数时遇到问题。 下面是 Access 中显示的函数。

Function DatePop(DATE)

Dim DateMin As Date
Dim DateMax As Date

DateMin = "01/10"
DateMax = CDate(Now())

If (Format(DATE, "dd/mm") >= DateMin 

Or 

Format(DATE, "dd/mm") <= DateMax) 

Then DatePop = 1
    
Else DatePop = 0
    
End If

End Function

如果日期在 10 月 1 日之后且在当前日期之前,则该函数的目标是返回 1。否则返回 0。

SQL Server 函数 ms-access

评论

1赞 Panagiotis Kanavos 9/7/2023
这个方法想做什么?SQL Server 和 Access 是完全不同的数据库。日期也不是文本,也没有格式。无论您想做什么,都不需要将任何内容转换为文本。
1赞 Panagiotis Kanavos 9/7/2023
事实上,你想要的似乎只是,其中有一列。即使在 WHERE 子句中,这也是一个坏主意,因为它会阻止索引的使用。 将使用索引。如果是 或者,则必须修改查询以处理时间部分。IIF(date_col BETWEEN '20221001' AND GETDATE(),1,0)date_coldateWHERE date_col BETWEEN '20221001' AND GETDATE()date_coldatetimedatetime2
0赞 Panagiotis Kanavos 9/7/2023
你还没有解释你真正想要的是什么
2赞 Panagiotis Kanavos 9/7/2023
这在 Access 和 SQL Server 中都是一个坏主意。请改用WHERE DATE_COL BETWEEN @date1 and @date2
1赞 Eric 9/8/2023
“01/10”不是有效日期。作为一个人,我什至不明白它是什么。是 2010 年 1 月 10 日或 1 月 1 日,还是 2001 年 10 月 1 日或 10 月???

答:

0赞 Albert D. Kallal 9/9/2023 #1

好的,让我们将 VBA 函数重写为 T-SQL。 创建此类函数后,可以在 T-SQL 代码中使用该函数(就像我们在 VBA 代码中所做的那样)。这些函数现在可以在任何 SQL Server SQL 表达式中自由使用。(同样,就像在 JET/ACE SQL 表达式中使用此类 VBA 函数一样)。

因此,在 T-SQL 中创建此类函数时存在 1 对 1 对称性,从那时起,转换为 T-SQL 代码的任何 VBA 代码现在都可以使用你创建的该函数。对于您编写服务器端的任何 SQL,该函数都可以在您编写的 SQL 中自由使用(同样,就像在 Access 中编写 SQL 并在您编写的 SQL 中使用 VBA 表达式一样)。

此外,您发布的函数中仍然存在一些“歧义”。它似乎依赖于“忽略”当前年份的字符串比较这一事实。这是一些“高风险”,因为台式计算机上区域设置的任何更改都会导致此类代码中断。

当你说“十月之后”时,你是指当年,还是任何时间和任何年份的任何日期,其中“月”在十月之后?这看起来像现在代码的工作方式,即使这不是你的本意。

不要使用保留词作为变量名称,因为它们只会在这里造成痛苦和折磨。

因此,让我们首先让这个VBA代码正常工作,或者更好地说明,以这样一种方式定义,我们可以100%清楚逻辑。因此,当我们花费时间和精力时,我们正在将有效代码转换为 T-SQL。

因此,根据您所拥有的,这看起来像是等变VBA代码:

Public Function DatePop(nDate) As Integer

    Dim ToDay           As Date
    Dim iMonth          As Integer
    Dim iDay            As Integer
    
    iMonth = 10
    iDay = 1
    
    If IsNull(nDate) Then
       DatePop = 0
       Exit Function
    End If
    

    ToDay = CDate(Now())

    If (Month(nDate) >= iMonth) And (Day(nDate) > iDay) Then
        DatePop = 1
        Exit Function
    End If
    
    If nDate <= ToDay Then
        DatePop = 1
        Exit Function
    End If
       
    DatePop = 0
    
End Function

如前所述,您注意到了“10 月之后”的逻辑,但您的意思是任何一年,还是仅指此逻辑的当前年份?

并注意我们如何强制(定义)函数返回的数据类型。再一次,我们不能把它留给风吹。

好的,基于上面的代码,我们现在可以将上面的 VBA 代码转换并重写成执行相同操作的 T-SQL 函数。

因此,此 T-SQL 应该有效:

CREATE FUNCTION DatePop
(
    @nDate date
)
RETURNS int
AS
BEGIN
    DECLARE @iMonth int
    DECLARE @iDay int
    DECLARE @ToDay date
    DECLARE @iResult int

    set @iResult = 0

    set @iMonth = 10
    set @iDay = 1
    set @ToDay = CAST(GETDATE() as date) -- date without time

    if month(@nDate) >= @iMonth AND day(@nDate) >= @iDay 
    BEGIN
        set @iResult = 1
    END

    if @nDate <= @ToDay
    BEGIN
       set @iResult = 1
    END

    RETURN @iResult

END

一旦创建了上述功能?那么这应该像VBA函数一样工作。这包括在 VBA 代码(或现在的 T-SQL 代码)中使用该函数,并且还包括在编写的服务器端 T-SQL 代码中自由使用此函数。

唯一值得注意的区别是 T-SQL 函数必须以 dbo 为前缀。(函数所属的架构)。因此,当您将 JET/ACE SQL 剪切 + 粘贴到 SQL Server 中时,通常相同的 SQL 可以工作,但您必须编辑该 SQL 中使用的 VBA 函数,并以 dbo 为前缀。

因此:

SELECT ID, HotelName, BookingDate, DatePop([BookingDate]) as MySeason
FROM Hotels

如果将上述内容剪切+粘贴到SQL Server,则将变为:

SELECT ID, HotelName, BookingDate, dbo.DatePop([BookingDate]) as MySeason
FROM Hotels

请注意,在上面,我们必须用“dbo.”来“前缀”DatePop 函数。