如何更新循环中 SELECT 返回的行?

发布于 2024-08-03 00:26:07 字数 714 浏览 10 评论 0原文

我对 SQL SELECT 语句返回的行有一个循环,并且在对行数据进行一些处理后,有时想要更新该行的值。循环体中的处理并不简单,我无法用 SQL 编写它。当我尝试对所选行执行 UPDATE 时,出现错误(在 Perl 的 DBD::SQLite::st 执行失败:数据库表被锁定下)。有没有一种可读、高效且可移植的方式来实现我想要做的事情?如果做不到这一点,是否有 DBD 或 SQLite 特定的方法可以做到这一点?

显然,我可以将更新推送到单独的数据结构中并在循环后执行它们,但我讨厌代码的处理方式。

如果您有兴趣,这里是相应的 Perl 代码。

my $q = $dbh->prepare(q{
  SELECT id, confLoc FROM Confs WHERE confLocId ISNULL});
$q->execute or die;
my $u = $dbh->prepare(q{
  UPDATE Confs SET confLocId = ? WHERE id = ?});
while (my $r = $q->fetchrow_hashref) {
    next unless ($r->{confLoc} =~ m/something-hairy/);
    next unless ($locId = unique_name_state($1, $2));
    $u->execute($locId, $r->{id}) or die;
}

I have a loop on the rows returned by an SQL SELECT statement, and, after some processing on a row's data, I sometimes want to UPDATE the row's value. The processing in the loop's body is non-trivial, and I can't write it in SQL. When I try to execute the UPDATE for the selected row I get an error (under Perl's DBD::SQLite::st execute failed: database table is locked). Is there a readable, efficient, and portable way to achieve what I'm trying to do? Failing that, is there a DBD or SQLite-specific way to do it?

Obviously, I can push the updates in separate data structure and execute them after the loop, but I'd hate the code's look after that.

If you're interested, here is the corresponding Perl code.

my $q = $dbh->prepare(q{
  SELECT id, confLoc FROM Confs WHERE confLocId ISNULL});
$q->execute or die;
my $u = $dbh->prepare(q{
  UPDATE Confs SET confLocId = ? WHERE id = ?});
while (my $r = $q->fetchrow_hashref) {
    next unless ($r->{confLoc} =~ m/something-hairy/);
    next unless ($locId = unique_name_state($1, $2));
    $u->execute($locId, $r->{id}) or die;
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

不离久伴 2024-08-10 00:26:07

暂时启用 AutoCommit

sqlite> .header on
sqlite> select * from test;
field
one
two
#!/usr/bin/perl

use strict;
use warnings;

use DBI;

my $dbh = DBI->connect('dbi:SQLite:test.db', undef, undef,
    { RaiseError => 1, AutoCommit => 0}
);

test_select_with_update($dbh);

sub test_select_with_update {
    my ($dbh) = @_;
    local $dbh->{AutoCommit} = 1;
    my $q = $dbh->prepare(q{SELECT field FROM test});
    my $u = $dbh->prepare(q{UPDATE test SET field = ? WHERE field = ?});
    $q->execute or die;
    while ( my $r = $q->fetchrow_hashref ) {
        if ( (my $f = $r->{field}) eq 'one') {
            $u->execute('1', $f) or die;
        }
    }
}

代码运行后:

sqlite> .header on
sqlite> select * from test;
field
1
two

Temporarily enable AutoCommit:

sqlite> .header on
sqlite> select * from test;
field
one
two
#!/usr/bin/perl

use strict;
use warnings;

use DBI;

my $dbh = DBI->connect('dbi:SQLite:test.db', undef, undef,
    { RaiseError => 1, AutoCommit => 0}
);

test_select_with_update($dbh);

sub test_select_with_update {
    my ($dbh) = @_;
    local $dbh->{AutoCommit} = 1;
    my $q = $dbh->prepare(q{SELECT field FROM test});
    my $u = $dbh->prepare(q{UPDATE test SET field = ? WHERE field = ?});
    $q->execute or die;
    while ( my $r = $q->fetchrow_hashref ) {
        if ( (my $f = $r->{field}) eq 'one') {
            $u->execute('1', $f) or die;
        }
    }
}

After the code has been run:

sqlite> .header on
sqlite> select * from test;
field
1
two
雪花飘飘的天空 2024-08-10 00:26:07

您的问题是,当您处于获取循环时,您使用相同的数据库处理程序来执行更新。

因此,让数据库处理程序的另一个实例来执行更新:

my $dbh = DBI->connect(...);
my $dbhForUpdate = DBI->connect(...) ;

然后在循环中使用 dbhForUpdate:

while(my $row = $sth->fetch()){
   ...
   $dbhForUpdate->do(...) ;
}

无论如何,我不建议这样做,因为您很有可能在数据库级别遇到并发问题。

Your problem is that you're using the same database handler to perform an update while you're in a fetching loop.

So have another instance of your database handler to perform the updates:

my $dbh = DBI->connect(...);
my $dbhForUpdate = DBI->connect(...) ;

Then use dbhForUpdate in your loop:

while(my $row = $sth->fetch()){
   ...
   $dbhForUpdate->do(...) ;
}

Anyway, I wouldn't recommend doing this since there's good chances you run into concurrency issues at the database level.

九八野马 2024-08-10 00:26:07

更多内容是对 Zoidberg 的评论的回答,但是如果您能够切换到像 Perl 那样的 ORM DBIx::Class 然后你发现你可以写这样的东西:

my $rs = $schema->resultset('Confs')->search({ confLocId => undef });

while ( my $data = $rs->next ) {
    next unless $data->confLoc =~ m/(something)-(hairy)/;
    if ( my $locId = unique_name_state( $1, $2 ) ) {
        $data->update({ confLocID => $locid });
    }
}

如果 DBIx::Class 没有吸引你的兴趣,那么 CPAN 就像Fey:例如 :ORMRose::DB

More in answer to Zoidberg's comment but if your were able to switch to an ORM like Perl's DBIx::Class then you find that you could write something like this:

my $rs = $schema->resultset('Confs')->search({ confLocId => undef });

while ( my $data = $rs->next ) {
    next unless $data->confLoc =~ m/(something)-(hairy)/;
    if ( my $locId = unique_name_state( $1, $2 ) ) {
        $data->update({ confLocID => $locid });
    }
}

And if DBIx::Class doesn't grab your fancy there are a few others on CPAN like Fey::ORM and Rose::DB for example.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文