MySQL:如何查询复合主键?具体来说,一个 NOT IN 查询?
Posted
技术标签:
【中文标题】MySQL:如何查询复合主键?具体来说,一个 NOT IN 查询?【英文标题】:MySQL: How do you query on a compound-primary-key? Specifically, a NOT IN query? 【发布时间】:2020-05-16 19:04:35 【问题描述】:我有一个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;
现在我想将内容从另一个数据库迁移到编辑表中,但跳过一些表行,如 test-cmets。我正在为此使用 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 上发布了解决方案,但我找不到任何解决方案(除了特定于其他应用程序的解决方案)。我猜这个算法不是我的朋友。
【问题讨论】:
请询问 1 个经过研究且不重复的具体问题,了解您遇到的问题。对于代码问题,请给出minimal reproducible example--cut & paste & runnable code,包括最小的代表性示例输入作为代码;期望和实际输出(包括逐字错误消息);标签和版本;明确的规范和解释。对于 SQL、DBMS 和 DDL(包括约束和索引)和输入格式为表格的代码。查询不需要 PS Keys。连接是基于条件的,而不是键。子查询的 IN 需要一行,而不是一个键。是否是多列是特定于 DBMS 的。通常 EXISTS 是更好的选择。 【参考方案1】:您的查询评估如下;你确定这是你想要的吗?
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)
)
);
【讨论】:
这是一种奇怪的看待方式,但本质上是:是的。这就是我想要的。那些表行同时是每个单独的可跳过行的否定。毕竟,NOT IN
就是这样。【参考方案2】:
没关系,我在提出问题时自己想通了。无论如何,我会为其他有类似问题的人发布答案。
首先,香草-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'
))
;
【讨论】:
在 SQL 中,不展开NOT IN
,元组也可以工作。喜欢:(edit_id, edit_time) NOT IN ((707969, 1434461454), (707969, 1434461503), ...)
@stickybit 我觉得OR NOT AND
非常混乱,所以我可能弄错了,但我认为你的查询在逻辑上会有所不同(而且很可能是OP 预期)。
@Strawberry:嗯,我不知道...首先请注意,我在这个答案中引用了 SQL,而不是任何来自问题的东西,也不是任何 PHP 的东西,如果不清楚的话。然后这是我的推理:NOT
之后括号中的所有内容都可以表示为带有元组的IN
。 AND
s 被使用元组覆盖,然后它是一堆OR
s,具有相同的左操作数,可以表示为IN
。所以我们得到NOT (... IN ...)
。这可以简化为NOT IN ...
。但当然我可能在某个地方错了。也许你可以构建一个反例?
我不知道NOT IN
支持元组,或者这是一件事(在INSERT
语句之外)。我无法(并且仍然无法)在 dev.mysql.com 上找到任何关于此的官方文档……我将对此进行调查,看看是否有 CakePHP 的东西。
有一个用于元组比较的表达式:***.com/questions/37919141/…以上是关于MySQL:如何查询复合主键?具体来说,一个 NOT IN 查询?的主要内容,如果未能解决你的问题,请参考以下文章