最近我在开发中遇到个挺棘手的问题,一段T-SQL语句在开发环境中明明跑得好好的,发布到生产环境却报错。
经过排查,只有WinXP下才会出现,即使是在干净的虚拟机环境中。客户端不管是用C#还是Delphi来调用这段T-SQL语句都报错,但在SQL Server Management Studio(SSMS)中运行却正常。
排除开发语言的原因,会不会是SQL Server的版本问题呢又测了一下,不管是SQL Server 2008 R2还是SQL Server 2012都一样。
按报错信息来推断,只可能是SQL Server数据库引擎 的问题,SSMS用的是SQL Server Native Client ,我们的客户端用的是OLEDB,虽然性能上有差别,但不至于报错吧。最近一次更新使用了新的with ... as ...语法,难道是这个引起的?
代码比较长,举个简单例子:
with m as (select * from test)
select id from m
with也就是公用表表达式 (Common Table Expression, CTE),SQL Server 从2005开始支持,符合ANSI SQL 标准,OLEDB作为通用的数据库引擎应该能支持。
查了下万能的Stack Overflow,有救了,->>传送门。那位仁兄用VBScript通过OLEDB方式调用也出过类似的问题,解决办法是在with语句之前加个分号“;”。
看来真的是WinXP中的OLEDB引擎的Bug引起,现在维保过了微软估计也不打算更新,这Bug肯定永无修复之日。
大家感受下:
1、建个临时表Test,数据库随便建一个,然后在WinXP中测下。
private void button1_Click(object sender, EventArgs e)
{
OleDbConnection conn = new OleDbConnection("Provider=SQLOLEDB.1;Password=12345;Persist Security Info=True;User ID=sa;Initial Catalog=Test;Data Source=localhost");
conn.Open();
OleDbCommand cmd=conn.CreateCommand();
cmd.CommandText = " with m as (select * from test)"; //CTE
cmd.CommandText+=" select * from m";
OleDbTransaction ts = conn.BeginTransaction();
cmd.Transaction = ts;
cmd.ExecuteNonQuery();
ts.Commit();
MessageBox.Show("Done!");
}
2、把cmd.CommandText = " with m as (select * from test)"; //CTE 这句改成 cmd.CommandText = ";with m as (select * from test)"; //CTE
其实彻底的解决办法,就是不用OLEDB,改成SQL Server Native Client,但旧代码改起来得费不少时间。