我有一个表来保存任务列表,这些任务需要在特定的日期和时间处理。棘手的部分是这些任务是递归运行时间必须根据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子句中的别名。
答案 0 :(得分: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英尺以外的任何变量)。
答案 1 :(得分: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
答案 2 :(得分:-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()