我可以启动存储过程并立即返回而无需等待它完成吗?


41

我们有一个存储过程,用户可以手动运行该存储过程以获取全天不断使用的报告的一些更新编号。

我有第二个存储过程,应该在第一个存储过程运行之后运行,因为它基于从该第一个存储过程获得的数字,但是它需要更长的时间运行并且用于单独的过程,所以我不想让用户等待第二个存储过程运行。

有没有一种方法可以让一个存储过程启动另一个存储过程,然后立即返回而无需等待结果?

我正在使用SQL Server 2005。


如何调用存储过程?ASP.NET Web应用程序?SSRS?
布朗斯通先生


@ Mr.Brownstone通常可以从ASP.Net Web应用程序中调用它,尽管其中一个以上可以调用它。我必须仔细检查。它有时也可以从SSRS手动运行。
雷切尔

@MartinSmith我过去曾经与SQL Service Broker一起工作过,希望能有一个更简单的方法。看起来像这样简单的事情如此复杂的设置。
雷切尔

1
@MartinSmith几乎就是我的想法-另外,对于SSRS,您不能,但是您可以做的是将Report Viewer合并到应用程序中并将rdl移入应用程序-这样就可以进行异步调用以及报告。
Mr.Brownstone

Answers:


27

看起来有多种方法可以完成此操作,但是我发现最简单的方法是Martin建议在SQL作业中设置过程,并使用存储过程中的异步sp_start_job命令启动它。

EXEC msdb.dbo.sp_start_job @job_name='Run2ndStoredProcedure'

这仅对我有用,因为我不需要为存储过程指定任何参数。

根据您的情况,其他可行的建议是

  • MartinSebastian建议的那样使用SQL Service Broker。如果您不介意设置和学习它的复杂性,那么这可能是最好的建议。
  • 在负责执行存储过程的代码中异步运行流程,如Mr.Brownstone建议

    这不是一个坏主意,但是在我的案例中,从多个地方调用了存储过程,因此找到所有这些地方并确保它们也调用第二个过程似乎并不实际。同样,第二个存储过程也很关键,而忘记运行它可能会对我们公司造成一些重大问题。

  • 使第一个过程设置一个标志,然后设置一个定期作业以检查该标志并运行是否设置了该标志,如Jimbo建议的那样。我不喜欢经常运行并每隔几分钟检查一次更改的作业,但是根据您的情况,当然值得考虑。

3
查看使用Service Broker的现成示例的异步过程执行。与sp_job相比,优点是它可以在Express Edition上运行,并且完全包含在数据库中(不依赖于MSDB作业表)。后者对于DBM故障转移和HA / DR恢复非常重要。
Remus Rusanu

射击,我看到马丁链接了同一篇文章。我将为故障转移/ DR参数保留注释。
Remus Rusanu

@RemusRusanu:好的,这是有关Service Broker的最佳信息来源之一,但是我想您已经知道了;-)。
玛丽安

我喜欢@Rusanu的链接,但我想要没有响应的东西(我认为与这个问题匹配)。我在abamacus.blogspot.com/2016/05/…上
珠算

另外,如果尝试启动SQL Agent作业,它将失败,EXECUTE permission was denied on the object 'sp_start_job', database 'msdb', schema 'dbo'.并且Azure上也不存在Service Broker或Sql Agent。我不知道为什么微软在经过十年半的询问后拒绝添加EXECUTE ASYNC RematerializeExpensiveCacheTable
伊恩·博伊德

8

您可以将服务代理与队列上的激活一起使用。这样,您可以将过程调用的参数发布到队列中。这大约需要插入的时间。提交事务并可能再等待几秒钟后,激活将自动异步调用接收程序。它不仅仅是wuold,还必须获取队列的参数并完成所需的工作。


7

这个老问题值得一个更全面的答案。其中一些在此处的其他答案/注释中提到,另一些可能适用于OP的特定情况,也可能不适用,但可能适用于希望从SQL异步调用存储的proc的其他情况。

只是为了完全明确:TSQL确实(本身)具有异步启动其他TSQL操作的能力

