select 中的 CAST 到 TIME 查询需要很长时间才能执行
CAST to TIME in select query is taking long time to execute
declare
@fromLocalTime TIME = '06:00:00',
@toLocalTime TIME = '06:59:59',
@runDate AS DATETIME2
SET @runDate = GETUTCDATE()
DECLARE @notificationCreatedFromDate AS DATETIME2 = (SELECT DATEADD(day, -1, @runDate))
SELECT
Col1,
Col2,
Col3,
...,
Global.UDF_ConvertDateUtcToTimeZone(@runDate, tz.TimeZone) AS 'UserLocalTime',
tz.TimeZone
FROM Notification tn
INNER JOIN Dilect d ON d.DilectID = tn.DilectID AND d.IsActive = 1
INNER JOIN NotificationType nt ON nt.NotificationTypeID = tn.NotificationTypeID AND nt.IsActive = 1
INNER JOIN Task t ON t.TaskID = tn.TaskID AND t.IsActive = 1 AND t.ApplicationID = @ApplicationID
INNER JOIN UserTimeZone userTz ON userTz.UserID = tn.UserID AND userTz.ClientId = t.ClientId
INNER JOIN Global.TimeZone tz ON tz.TimeZoneId = userTz.TimeZoneId AND tz.IsActive = 1
WHERE CAST (Global.UDF_ConvertDateUtcToTimeZone(@runDate, tz.TimeZone) AS TIME) BETWEEN @fromLocalTime AND @toLocalTime
AND tn.CompletedDate IS NULL
AND tn.IsActive = 1
AND d.CompletedDate IS NOT NULL
AND tn.Created_DT >= @notificationCreatedFromDate and tn.Created_DT <= @runDate
WHERE CAST (Global.UDF_ConvertDateUtcToTimeZone(@runDate, tz.TimeZone) AS TIME) BETWEEN @fromLocalTime AND @toLocalTime
我有这个以前编写的查询,直到昨天都运行良好。突然这个查询需要很长时间才能执行(现在 10 分钟 - 之前 10 秒)。我之前在列中添加了建议索引。该问题是由于上述代码中突出显示的行(WHERE 子句)引起的。如果我保留该行代码,则查询需要 10 分钟才能执行,如果删除它,它将在 10 秒内执行,并且 returns 300K 记录。
我已经浏览了几篇关于 CAST 如何影响 select 语句的文章,但与场景无关。
我还尝试将除突出显示行之外的所有代码移动到外部查询,并在 where 子句中给出突出显示的查询,但看不到任何性能差异。
谁能告诉我这一行(WHERE 子句)可能存在的问题并提出解决此问题的建议。
Global.UDF_ConvertDateUtcToTimeZone(@runDate, tz.TimeZone) AS 'UserLocalTime',
SQL 服务器很难优化标量user-defined 函数,通常必须为每一行调用该函数。如果函数本身本身效率低下,那么您的问题就会成倍增加,并且会随着数据变大而不断增加。 Microsoft 已尝试在现代版本中解决此问题,but there are a lot of restrictions and limitations and they have to fix something in almost every cumulative update。
与其依赖引擎最终弥补这种低效率,更好的策略是从一开始就编写内联(而不是 multi-statement!)table-valued 函数。这导致将查询逻辑折叠到外部查询中,并为 SQL 服务器提供更好的统计信息和更多需要考虑的优化路径。
假设您的标量函数如下所示:
CREATE FUNCTION dbo.DoAThingToOneRow(@i int)
RETURNS int
AS
BEGIN
RETURN (SELECT [output] = @i + 1);
END
你这样称呼它:
SELECT [object_id],
[output] = dbo.DoAThingToOneRow([object_id])
FROM sys.all_objects;
改成这样:
CREATE FUNCTION dbo.DoAThingToAllRows(@i int)
RETURNS TABLE WITH SCHEMABINDING
AS
RETURN (SELECT [output] = @i + 1);
并这样称呼它:
SELECT o.[object_id],
f.[output]
FROM sys.all_objects AS o
CROSS APPLY dbo.DoAThingToAllRows(o.[object_id]) AS f;
我可以深入研究计划和执行统计数据之间的差异,但您应该使用自己的函数自行执行此操作,以获得更真实的比较。您可以检查 sys.dm_exec_query_stats
和 sys.dm_exec_function_stats
.
之类的内容
declare
@fromLocalTime TIME = '06:00:00',
@toLocalTime TIME = '06:59:59',
@runDate AS DATETIME2
SET @runDate = GETUTCDATE()
DECLARE @notificationCreatedFromDate AS DATETIME2 = (SELECT DATEADD(day, -1, @runDate))
SELECT
Col1,
Col2,
Col3,
...,
Global.UDF_ConvertDateUtcToTimeZone(@runDate, tz.TimeZone) AS 'UserLocalTime',
tz.TimeZone
FROM Notification tn
INNER JOIN Dilect d ON d.DilectID = tn.DilectID AND d.IsActive = 1
INNER JOIN NotificationType nt ON nt.NotificationTypeID = tn.NotificationTypeID AND nt.IsActive = 1
INNER JOIN Task t ON t.TaskID = tn.TaskID AND t.IsActive = 1 AND t.ApplicationID = @ApplicationID
INNER JOIN UserTimeZone userTz ON userTz.UserID = tn.UserID AND userTz.ClientId = t.ClientId
INNER JOIN Global.TimeZone tz ON tz.TimeZoneId = userTz.TimeZoneId AND tz.IsActive = 1
WHERE CAST (Global.UDF_ConvertDateUtcToTimeZone(@runDate, tz.TimeZone) AS TIME) BETWEEN @fromLocalTime AND @toLocalTime
AND tn.CompletedDate IS NULL
AND tn.IsActive = 1
AND d.CompletedDate IS NOT NULL
AND tn.Created_DT >= @notificationCreatedFromDate and tn.Created_DT <= @runDate
WHERE CAST (Global.UDF_ConvertDateUtcToTimeZone(@runDate, tz.TimeZone) AS TIME) BETWEEN @fromLocalTime AND @toLocalTime
我有这个以前编写的查询,直到昨天都运行良好。突然这个查询需要很长时间才能执行(现在 10 分钟 - 之前 10 秒)。我之前在列中添加了建议索引。该问题是由于上述代码中突出显示的行(WHERE 子句)引起的。如果我保留该行代码,则查询需要 10 分钟才能执行,如果删除它,它将在 10 秒内执行,并且 returns 300K 记录。 我已经浏览了几篇关于 CAST 如何影响 select 语句的文章,但与场景无关。 我还尝试将除突出显示行之外的所有代码移动到外部查询,并在 where 子句中给出突出显示的查询,但看不到任何性能差异。
谁能告诉我这一行(WHERE 子句)可能存在的问题并提出解决此问题的建议。
Global.UDF_ConvertDateUtcToTimeZone(@runDate, tz.TimeZone) AS 'UserLocalTime',
SQL 服务器很难优化标量user-defined 函数,通常必须为每一行调用该函数。如果函数本身本身效率低下,那么您的问题就会成倍增加,并且会随着数据变大而不断增加。 Microsoft 已尝试在现代版本中解决此问题,but there are a lot of restrictions and limitations and they have to fix something in almost every cumulative update。
与其依赖引擎最终弥补这种低效率,更好的策略是从一开始就编写内联(而不是 multi-statement!)table-valued 函数。这导致将查询逻辑折叠到外部查询中,并为 SQL 服务器提供更好的统计信息和更多需要考虑的优化路径。
假设您的标量函数如下所示:
CREATE FUNCTION dbo.DoAThingToOneRow(@i int)
RETURNS int
AS
BEGIN
RETURN (SELECT [output] = @i + 1);
END
你这样称呼它:
SELECT [object_id],
[output] = dbo.DoAThingToOneRow([object_id])
FROM sys.all_objects;
改成这样:
CREATE FUNCTION dbo.DoAThingToAllRows(@i int)
RETURNS TABLE WITH SCHEMABINDING
AS
RETURN (SELECT [output] = @i + 1);
并这样称呼它:
SELECT o.[object_id],
f.[output]
FROM sys.all_objects AS o
CROSS APPLY dbo.DoAThingToAllRows(o.[object_id]) AS f;
我可以深入研究计划和执行统计数据之间的差异,但您应该使用自己的函数自行执行此操作,以获得更真实的比较。您可以检查 sys.dm_exec_query_stats
和 sys.dm_exec_function_stats
.