多个准备好的语句使用 DBD::Sybase 中断事务
在我的 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 来解决其他一些问题。)
任何有关如何解决此问题的帮助/建议将不胜感激。特别是,为另一个准备使用不同的数据库句柄并不是理想的解决方案,因为这将违背将它们放在单个事务中的目的。
更新:事实证明,如果我在附加插入上至少执行一次,那么程序将再次正常运行。所以看起来每个准备好的语句都需要在 Sybase 下运行。但这不是 ODBC 的要求,而且一般来说也不是合理的要求。无论如何要绕过它吗?
In my Perl script, I use DBD::Sybase (via DBI module) to connect to a SQL Server 2008. The base program as below runs without problem:
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");
however, if I insert one more prepare
statement right before the existing prepare
statement, to have the program look like:
...
my $insert = $dbh->prepare("INSERT INTO mytable (name, qty) VALUES (?, ?)");
my $update = $dbh->prepare("UPDATE mytable SET qty = ? where name = ?");
...
and the rest is all the same, then when I run it, I got:
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.
So looks like the additional prepare
statement somehow disrupted the entire transaction flow. I had been running the same code via the DBD::ODBC
driver with no problem against a SQL SERVER 2005. (But my firm upgraded to 2008 and I had to use the DBD::Sybase
to get around some other problems.)
Any help / suggestion on how to resolve this issue would be much appreciated. In particular, using a different db handle for the other prepare
is not a desired solution since that will beat the purpose of having them in a single transaction.
UPDATE: Turns out if I execute at least once on the additional insert, then the program is again run fine. So looks like every prepared statement needs to be run under Sybase. But that isn't a requirement with ODBC and isn't a reasonable requirement in general. Anyway to get around it?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
发布评论
评论(3)
您正在学习 perl 和 Sybase 基础知识并得出一些错误的结论。
暂时忘记它在 ODBC 下的作用。 ODBC 很可能打开了 AUTOCOMMIT,因此您没有任何事务控制。 (当 DBD:: 支持 DB-Lib 和 CT-Lib 时,为什么有人会使用 ODBC,这是我无法理解的,但这是一个单独的故事。)
回复:“所以看起来每个准备好的语句都需要在 Sybase 下运行。” Rawheiser
是正确的。您究竟希望通过准备一批但执行 Do 来实现什么?除了在 Sybase 下之外,您还希望在哪里执行在 Sybase 下准备的批处理?
执行与准备/执行有很大不同。 Sybase 的准备/执行在数百万个程序中运行良好。你只需要了解它的作用,而不是你认为它应该做什么。准备让您加载一批,即在正常 Sybase 意义上由 GO 终止的命令块。 Execute 执行准备好的批处理(提供 GO 并将批处理发送到服务器),并捕获返回的任何内容(根据您设置的任何数组/变量)。
执行是立即的、单一的命令,无需准备。准备+执行相结合。
仅执行单语句 do's,并且仅执行动态 SQL,因为这就是您可以开始工作的全部内容,这是非常有限的,而且完全没有必要。
您当前有:
准备:
UPDATE
Execute (100)
ExecuteImmediate(Do):
COMMIT TRAN
当然,没有 BEGIN TRAN。 (执行的第一个“do”,BEGIN TRAN消失了)
我认为你想要的(最初的意图)是这样的。忘记“do”:
准备:
BEGIN TRAN
UPDATE
COMMIT TRAN
执行(100)
然后将其更改为:
BEGIN TRAN
INSERT
UPDATE
COMMIT TRAN
执行(100)
您的 $update 和 $insert 会让您感到困惑(您正在执行多语句批处理,对吧?不是准备批次的中间)。如果您摆脱它们,并根据 $execute [无论您在批处理中准备的内容] 进行思考,它可能会帮助您更好地理解问题。
在上述所有工作均按预期进行之前,请勿下结论。
并阅读 BEGIN/COMMIT TRAN。
最后,“END TRAN
”到底是什么?我认为您发布的代码块不是真实的。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
不要动态创建SQL,这是危险的(sql注入)。
您应该能够准备多个插入/更新,并且您到 DBI 文档的链接并没有说您不能,它说某些驱动程序可能无法告诉您有关仅准备好的语句的更多信息。
我会将一个错误的失败示例发布到 dbi-users 列表以供评论,因为 DBD::Sybase 维护者在那里闲逛(请参阅 dbi.perl.org)。
Don't dynamically create SQL, it is dangerous (sql injection).
You should be able to prepare multiple inserts/updates and your link to the DBI documentation does not say you cannot, it says some drivers may not be able to tell you much about a statement which is ONLY prepared.
I'd post a failing example with error to the dbi-users list for comment as the DBD::Sybase maintainer hangs out there (see dbi.perl.org).