-1

我有一张comment桌子和一张comment_edit桌子,还有olddb_edit。简化后,相关表如下所示:

CREATE TABLE `olddb_edit` (
    edit_id INT NOT NULL,
    edit_time INT NOT NULL,
    edit_text TEXT NOT NULL,

    PRIMARY KEY (edit_id, edit_time)

) ENGINE=InnoDB;

现在我想将内容从另一个数据库迁移到编辑表中,但跳过一些表行,如测试评论。我正在为此使用 CakePHP(实际上是 Phinx)。

通常,这就足够了:

$skippable = array(
    12345, 23456, 34567, 45678,
);
$rows = $this->getQueryBuilder()
    ->select('*')
    ->from('olddb_comment')
    ->where(array(
        'comment_id NOT IN' => $skippable,
    ))
    ->execute()
    ->fetchAll('assoc')
;

但是一个简单的NOT IN子句显然不适用于复合主键。

我在想,$skippable数组应该是这样的:

$skippable = array(
    array('id' => 707969,   'time' => 1434462225),
    array('id' => 707969,   'time' => 1434462463),
    array('id' => 707969,   'time' => 1434462551),
);

然后我将通过 for 循环或其他方式运行 where 子句。但老实说,我什至不知道如何在 vanilla-MySQL 中做到这一点。

可能已经在 SO 上发布了一个解决方案,但我找不到任何解决方案(除了特定于其他应用程序的解决方案)。我猜这个算法不是我的朋友。

4

2 回答 2

0

没关系,我在提出问题时自己想通了。无论如何,我都会为其他有类似问题的人发布答案。


首先,香草-MySQL。NOT IN如果你分解(imo),它就像你想象的那样直观:

SELECT * FROM olddb_edit WHERE
NOT (
    (edit_id = 707969 AND edit_time = 1434461454)
OR  (edit_id = 707969 AND edit_time = 1434461503)
OR  (edit_id = 707969 AND edit_time = 1434461925)
);

通过 CakePHP / Phinx 查询构建器,您可以使用匿名函数、for 循环和非或构造:

$qb = $this->getQueryBuilder()
    ->select('*')
    ->from('olddb_edit')
    ->where(array(
        'edit_some_other_optional_condition = 1',
    ))

    // also skip skippables.
    ->where(function($exp) use ($skippable) {
        $ORed = array();
        foreach ($skippable as $edit) {
            array_push($ORed, array(
                'edit_id'   => $edit['id'],
                'edit_time' => $edit['time'],
            ));
        }
        return $exp->not($exp->or_($ORed));
    })
;

更新:根据@ndm 的评论,我使用 TupleComparison 提出了一个令人满意的解决方案。—(@ndm,如果您愿意,请随时发布您的答案。我将删除/编辑我的答案并选择您的答案。您值得称赞)。

// remove the keys from my previous solution.
$skippable = array(
    array(707969,   1434462225),
    array(707969,   1434462463),
    array(707969,   1434462551),
);

$qb = $this->getQueryBuilder()
    ->select('*')
    ->from('olddb_edit')
    ->where(array(
        'edit_some_other_optional_condition = 1',
    ))

    // also skip skippables.
    ->where(new Cake\Database\Expression\TupleComparison(
        array('edit_id', 'edit_time'),
        $skippable,
        array('integer', 'integer'),
        'NOT IN'
    ))
;
于 2020-05-16T19:04:35.477 回答
0

您的查询评估如下;你确定那是你想要的吗?

select edit_id 
     , edit_time
     , edit_text 
  from olddb_edit 
 where 
     (
       (
         (edit_id <> 707969) 
      or (edit_time <> 1434461454)
       ) 
   and (
         (edit_id <> 707969) 
      or (edit_time <> 1434461503)
       ) 
   and (
         (edit_id <> 707969) 
      or (edit_time <> 1434461925)
        )
      );
于 2020-05-16T19:20:20.293 回答