简体   繁体   中英

How can a SQL Server function with a recently added keyword support older versions of SQL Server

We are writing database upgrade scripts to be run on our customers servers which could be any version of SQL Server from 2012 upwards.

One thing we are doing is migrating DATETIME columns from local time zone format to UTC format.

I wrote a function to do this that can be called from the migration SQL.

The function uses one of 2 methods depending on the version of SQL Server - using DATETIMEOFFSET if 2016 or newer or a fallback if older.

The issue is that older servers won't allow the function to be created as it contains the DATETIMEOFFSET keyword.

I tried moving the DATETIMEOFFSET SQL to a string and calling it with sp_executesql but found out I can't call a stored procedure from a function.

Any ideas on how to handle this?

I could write 2 functions and install one or the other depending on the server version but am unsure if this is possible with SQL Server Data Tools and dacpacs.

Here is the function:

CREATE FUNCTION [dbo].[ConvertLocalDateToUtc]
    (@LocalDate DATETIME2, 
     @LocalZone NVARCHAR(128))
RETURNS DATETIME2
AS
BEGIN
    DECLARE @convertedDate DATETIME2;

    IF (SELECT CAST(SERVERPROPERTY('ProductMajorVersion') AS INT)) >= 13
    BEGIN
        -- if SQL Server 2016 or later, this takes account of historical daylight saving times
        DECLARE @ZonedLocalDate DATETIMEOFFSET = @LocalDate AT TIME ZONE @LocalZone;
        DECLARE @ZonedUtcDate DATETIMEOFFSET = @ZonedLocalDate AT TIME ZONE 'UTC';
        SET @convertedDate = CAST(@ZonedUtcDate AS DATETIME2);
    END
    ELSE
    BEGIN
        -- if earlier than SQL Server 2016 this uses the current time zone
        -- which may or may not be the same DST as historical dates
        SET @convertedDate = DATEADD(MI,(DATEDIFF(MI, SYSDATETIME(), SYSUTCDATETIME())), @LocalDate);
    END

    RETURN(@convertedDate);  
END

We managed to fix it with the following post deployment script. It was a similar workaround to that suggested by Martin Smith in the comments. We decided to place both functions in one script rather than a base function and an upgrade to help with maintainability.

-- delete function if we already have it
IF object_id(N'ConvertLocalDateToUtc', N'FN') IS NOT NULL
    DROP FUNCTION ConvertLocalDateToUtc
GO

DECLARE @SQLString nvarchar(MAX);  

-- build server version specific function

-- if SQL Server 2016 or later, this takes account of historical daylight saving times
IF (SELECT CAST(SERVERPROPERTY('ProductMajorVersion') AS INT)) >= 13
BEGIN
SET @SQLString = N'
CREATE FUNCTION [dbo].[ConvertLocalDateToUtc]
(
    @LocalDate DATETIME2, 
    @LocalZone NVARCHAR(128)
)
RETURNS DATETIME2
AS
BEGIN
    DECLARE @convertedDate DATETIME2;
    DECLARE @ZonedLocalDate DATETIMEOFFSET = @LocalDate AT TIME ZONE @LocalZone;
    DECLARE @ZonedUtcDate DATETIMEOFFSET = @ZonedLocalDate AT TIME ZONE ''UTC'';
    SET @convertedDate = CAST(@ZonedUtcDate AS DATETIME2);
    RETURN(@convertedDate);  
END
';
END
ELSE
BEGIN
-- if earlier than SQL Server 2016 this uses the current time zone
-- which may or may not be the same DST as historical dates
SET @SQLString = N'
CREATE FUNCTION [dbo].[ConvertLocalDateToUtc]
(
    @LocalDate DATETIME2, 
    @LocalZone NVARCHAR(128)
)
RETURNS DATETIME2
AS
BEGIN
    DECLARE @convertedDate DATETIME2;
    SET @convertedDate = DATEADD(MI,(DATEDIFF(MI, SYSDATETIME(), SYSUTCDATETIME())), @LocalDate);
    RETURN(@convertedDate);  
END
';
END

-- run the sql to create the function
EXECUTE sp_executesql @SQLString;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM