多个预准备语句使用DBD :: Sybase中断事务

时间:2010-10-06 14:42:26

标签: perl transactions sybase dbi

在我的Perl脚本中,我使用DBD :: Sybase(通过DBI模块)连接到SQL Server 2008.下面的基本程序运行没有问题:

use DBI;

# assign values to $host, $usr, $pwd
my $dbh = DBI->connect("dbi:Sybase:$host", $usr, $pwd);
$dbh->do("BEGIN TRAN tr1");
my $update = $dbh->prepare("UPDATE mytable SET qty = ? where name = ?");
$update->execute(100, 'apple');
$dbh->do("END TRAN tr1");

但是,如果我在现有prepare语句之前再插入一个prepare语句,那么程序将如下所示:

...
my $insert = $dbh->prepare("INSERT INTO mytable (name, qty) VALUES (?, ?)");
my $update = $dbh->prepare("UPDATE mytable SET qty = ? where name = ?");
...

其余的都是一样的,然后当我运行它时,我得到了:

DBD::Sybase::db do failed: Server message number=3902 severity=16 state=1 line=1 server=xxx text=The COMMIT TRANSACTION request has no corresponding BEGIN TRANSACTION.

所以看起来额外的prepare语句会以某种方式破坏整个事务流。我通过DBD::ODBC驱动程序运行相同的代码,对SQL SERVER 2005没有任何问题。(但我的公司升级到2008年,我不得不使用DBD::Sybase解决其他一些问题。)

非常感谢有关如何解决此问题的任何帮助/建议。特别是,为另一个prepare使用不同的数据库句柄不是一个理想的解决方案,因为这样做会超过在单个事务中使用它们的目的。

UPDATE:如果我在附加插件上至少执行一次,那么程序再次正常运行。所以看起来每个准备好的语句都需要在Sybase下运行。但这不是ODBC的要求,并且通常不是合理的要求。无论如何要绕过它?

3 个答案:

答案 0 :(得分:1)

您正在学习perl和Sybase的基础知识并得出一些不正确的结论。

忘掉它在ODBC下做了什么。 ODBC很可能已打开AUTOCOMMIT,因此您没有任何事务控制。 (当DBD ::支持DB-Lib和CT-Lib时,为什么有人会使用ODBC,但这是一个单独的故事。)

Re:“所以看起来每个准备好的语句都需要在Sybase下运行。”

Rawheiser是对的。您期望通过准备批次但执行Do来实现什么?您希望在Sybase以外的其他地方执行哪些批处理,而不是在Sybase下执行?

vs准备/执行是完全不同的。准备/执行Sybase在数百万个程序中运行良好。你只需要了解它的作用,而不是你认为它应该做什么。准备让你加载一个批处理,一个在正常的Sybase意义上由GO终止的命令块。执行执行准备好的批处理(提供GO并将批处理发送到服务器),并捕获返回的任何内容(根据您设置的任何数组/变量)。

做即时,单一命令,没有准备。准备+执行合并。

只执行单个语句do,而且只执行动态SQL,因为这是你可以开始工作的全部内容,非常有限且非常不必要。

您目前有:

准备:

UPDATE
Execute (100)
ExecuteImmediate(Do):
COMMIT TRAN

当然,没有BEGIN TRAN。 (第一个“执行”执行,BEGIN TRAN消失了)

我认为你想要的(原定的)就是这个。忘掉'做':

准备:

BEGIN TRAN
UPDATE
COMMIT TRAN

执行(100)

然后将其更改为:

BEGIN TRAN
INSERT
UPDATE
COMMIT TRAN

执行(100)

你的$ update和$ insert会让你感到困惑(你正在执行一个多语句批处理,对吧?在准备批处理中间不是一个孤立的单个命令)。如果你摆脱它们,并考​​虑$ execute [你在批处理中准备的任何东西],它可能会帮助你更好地理解这个问题。

在完成上述所有操作之前,不要形成结论。

阅读BEGIN / COMMIT TRAN。

最后,究竟什么是“END TRAN”?我不认为您发布的代码块是真实的。

答案 1 :(得分:0)

事实证明,DBI的prepare方法在各种数据库驱动程序中并不是可移植的,如here所述。对于Sybase驱动程序,prepare很可能无法正常工作。一种说法是,在运行prepare之后,变量$insert->{NUM_OF_FIELDS}未定义。

要解决此问题,请执行以下操作之一:

1)不要做任何准备。只需在文本字符串中动态构造语句并运行$dbh->do($stmt)

2)在运行finish之前,对所有未完成的语句句柄(在该数据库句柄下)运行COMMIT TRAN。我个人更喜欢这种方式。

答案 2 :(得分:0)

不要动态创建SQL,这很危险(sql注入)。

您应该能够准备多个插入/更新,并且您指向DBI文档的链接并不表示您不能,它说某些驱动程序可能无法告诉您有关仅准备的语句的详细信息。

我将一个有错误的示例发布到dbi-users列表中以便注释,因为DBD :: Sybase维护者在那里挂起(参见dbi.perl.org)。