公共表达式错误

4

我必须使用老式的ADODB(而不是ADO.NET)来执行包含公共表达式的语句。我正在使用(必须使用)SQLOLEDB提供程序。在从Windows 7 / Windows Server 2008客户端执行DML语句时,一切正常,但在WinXP或Win2K3服务器上执行时却不行。我已经对该例程进行了分析,并发现旧操作系统发送了略有不同的SQL语句。

Win7 + 2008 =    exec sp_executesql N'WITH source(Vsl, Cpt, SrcTyp, SrcNum, Opn, JobNum, Qty, Cst, Vry, Reg, Vnt, Sbk) AS ...'

WinXP + Win2K3 = exec sp_executesql N'exec WITH source(Vsl, Cpt, SrcTyp, SrcNum, Opn, JobNum, Qty, Cst, Vry, Reg, Vnt, Sbk) AS ...'

请注意命令文本中额外的“exec”。
似乎旧操作系统上的SQLOLEDB.1版本会错误处理WITH语句,并将其视为需要加上“exec”。
有人能解释一下吗?我是否可以应用SQLOLEDB驱动程序更新到旧操作系统?或者有其他解决方法。

2
细节很重要--如果CTE不是递归的,那么也没有任何好处,所以你可以将其重写为派生表/内联视图。 - OMG Ponies
4
咳嗽声* 存储过程 咳嗽声 - 3Dave
2个回答

4
(FYI,您应该重新查看一些现有问题,因为它们大多数似乎有有用的答案,这些答案似乎可以解决问题;您的评论甚至表明了这一点。如果它们有答案,请接受它。)
如果您真的需要在此处使用CTE(这意味着您正在执行某些递归操作,而不仅仅是将其用作方便而不是内联选择或内联连接),最简单和最快的解决方法可能是将您的SQL包含在自己对sp_executesql的调用中。您最终会嵌套调用它(看起来很傻),但不应引起任何实际问题。

1

如果查询中没有参数,将查询封装在sp_executesql语句中可以很好地工作,否则参数不会被解析,因为它们在到达ADO时已经被放在引号字符串中,这会导致语法错误。

我所做的是创建一个TADOQuery派生类,重写构造函数,如下所示:

constructor TCPADOQuery.Create(AOwner: TComponent);
begin
  inherited;
  TWideStringList(SQL).OnChange := LocalQueryChanged;
end;

LocalQueryChanged 然后检查查询是否以公共表达式开头,并在查询开头插入一个虚拟声明,这是 XP ADO 解析器可以理解的。如果 CTE 不是查询中的第一条语句,则必须在其前面加上分号,因此我们必须先修复它:

procedure TCPADOQuery.LocalQueryChanged(Sender: TObject);
var
  a: WideString;
begin
  if not (csLoading in ComponentState) then
    Close;
  a := Trim(SQL.Text);
  if Uppercase(copy(a, 1, 4)) = 'WITH' then a := ';' + a;
  if Uppercase(copy(a, 1, 5)) = ';WITH' then
    a := 'DECLARE @DummyForADO_XP BIT'#13#10 + a;
  CommandText := a;
end;

这解决了问题,并且使我不必重新编写所有使用CTEs和参数的代码。


虽然我没有在这里使用你的完整答案,但在语句开头添加一个虚拟声明以使XP解析器工作的技巧真是太好了。我正在支持将Access前端与SQL Server配合使用,这让我感到非常疯狂。 - Marc Talbot

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