自联接的困难 MySQL 更新查询

Posted

技术标签:

【中文标题】自联接的困难 MySQL 更新查询【英文标题】:Difficult MySQL Update Query with Self-Join 【发布时间】:2011-09-16 20:27:46 【问题描述】:

我们的网站有列表。我们使用具有以下结构的连接表将我们的成员与这些不同的列表联系起来:

CREATE TABLE `connections` (
  `cid1` int(9) unsigned NOT NULL DEFAULT '0',
  `cid2` int(9) unsigned NOT NULL DEFAULT '0',
  `type` char(2) NOT NULL,
  `created` datetime DEFAULT NULL,
  `updated` datetime DEFAULT NULL,
  PRIMARY KEY (`cid1`,`cid2`,`type`,`cid3`),
  KEY `cid1` (`cid1`,`type`),
  KEY `cid2` (`cid2`,`type`)
);

我们遇到的问题是,当我们必须不时合并重复的列表时,我们还需要更新我们的成员连接,并且一直使用以下查询,如果成员连接到两个列表,则会中断:

update connections set cid2=100000
where type IN ('MC','MT','MW') AND cid2=100001;

我不知道如何执行以下操作来解决此问题:

update connections set cid2=100000
where type IN ('MC','MT','MW') AND cid2=100001 AND cid1 NOT IN (
    select cid1 from connections
    where type IN ('MC','MT','MW') AND cid2=100000
);

当我尝试运行该查询时,我收到以下错误:

ERROR 1093 (HY000): You can't specify target table 'connections' for update in FROM clause

这是一些示例数据。注意 cid1 = 10025925 的更新冲突

+----------+--------+------+---------------------+---------------------+
| cid1     | cid2   | type | created             | updated             |
+----------+--------+------+---------------------+---------------------+
| 10010388 | 100000 | MC   | 2010-08-05 18:04:51 | 2011-06-16 16:26:17 |
| 10025925 | 100000 | MC   | 2010-10-31 09:21:25 | 2010-10-31 16:21:25 |
| 10027662 | 100000 | MC   | 2011-06-13 16:31:12 | NULL                |
| 10038375 | 100000 | MW   | 2011-02-05 05:32:35 | 2011-02-05 19:51:58 |
| 10065771 | 100000 | MW   | 2011-04-24 17:06:35 | NULL                |
| 10025925 | 100001 | MC   | 2010-10-31 09:21:45 | 2010-10-31 16:21:45 |
| 10034884 | 100001 | MC   | 2011-01-20 18:54:51 | NULL                |
| 10038375 | 100001 | MC   | 2011-02-04 05:00:35 | NULL                |
| 10041989 | 100001 | MC   | 2011-02-26 09:33:18 | NULL                |
| 10038259 | 100001 | MC   | 2011-05-07 13:34:20 | NULL                |
| 10027662 | 100001 | MC   | 2011-06-13 16:33:54 | NULL                |
| 10030855 | 100001 | MT   | 2010-12-31 20:40:18 | NULL                |
| 10038375 | 100001 | MT   | 2011-02-04 05:00:36 | NULL                |
+----------+--------+------+---------------------+---------------------+

我希望有人可以建议运行上述查询的正确方法。提前致谢!

【问题讨论】:

【参考方案1】:

您的查询出错的原因是因为在 mysql 中您无法从您尝试在同一查询中更新的表中进行 SELECT。

使用 UPDATE IGNORE 避免重复冲突。

我认为您应该尝试阅读 INSERT ON DUPLICATE KEY。这个想法是您构建一个始终会创建 DUPLICATE 冲突的 INSERT 查询,然后 UPDATE 部分将发挥作用。

【讨论】:

有时最好的解决方案是简单的解决方案。我在更新语句中添加了忽略,然后用旧的 cid2 删除了所有剩余的行。非常感谢您的帮助!【参考方案2】:

一种可能的方法是为您的子查询使用临时表,然后从临时表中进行选择。但是,如果您需要执行大量此类查询,效率可能会很快下降。

create temporary table subq as select cid1 from connections where type IN ('MC','MT','MW') AND cid2=100000

update connections set cid2=100000 where type IN ('MC','MT','MW') AND cid2=100001 AND cid1 NOT IN (select cid1 from subq);

【讨论】:

【参考方案3】:
UPDATE connections cn1
LEFT JOIN connections cn2 ON cn1.cid1 != cn2.cid1
    AND cn2.type IN ('MC','MT','MW')
    AND cn2.cid2=100000
SET cn1.cid2=100000
WHERE cn1.TYPE IN ('MC','MT','MW') 
    AND cn1.cid2=100001 
    AND cn2.cid1 IS NULL -- i.e. there is no matching record

【讨论】:

我知道这已经很老了,但是这里有人知道为什么左外连接在这里更好吗?【参考方案4】:

我正在考虑类似以下的问题,但我不能 100% 确定您的数据在确定准确性之前和之后的样子。这个想法是在子查询的 where 子句中将表与自身连接起来,并且排除 cid1 不能匹配的位置。

update connections c1 left outer join connections c2
 on (c2.cid2 = 100000 and c2.type in ('MC','MT','MW') and c1.cid1 != c2.cid1)
 set c1.cid2 = 100000
 where c1.type in ('MC', 'MT', 'MW') and c1.cid2=100001 and c2.cid1 is null;

据我所知,它会起作用的。我使用了您的create table(减去主键中的cid3)并确保我有2行具有相同的cid1和不同的cid2(一个为100000,另一个为100001)并且该语句仅影响1 行。

【讨论】:

谢谢,但不幸的是它没有起到作用。我正在尝试遵循似乎正确但不确定问题所在的逻辑。由于某种原因,它似乎与正确的行不匹配(请参阅上面新添加的数据)。

以上是关于自联接的困难 MySQL 更新查询的主要内容,如果未能解决你的问题,请参考以下文章

使用自联接进行更新

进行递归自联接的最简单方法?

如何改进包含存储过程使用的多个自联接的视图

SQLAlchemy:在 MySQL 上使用自联接创建删除查询

MySQL 入门:查询和更新的内部实现

Mysql 自联接性能