如何使用用户定义函数遍历列值
How to use a User Defined Function to iterate through column values
我有一个函数可以根据某个办公地点将 UTC 时间转换为本地时间。
该函数的两个参数是作为 datetime2 数据类型的 UTC 和作为 int 数据类型的 Office。
SELECT [fn].[ConvertFromUTC]('2021-03-14 07:00:00', 5740)
结果:2021-03-14 03:00:00
这里是 table,我想根据支持站点位置(办公室)将所有 UTC 时间转换为本地时间。
执行此操作的最佳方法是什么?我尝试使用类似的东西,但不确定如何遍历每个支持站点和 UTC 时间。建议?在这种情况下 Cursor 是理想的选择吗?
DECLARE @meh nvarchar(50)
DECLARE @x datetime2
DECLARE @y int
Set @x = '2021-03-14 07:00:00'
Set @y = 20608
EXEC @meh = fn.ConvertFromUTC
@DateTime = @x,
@Office = @y
SELECT @meh
这是函数的代码。
ALTER Function [fn].[ConvertFromUTC]
/*This function converts a DateTime from UTC to local time at each office.*/
(
@DateTime DATETIME2(0),
@Office int
)
RETURNS DATETIME2(0)
AS
BEGIN
DECLARE @TimeZone nvarchar(50)
DECLARE @Result DATETIME2(0)
IF @Office IN ('20608','5740')
BEGIN
SET @TimeZone = 'US Eastern Standard Time'
SET @Result = @DateTime at time zone 'UTC' at time zone @TimeZone
END
ELSE IF @Office = '597'
BEGIN
SET @TimeZone = 'W. Europe Standard Time'
SET @Result = @DateTime at time zone 'UTC' at time zone @TimeZone
SET @Result = DATEADD(HOUR,-1,@Result) /* 'AT TIME ZONE' functionality for Europe uses the wrong offset. This corrects it.*/
END
ELSE IF @Office = '6179' /*Not using 'AT TIME ZONE' functionality because it doesn't recognize that Australia observes Daylight Savings Time*/
BEGIN
/*DateTime is the end and beginning of Sydney daylight savings time in UTC.
Ends first Sunday in April at 2:00:00 and begins first Sunday in October at 3:00:00
In UTC, Ends first Saturday in April at 16:00:00 and begins first Saturday in October 16:00:00*/
IF @DateTime >= DATEADD(dd, (5-(DATEDIFF(dd,0,DATEADD(mm,(YEAR(@DateTime)-1900) * 12 + 3,0))%7)),DATEADD(mm,(YEAR(@DateTime)-1900) * 12 + 3,0))+'16:00:00'
AND @DateTime < DATEADD(dd,0 + (5-(DATEDIFF(dd,0,DATEADD(mm,(YEAR(@DateTime)-1900) * 12 + 9,0))%7)),DATEADD(mm,(YEAR(@DateTime)-1900) * 12 + 9,0))+'16:00:00'
BEGIN
SET @Result = DATEADD(hour,10,@DateTime)
END
ELSE
BEGIN
SET @Result = DATEADD(hour,11,@DateTime)
END
END
RETURN @Result
END
首先,您目前拥有的是 标量用户定义函数 (UDF)。
你可以像这样简单地使用它:
SELECT fn.ConvertFromUTC(t.YourDate, 5740)
FROM YourTable t;
标量 UDF 由于各种原因很慢,应该避免使用,尽管大多数都得到了 SQL Server 2019 的 UDF 内联的帮助,它是通常最好将其重写为 内联 Table 值函数 。这 returns 一个行集,有点像参数化视图。
要使其内联,它必须是单个 RETURN (SELECT
语句
CREATE OR ALTER Function [fn].[ConvertFromUTC]
/*This function converts a DateTime from UTC to local time at each office.*/
(
@DateTime DATETIME2(0),
@Office int
)
RETURNS TABLE
AS RETURN
(
SELECT Result = CASE
WHEN @Office IN ('20608', '5740')
THEN @DateTime AT TIME ZONE 'UTC' AT TIME ZONE 'US Eastern Standard Time'
WHEN @Office = '597'
THEN DATEADD(HOUR, -1, @DateTime AT TIME ZONE 'UTC' AT TIME ZONE 'W. Europe Standard Time')
WHEN @Office = '6179'
THEN
/*DateTime is the end and beginning of Sydney daylight savings time in UTC.
Ends first Sunday in April at 2:00:00 and begins first Sunday in October at 3:00:00
In UTC, Ends first Saturday in April at 16:00:00 and begins first Saturday in October 16:00:00*/
CASE WHEN @DateTime >= DATEADD(dd, (5-(DATEDIFF(dd, 0, DATEADD(mm, (YEAR(@DateTime) - 1900) * 12 + 3, 0)) %7)), DATEADD(mm,(YEAR(@DateTime) - 1900) * 12 + 3, 0)) + '16:00:00'
AND @DateTime < DATEADD(dd, 0 + (5 - (DATEDIFF(dd, 0, DATEADD(mm, (YEAR(@DateTime)-1900) * 12 + 9, 0)) % 7)), DATEADD(mm,(YEAR(@DateTime) - 1900) * 12 + 9, 0)) + '16:00:00'
THEN DATEADD(hour,10,@DateTime)
ELSE DATEADD(hour,11,@DateTime)
END
END
);
GO
你可以这样使用它:
SELECT utc.Result
FROM YourTable t
CROSS APPLY fn.ConvertFromUTC(t.YourDate, 5740) utc;
-- Because it's only one value you can also do this
SELECT
(SELECT utc.Result FROM fn.ConvertFromUTC(t.YourDate, 5740))
FROM YourTable t;
我必须说,我对这个函数的原作者有异议,他明明知道AT TIME ZONE
,但认为它不能正常工作。
SET @TimeZone = 'W. Europe Standard Time'
SET @Result = @DateTime at time zone 'UTC' at time zone @TimeZone
SET @Result = DATEADD(HOUR,-1,@Result) /* 'AT TIME ZONE' functionality for Europe uses the wrong offset. This corrects it.*/
欧洲不是单一的,大概正确的时区应该是'GMT Standard Time'
。
ELSE IF @Office = '6179' /*Not using 'AT TIME ZONE' functionality because it doesn't recognize that Australia observes Daylight Savings Time*/
BEGIN
/*DateTime is the end and beginning of Sydney daylight savings time in UTC.
同样,澳大利亚不是一个时区,我怀疑使用了 E. Australia Standard Time
而不是 'AUS Eastern Standard Time'
。
您可以通过select * from sys.time_zone_info
查看服务器上所有可用的时区,您也可以通过Windows添加更多时区。
还有一点就是这个函数本该是converts a DateTime from UTC to local time
,但是两次使用AT TIME ZONE
只是在从一个时区转换到另一个时区的时候用到的,如果时间已经在 'UTC'
.
,则只应执行一次
还有一点:我建议你在查询的table中实际存储正确的时区,然后你可以通过时区 而不是 @Office
并避免一堆 CASE
表达式。
我有一个函数可以根据某个办公地点将 UTC 时间转换为本地时间。 该函数的两个参数是作为 datetime2 数据类型的 UTC 和作为 int 数据类型的 Office。
SELECT [fn].[ConvertFromUTC]('2021-03-14 07:00:00', 5740)
结果:2021-03-14 03:00:00
这里是 table,我想根据支持站点位置(办公室)将所有 UTC 时间转换为本地时间。
执行此操作的最佳方法是什么?我尝试使用类似的东西,但不确定如何遍历每个支持站点和 UTC 时间。建议?在这种情况下 Cursor 是理想的选择吗?
DECLARE @meh nvarchar(50)
DECLARE @x datetime2
DECLARE @y int
Set @x = '2021-03-14 07:00:00'
Set @y = 20608
EXEC @meh = fn.ConvertFromUTC
@DateTime = @x,
@Office = @y
SELECT @meh
这是函数的代码。
ALTER Function [fn].[ConvertFromUTC]
/*This function converts a DateTime from UTC to local time at each office.*/
(
@DateTime DATETIME2(0),
@Office int
)
RETURNS DATETIME2(0)
AS
BEGIN
DECLARE @TimeZone nvarchar(50)
DECLARE @Result DATETIME2(0)
IF @Office IN ('20608','5740')
BEGIN
SET @TimeZone = 'US Eastern Standard Time'
SET @Result = @DateTime at time zone 'UTC' at time zone @TimeZone
END
ELSE IF @Office = '597'
BEGIN
SET @TimeZone = 'W. Europe Standard Time'
SET @Result = @DateTime at time zone 'UTC' at time zone @TimeZone
SET @Result = DATEADD(HOUR,-1,@Result) /* 'AT TIME ZONE' functionality for Europe uses the wrong offset. This corrects it.*/
END
ELSE IF @Office = '6179' /*Not using 'AT TIME ZONE' functionality because it doesn't recognize that Australia observes Daylight Savings Time*/
BEGIN
/*DateTime is the end and beginning of Sydney daylight savings time in UTC.
Ends first Sunday in April at 2:00:00 and begins first Sunday in October at 3:00:00
In UTC, Ends first Saturday in April at 16:00:00 and begins first Saturday in October 16:00:00*/
IF @DateTime >= DATEADD(dd, (5-(DATEDIFF(dd,0,DATEADD(mm,(YEAR(@DateTime)-1900) * 12 + 3,0))%7)),DATEADD(mm,(YEAR(@DateTime)-1900) * 12 + 3,0))+'16:00:00'
AND @DateTime < DATEADD(dd,0 + (5-(DATEDIFF(dd,0,DATEADD(mm,(YEAR(@DateTime)-1900) * 12 + 9,0))%7)),DATEADD(mm,(YEAR(@DateTime)-1900) * 12 + 9,0))+'16:00:00'
BEGIN
SET @Result = DATEADD(hour,10,@DateTime)
END
ELSE
BEGIN
SET @Result = DATEADD(hour,11,@DateTime)
END
END
RETURN @Result
END
首先,您目前拥有的是 标量用户定义函数 (UDF)。
你可以像这样简单地使用它:
SELECT fn.ConvertFromUTC(t.YourDate, 5740)
FROM YourTable t;
标量 UDF 由于各种原因很慢,应该避免使用,尽管大多数都得到了 SQL Server 2019 的 UDF 内联的帮助,它是通常最好将其重写为 内联 Table 值函数 。这 returns 一个行集,有点像参数化视图。
要使其内联,它必须是单个 RETURN (SELECT
语句
CREATE OR ALTER Function [fn].[ConvertFromUTC]
/*This function converts a DateTime from UTC to local time at each office.*/
(
@DateTime DATETIME2(0),
@Office int
)
RETURNS TABLE
AS RETURN
(
SELECT Result = CASE
WHEN @Office IN ('20608', '5740')
THEN @DateTime AT TIME ZONE 'UTC' AT TIME ZONE 'US Eastern Standard Time'
WHEN @Office = '597'
THEN DATEADD(HOUR, -1, @DateTime AT TIME ZONE 'UTC' AT TIME ZONE 'W. Europe Standard Time')
WHEN @Office = '6179'
THEN
/*DateTime is the end and beginning of Sydney daylight savings time in UTC.
Ends first Sunday in April at 2:00:00 and begins first Sunday in October at 3:00:00
In UTC, Ends first Saturday in April at 16:00:00 and begins first Saturday in October 16:00:00*/
CASE WHEN @DateTime >= DATEADD(dd, (5-(DATEDIFF(dd, 0, DATEADD(mm, (YEAR(@DateTime) - 1900) * 12 + 3, 0)) %7)), DATEADD(mm,(YEAR(@DateTime) - 1900) * 12 + 3, 0)) + '16:00:00'
AND @DateTime < DATEADD(dd, 0 + (5 - (DATEDIFF(dd, 0, DATEADD(mm, (YEAR(@DateTime)-1900) * 12 + 9, 0)) % 7)), DATEADD(mm,(YEAR(@DateTime) - 1900) * 12 + 9, 0)) + '16:00:00'
THEN DATEADD(hour,10,@DateTime)
ELSE DATEADD(hour,11,@DateTime)
END
END
);
GO
你可以这样使用它:
SELECT utc.Result
FROM YourTable t
CROSS APPLY fn.ConvertFromUTC(t.YourDate, 5740) utc;
-- Because it's only one value you can also do this
SELECT
(SELECT utc.Result FROM fn.ConvertFromUTC(t.YourDate, 5740))
FROM YourTable t;
我必须说,我对这个函数的原作者有异议,他明明知道AT TIME ZONE
,但认为它不能正常工作。
SET @TimeZone = 'W. Europe Standard Time'
SET @Result = @DateTime at time zone 'UTC' at time zone @TimeZone
SET @Result = DATEADD(HOUR,-1,@Result) /* 'AT TIME ZONE' functionality for Europe uses the wrong offset. This corrects it.*/
欧洲不是单一的,大概正确的时区应该是'GMT Standard Time'
。
ELSE IF @Office = '6179' /*Not using 'AT TIME ZONE' functionality because it doesn't recognize that Australia observes Daylight Savings Time*/
BEGIN
/*DateTime is the end and beginning of Sydney daylight savings time in UTC.
同样,澳大利亚不是一个时区,我怀疑使用了 E. Australia Standard Time
而不是 'AUS Eastern Standard Time'
。
您可以通过select * from sys.time_zone_info
查看服务器上所有可用的时区,您也可以通过Windows添加更多时区。
还有一点就是这个函数本该是converts a DateTime from UTC to local time
,但是两次使用AT TIME ZONE
只是在从一个时区转换到另一个时区的时候用到的,如果时间已经在 'UTC'
.
还有一点:我建议你在查询的table中实际存储正确的时区,然后你可以通过时区 而不是 @Office
并避免一堆 CASE
表达式。