1

我有一个表格来保存任务列表,这些任务需要在特定的日期和时间处理。棘手的部分是这些任务是递归的,运行时间必须基于 5 个不同的参数来计算。

通过 UDF 计算运行时间很简单:

Function dbo.task_next_run(
    @task_type varchar(10),
    @task_schedule_day_of_week varchar(20),
    @task_schedule_time varchar(20),
    @task_period smallint,
    @last_run datetime
)
Returns datetime
...
...
...
Return @next_run

我最后的任务查询是这样的:

SELECT id, 
       task_name, 
       last_run 
From tasks 
Where dbo.task_next_run
(
   task_type, @task_schedule_day_of_week, 
   @task_schedule_time, @task_period, @last_run
) < getdate() and 
dbo.task_next_run
(
     task_type, @task_schedule_day_of_week, 
     @task_schedule_time, @task_period, @last_run
) > last_run

我的问题是在 where 子句中运行相同的函数 2 次。我需要一个解决方案来在 where 子句中使用计算值作为别名。

4

3 回答 3

4

你为什么不这样做:

DECLARE @now DATETIME = CURRENT_TIMESTAMP;

SELECT id, task_name, last_run
FROM 
(
  SELECT id, task_name, last_run, d = dbo.task_next_run
  (task_type, @task_schedule_day_of_week, @task_schedule_time, @task_period, @last_run)
  From tasks 
) AS x
WHERE x.d < @now
AND x.d > x.last_run;

我相当肯定 SQL Server 会将它折叠成相同的东西,并且只调用该函数一次而不是两次。不过,根据函数的性质,它可能仍每行执行一次。您是否考虑过将 UDF 转换为内联表值函数?这些通常优化得更好。

另一种选择是(如评论中所述):

DECLARE @now DATETIME = CURRENT_TIMESTAMP;

DECLARE @d TABLE(task_type INT PRIMARY KEY, post DATETIME);

INSERT @d SELECT task_type, dbo.task_next_run(task_type, @variables)
  FROM (SELECT task_type FROM dbo.tasks GROUP BY task_type);

现在你可以说:

SELECT t.id, t.task_name, t.last_run
FROM dbo.tasks AS t
INNER JOIN @d AS d
ON t.task_type = d.task_type
AND t.last_run > d.post
WHERE d.post < @now;

您甚至可以先进一步过滤:

DELETE @d WHERE post >= @now;

这使您可以消除上面的 WHERE。

总而言之,它可能仍然优化相同,但可能值得一试以获得更好的性能(对于这里的任何人来说,从 30,000 英尺预测的变量太多了)。

于 2012-08-30T17:23:03.827 回答
2

交叉应用是我需要的。这是 Cross Apply 的最终查询。

SELECT id, task_name, last_run, func.next_run
FROM tasks
Cross Apply (Select dbo.task_next_run(task_type, @task_schedule_day_of_week, @task_schedule_time, @task_period, @last_run) as next_run) as func
WHERE 
func.next_run < getdate() and
func.next_run > last_run
于 2012-08-30T17:21:38.850 回答
-1
SELECT id, 
       task_name, 
       last_run 
       From tasks 
WHERE dbo.task_next_run
      (
            task_type, @task_schedule_day_of_week, 
            @task_schedule_time, @task_period, @last_run
      ) BETWEEN  last_run AND getdate() 
于 2012-08-30T17:25:18.693 回答