Sql 在where子句中多次使用用户定义函数(UDF)并仅运行该函数一次

Sql 在where子句中多次使用用户定义函数(UDF)并仅运行该函数一次,sql,sql-server,sql-server-2008,Sql,Sql Server,Sql Server 2008,我有一个表格来保存任务列表,这些任务需要在特定的日期和时间进行处理。棘手的是,这些任务是递归的,必须根据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,

我有一个表格来保存任务列表,这些任务需要在特定的日期和时间进行处理。棘手的是,这些任务是递归的,必须根据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子句中使用计算值作为别名的解决方案。

我需要交叉应用。下面是交叉应用的最终查询

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
交叉申请是我需要的。下面是交叉应用的最终查询

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
你为什么不:

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;
现在你可以说:

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;
这允许您消除上面的WHERE

总而言之,它可能仍然优化了相同的性能,但可能值得一试,性能稍好一点,因为这里的任何人都无法从30000英尺的高度预测太多的变量。

为什么不这样做:

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;
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() 
现在你可以说:

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;
这允许您消除上面的WHERE


总而言之,它可能仍然优化相同的性能,但可能值得一试,性能稍好一点,因为这里的任何人都无法从30000英尺的高度预测太多的变量。

不。介于>和

之间是不同的。。。它与>=相同,且编号。介于之间与>和<。。。这与>=相同,在查询中使用GetDate是在追逐移动目标,影响性能,并可能产生奇怪的结果,例如,随着日期的变化。在变量中捕获当前日期/时间,然后根据需要使用该值几乎总是一个更好的主意。这在存储过程中的多个语句中更为重要。多次使用GetDate最常见的原因是在捕获长时间运行的操作的开始和结束时间时。在查询中使用GetDate是为了追逐移动的目标,会影响性能,并且可能会产生奇怪的结果,例如,随着日期的变化。在变量中捕获当前日期/时间,然后根据需要使用该值几乎总是一个更好的主意。这在存储过程中的多个语句中更为重要。多次使用GetDate最常见的原因是在捕获长时间运行的操作的开始时间和结束时间时。您的想法几乎是一样的,而且行之有效。。关于每行执行;在我的例子中,必须为每一行运行它来计算下一次运行。递归规则可以更改。因此,在我的例子中没有区别。因为5个输入中有4个来自变量,所以在给定一组有限的任务类型值的情况下,首先生成一个包含所有可能结果的表是可行的。我明白你的意思,这可能是个明智的决定。。我预计会有几百个任务。。谢谢。你的想法几乎是一样的,而且很管用。。关于每行执行;在我的例子中,必须为每一行运行它来计算下一次运行。递归规则可以更改。因此,在我的例子中没有区别。因为5个输入中有4个来自变量,所以在给定一组有限的任务类型值的情况下,首先生成一个包含所有可能结果的表是可行的。我明白你的意思,这可能是个明智的决定。。我预计会有几百个任务。。谢谢
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()