简体   繁体   English

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

[英]Using user defined function (UDF) in where clause more than once with running the function only once

I've a table to keep list of tasks and these tasks need to be processed on specific date and time. 我有一张桌子来保存任务列表,这些任务需要在特定的日期和时间进行处理。 Tricky part is these tasks are recursive running time must be calculated based on 5 different parameters. 棘手的是这些任务是递归的,运行时间必须基于5个不同的参数来计算。

Calculating running time through UDF was simple part: 通过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

My final task query was this: 我最后的任务查询是这样的:

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

My problem is running same function 2 times in where clause. 我的问题是在where子句中运行相同的函数2次。 I need a solution to use calculated value as alias in where clause. 我需要一种解决方案,以将计算值用作where子句中的别名。

Why don't you do: 你为什么不这样做:

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;

I am fairly certain though that SQL Server will collapse it to the same thing, and only call the function once and not twice. 我相当确定,尽管SQL Server会将其折叠为同一事物,并且只调用一次函数,而不是两次。 Depending on the nature of the function, it might still execute once per row, though. 但是,根据函数的性质,它可能仍会每行执行一次。 Have you considered converting the UDF to an inline table-valued function? 您是否考虑过将UDF转换为内联表值函数? These generally optimize much better. 这些通常优化得更好。

Another option would be (as described in a comment): 另一个选择是(如评论中所述):

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);

Now you can say: 现在您可以说:

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;

You could even filter further first: 您甚至可以先进一步过滤:

DELETE @d WHERE post >= @now;

This allows you to eliminate the WHERE above. 这使您可以消除上面的WHERE。

All told it might still optimize the same, but could be worth a shot at slightly better performance (way too many variables for anyone here to predict from 30,000 feet). 总而言之,它可能仍然可以优化相同的性能,但是值得以稍微好一点的性能来尝试(对于这里的任何人,从30,000英尺的距离进行预测都需要太多变量)。

Cross Apply is what I needed. 交叉应用是我所需要的。 Here is the final query with Cross Apply. 这是使用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
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() 

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM