如何从SELECT语句输出进度消息?
我有一个SQL脚本,我想在运行时输出进度消息。让它在SQL语句之间输出消息很容易,但是我有一些运行时间很长的INSERT INTO SELECTs。有没有一种方法可以使select语句随即输出消息,例如,每隔1000行或者每5秒输出一次?
注意:这适用于SQL Anywhere,但是任何SQL方言的答案都可以。
解决方案
SQL本身对此没有规定。这样做的任何方式都将涉及直接与数据库引擎对话,并且在整个数据库中都不是标准的。
当然,没有针对此的SQL标准解决方案。抱歉,注定要死命了,但是我在Oracle,SQL Server,Sybase或者MySQL中还没有看到可以做到这一点的东西,因此对于SQLAnywhere来说我并不抱太大希望。
如果我们不打算使用Toad,则可以从表中生成一组INSERT语句,并将其配置为以用户输入频率提交。我们可以稍微修改一下脚本,然后在执行过程中查看已提交了多少新数据。
我同意SQL没有直接执行此操作的方法。一种方法可能是一次只插入TOP 1000,然后打印状态消息。然后继续根据需要重复进行此操作(以某种形式进行循环)。缺点是我们需要一种方法来跟踪自己的位置。
我应该指出,这种方法不会像仅进行一次大型INSERT那样有效。
确实,基于集合的操作(关系数据库使用的进度)的想法并不是太有用,至少不会像进度条所显示的那样(完成百分比与总计)。当优化器弄清楚需要做什么并真正理解了操作的全部成本时,我们已经完成了很大一部分操作。进度显示实际上是用于迭代操作,而不是设置操作。
这是关于常规SELECT语句的执行。对于作为单独语句的插入,可以通过监视语句的使用率,通过提交者采取各种方式来执行。如果它们是批量插入(选择,插入等),则我们确实遇到了我上面描述的相同问题。设置操作的批处理方式使进度条类型的显示显得毫无意义。
无法检索单个查询的执行状态。没有主流的数据库引擎提供此功能。
此外,如果存在任何进度实现,就会产生可衡量的开销,因此,如果查询已经花费了令人不舒服的长时间,而我们却想显示进度,则通过显示所述进度可能不是设计目标,从而导致额外的速度降低。
我们可能会发现有关估计SQL执行进度的这篇文章很有帮助,尽管其实际意义是有限的。
我是SQL Anywhere引擎开发团队的成员,目前无法做到这一点。我不能保证,但是我们正在考虑在将来的版本中添加这种功能。
我们可以通过定时几次运行来模拟用户的效果,然后以平均记录/秒的速度使进度条前进。
唯一的其他方法是
1请参考数据库引擎的API,以查看是否为此做了任何规定
或者
2将INSERT分解为许多较小的语句,并随时进行报告。但这将对性能产生重大的负面影响。
如果需要它或者要死掉,可以使用一些带有db变量的触发逻辑进行插入,更新,删除,并且可以通过sql逐次检索变量数据并向用户显示进度。
如果我们不想使用它,我可以写个例子并发送给我们。
一个想法可能是让另一个单独的进程对完成插入的表中的行数进行计数,以确定已经有多少行。当然,这最终需要我们知道总数。仅当我们不太担心服务器负载时,这可能才可以。
这是我要执行的操作(Sybase / SQL Server语法):
DECLARE @total_rows int SELECT @total_rows = count(*) FROM Source_Table WHILE @total_rows > (SELECT count(*) FROM Target_Table) BEGIN SET rowcount 1000 print 'inserting 1000 rows' INSERT Target_Table SELECT * FROM Source_Table s WHERE NOT EXISTS( SELECT 1 FROM Target_Table t WHERE t.id = s.id ) END set rowcount 0 print 'done'
或者,我们可以根据ID(假设ID是一个数字)来执行此操作:
DECLARE @min_id int, @max_id int, @start_id int, @end_id int SELECT @min_id = min(id) , @max_id = max(id) FROM Source_Table SELECT @start_id = @min_id , @end_id = @min_id + 1000 WHILE @end_id <= @max_id BEGIN print 'inserting id range: ' + convert(varchar,@start_id) + ' to ' + convert(varchar,@end_id) INSERT Target_Table SELECT * FROM Source_Table s WHERE id BETWEEN @start_id AND @end_id SELECT @start_id = @end_id + 1, @end_id = @end_id + 1000 END set rowcount 0 print 'done'