这并不意味着您仍然没有很多选择:

  • SQL代理作业:创建多个SQL作业,然后安排它们在所需的时间运行,或者使用来从“主控件”存储的proc中异步启动它们sp_start_job。如果您需要以编程方式监视其进度,只需确保每个作业都更新一个自定义的JOB_PROGRESS表即可(或者您可以xp_sqlagent_enum_jobs按照Gregory A. Larsen的这篇出色文章中的说明,检查它们是否已经使用了未记录的功能)。即使并行进程运行的是具有不同参数的相同存储过程,您也需要创建尽可能多的独立作业,以使并行进程运行。
  • SSIS包:对于更复杂的异步方案,请创建具有简单分支任务流的SSIS包。SSIS将在单个spid中启动这些任务,SQL将并行执行这些任务。从SQL代理作业中调用SSIS包。
  • 自定义应用程序:使用该语言提供的异步方法,以您选择的语言(C#,Powershell等)编写一个简单的自定义应用程序。在每个应用程序线程上调用一个SQL存储的proc。
  • OLE自动化:在SQL中,使用sp_oacreatesp_oamethod启动一个新进程,以互相调用存储的proc,如本文所述,Gregory A. Larsen也是如此。
  • Service Broker:研究使用Service Broker这是本文异步执行的一个很好的例子
  • CLR并行执行:使用CLR命令Parallel_AddSqlParallel_Execute如Alan Kaplan 在本文中所述(仅适用于SQL2005 +)。
  • 预定的Windows任务:出于完整性考虑列出,但我不喜欢此选项。

如果是我,那么我可能会在较简单的方案中使用多个SQL Agent Job,在较复杂的方案中使用SSIS软件包。

就您而言,调用SQL Agent作业听起来像是一个简单且易于管理的选择。

最后一条评论:SQL已经尽可能尝试并行化各个操作*。这意味着不能同时运行两个任务,而不能同时运行两个任务。仔细测试以查看它是否真的可以改善任何东西。

我们有一个开发人员创建了一个DTS包,以同时运行8个任务。不幸的是,它只是一个4-CPU服务器:)

*假设为默认设置。可以通过更改服务器的“最大并行度”或“相似性掩码”或使用MAXDOP查询提示来修改此设置。


6

是的,一种方法:

  1. 第一个存储过程完成后,它将插入一条记录,其中包含第二个存储过程运行所需的所有信息
  2. 第二个存储过程作为作业运行,每分钟或您决定什么时间运行
  3. 它检查插入的记录,进行处理并将该记录标记为完成

这将存储过程的执行限制为您正在运行的作业数,如果您只希望一次由一个客户端调用此存储就可以了-而您可能有多个客户端同时调用同一过程。此外,客户如何知道工作已完成而无需反复轮询数据库以了解是否已设置标志?
布朗斯通先生

1
@ Mr.Brownstone-作业在运行时可能会处理多个由不同存储过程调用排队的未完成任务。存储过程也可能会调用sp_start_job以启动它,或者根据需要动态创建作业,以避免每分钟轮询一次,但是这种情况下的复杂性可能意味着它不会比服务代理简单。
马丁·史密斯

@MartinSmith我实际上已经为第二个存储过程设置了作业,因为它以前每晚运行一次,直到我们发现它与第一个过程的编号无法正确同步的问题为止。如果我从第一个存储过程开始作业,它将异步运行并立即从SP返回吗?
雷切尔

@Rachel-是的 sp_start_job立即返回。虽然不记得需要什么权限。
马丁·史密斯

1
如果您不想打开大的安全漏洞,则从另一个过程/数据库开始作业是一个相当复杂的问题。Erland Sommarskog上有一篇文章介绍了您需要结合使用的各种技术:sommarskog.se/grantperm.html 但是,这里没有完整的解决方案。
塞巴斯蒂安·梅恩

1

另一种可能性是,当第一个存储过程完成时将其写入审计表,并在写入审计表时将触发器置于审计表上,以启动第二个存储过程。无需连续轮询,也不需要额外的SQL Server代理作业。


3
在对审计表的插入完成之前,第一个存储过程不会返回,并且在触发器完成执行(包括对第二个存储过程的调用)之前,不会发生第一个存储过程
Martin Smith

1
我已经研究过使用触发器,但是触发器与INSERTor UPDATE语句同步运行,而不是异步运行,因此Martin是正确的,第一个过程仍然会一直等到第二个过程完成返回。
雷切尔
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.