如何以编程方式检查(解析)TSQL语句的有效性?

51

我想让我的集成测试更具幂等性。一个想法是在每个测试后执行回滚,另一个想法是以某种编程方式解析文本,类似于 Query Analyzer 或 SSMS 中的绿色复选框。

如何在不使用 ADO.NET 运行命令的情况下让 SQL Server 解析我的命令?

更新:这是最终实现所需功能的解决方案:

using (DbCommand executeOnly = Factory.DbCommand())
{
    executeOnly.Connection = command.Connection;
    executeOnly.CommandType = CommandType.Text;
    executeOnly.CommandText = "SET NOEXEC ON;" + sqlCommand;
    executeOnly.Connection.Open();
    executeOnly.ExecuteNonQuery();
}
//set more properties of command.
command.Execute();

由于无法解释的原因,“SET PARSEONLY ON”只能在查询分析器中运行。我无法在ADO.NET连接上设置它。这也无所谓,因为PARSEONLY似乎只会捕捉语法错误,这不是常见的错误。而SET NOEXEC ON将会捕捉更广泛的错误类型,例如引用了缺失表或列的视图或存储过程中缺少参数。


1
我猜测这是 executeOnly.CommandText = "SET NOEXEC ON; " + sqlCommand; :3 - Liz
7个回答

38

我认为你要查找的命令是 SET NOEXEC ON。如果你对于连接设置了这个选项,查询语句将被解析但不会被执行。另一个选项是SET PARSEONLY ON,但是我真的不确定两者之间的区别。


4
根据我回答下面的评论,我认为 SQL 2K8 中使用 SET NOEXEC ON 可能存在 bug,其他人也已经复现了该问题。请查看我提交的 MS Connect bug:https://connect.microsoft.com/SQLServer/feedback/details/569263/set-noexec-on-does-not-flag-up-invalid-object-name-errors - AdaTheDev
PARSEONLY仅解析,NOEXEC不执行但解析编译。也就是说,对于缺少的表等会抛出错误。 - CervEd

23
+1 对Eric的回答。但我发现 SET FMTONLY ON 也很有用,因为 SET NOEXEC ON 看起来并没有抛出所有错误。
例如:
SELECT * FROM ATableThatDoesNotExist

在使用SET NOEXEC ON运行时,虽然数据库中不存在该表,但会提示操作成功。而使用SET FMTONLY ON则会抛出“无效对象名称”的错误。

使用SET FMTONLY ON还会返回有关将要返回的结果集的元数据,这非常方便。


当我使用 SET NOEXEC ONSELECT * FROM ATableThatDoesNotExist 时,出现了错误。 - KM.
@KM @Ada 抱歉,我在 SET NOEXEC ONSELECT 之间没有加上必要的 "GO"。 - Martin Smith
我没有使用GO,只是运行了SET NOEXEC ON并得到了“Command(s) completed successfully.”的消息,然后运行查询,结果产生了错误。如果你运行SET NOEXEC ON;SELECT...,就不会出现错误。但是在SRET和SELECT之间加上一个GO将会导致错误。 - KM.
@KM - 哦,对我来说,在 SQL Server 2008 RTM 上,我从来没有遇到过这个错误。然而,当我在 SQL Server 2005 上尝试时,如果你有 GO 语句,它确实会像你所说的那样表现出来。你是在尝试 2005 吗?看起来行为变了很奇怪。 - AdaTheDev
+1 表示在 SET FMTONLY ON 时也会返回有关结果集的元数据。这对于在 QA 环境中进行故障排除而不执行实际 SQL 语句的开发人员非常有用 :) - HappyTown

10

使用以下系统存储过程和函数,SQL Server 2012 可以解析您的语法、存储过程和表:

它们应该替代 "SET FMTONLY".

我已经测试过它们,它们比 "SET NOEXEC ON" 和 "SET PARSEONLY ON" 更有效。

示例:

不会抛出错误:

sp_describe_undeclared_parameters
    @tsql = N'SELECT object_id, name, type_desc FROM sys.indexes;'

将正确地抛出错误(在这种情况下,“SET NOEXEC”和“SET PARSEONLY”不会引发错误):

sp_describe_undeclared_parameters 
  @tsql = N'SELECT object_id, name, type_desc FROM sys.indexes;SELECT object_id, name, type_desc FROM sys.NOTaTABLE;'

7
请使用以下查询:
SET PARSEONLY ON
--Your query here
SET PARSEONLY OFF

与 set statistics time,io on 不同,您的示例仍会执行查询: SET PARSEONLY ON; select 'OK'; SET PARSEONLY OFF; 在关闭它之前我们需要添加 go 吗? - Alex

6

SET PARSEONLY :检查每个Transact-SQL语句的语法,并返回任何错误消息,而不编译或执行该语句。


当您按下绿色复选框时,SSMS正在执行的操作就是这样。您可以在SQL Profiler中轻松查看它发生的过程。 - deroby

6

这真的取决于测试的目的。

如果您的语句可行(不太复杂),最可靠的方法是在每个测试后使用回滚。

我过去曾经这样做,并且很高兴被通知了运行时问题,否则我无法发现。


+1 - 我同意。真正执行语句并在之后进行回滚将是测试最可靠的方式。 - Eric Petroelje
SET NOEXEC ON 仅检查查询语句的有效性。一个查询语句可能是有效的,但并不能返回正确的结果。 - OMG Ponies

4

VSTSDBPro拥有一个可通过编程访问的查询解析器:链接


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接