0

这是一个非常好的 IMO,我还没有在 SO 或 Google 上看到过类似的例子,所以你去吧。我需要在我正在构建的 Perl 应用程序中执行以下操作。不幸的是,它不能直接在 MySQL 中完成,需要 DBI。简而言之,我需要使用 Database1.tableA 并找到与列“状态”匹配“开始”的每条记录。我可以这样做,因为它相当容易(DBI 还不是很好,但已经阅读了文档),但是我遇到问题的地方是我接下来要做的。

my $started_query = "SELECT primary_ip FROM queue WHERE status='started'";
        my $started = $dbh->prepare($started_query);
        $started->execute();

        while ( my @started = $started->fetchrow_array() ) {
     # Where I am hoping to have the following occur so it can go by row
     # for only rows with the status 'started'
}

所以对于@started数组中的每条记录,每次while循环的迭代实际上只包含一个值,我需要看看它是否存在于Database2.tableA中,如果它确实存在于我需要的另一个数据库(Database2.tableA)中从 Database1.tableA 中删除它,但如果它在另一个数据库 (Database2.tableA) 中不存在,我需要更新当前数据库 (Database1.tableA) 中的记录。

基本上复制了以下半有效的 MySQL 语法。

DELETE FROM tableA WHERE primary_ip IN (SELECT primary_ip FROM db2.tablea) OR UPDATE tableA SET status = 'error'

我仅限于 DBI 连接到两个数据库,目前逻辑正在逃避我。我可以对两个数据库进行查询并存储在@arrays 中,然后进行比较,但这似乎是多余的,因为我认为这应该是可能的,while ( my @started = $started->fetchrow_array() )因为这将节省运行时和所需的资源。我对在 DBI 实例之间传递变量也不够熟悉,因为 @started 数组将始终包含我需要查询和删除的列值,我想充分利用已定义并传递给 DBI 对象的优势。

我将整晚都在做这件事,并且已经喝了几壶咖啡,所以非常感谢你帮助我理解这个逻辑。

4

1 回答 1

1

使用 fetchrow_hashref 会更好,它返回键/值对的 hashref,其中键是列名,而不是基于显示在数组中序号位置的列进行编码。

您需要一个额外的数据库句柄来进行查找和更新,因为您在第一个数据库句柄上有一个实时语句句柄。像这样的东西:

my $dbh2 = DBI->connect(...same credentials...);

...

while(my $row = $started->fetchrow_hashref)
{
    if(my $found = $dbh2->selectrow_hashref("SELECT * FROM db2.t2 WHERE primary_ip = ?",undef,$row->{primary_ip}))
    {
        $dbh2->do("DELETE FROM db1.t1 WHERE primary_ip = ?",undef,$found->{primary_ip});
    }
    else
    {
        $dbh2->do("UPDATE db1.t1 SET status = 'error' WHERE primary_ip = ?",undef,$found->{primary_ip}");
    }

}

从技术上讲,我“不需要”从 db2.t2 中获取行,my $found因为您只是在测试是否存在,还有其他方法,但是在这里使用它可以防止做一些您不打算做的事情,因为如果我们以某种方式得到一些错误的逻辑,那将是 undef,这应该可以防止我们做出一些潜在的错误更改。

但是使用循环迭代来接近关系数据库很少是最好的策略。

这个“可以”直接在 MySQL 中完成,只需几个查询。

首先是更新,其中 t1.status = 'started' 并且 t2.primary_ip 没有与 t1.primary_ip 匹配的值:

UPDATE db1.t1 a LEFT JOIN db2.t2 b ON b.primary_ip = a.primary_ip
   SET a.status = 'error' 
 WHERE b.primary_ip IS NULL AND a.status = 'started';

如果您正在考虑“但 b.primary_ip 永远不会为空”......好吧,它在没有匹配行的左连接中为空。

然后从 t1 中删除行也可以通过连接来完成。多表连接仅删除DELETE和之间列出的表别名中的行FROM。同样,我们用别名“a”调用“t1”,用别名“b”调用 t2。

DELETE a
  FROM db1.t1 a JOIN db2.t2 b ON a.primary_ip = b.primary_ip
 WHERE a.status = 'started'; 

这会从 t1 ("a") 中删除每一行,其中 status = 'started' 并且在 t2 中存在匹配行。

于 2013-10-29T03:33:37.497 回答