如何重写此 MySQL 查询,使其不会引发此错误:您无法在 FROM 子句中指定目标表 'crawlLog' 进行更新?

Posted

技术标签:

【中文标题】如何重写此 MySQL 查询,使其不会引发此错误:您无法在 FROM 子句中指定目标表 \'crawlLog\' 进行更新?【英文标题】:How do I rewrite this MySQL query so it doesn't throw this error: You can't specify target table 'crawlLog' for update in FROM clause?如何重写此 MySQL 查询,使其不会引发此错误:您无法在 FROM 子句中指定目标表 'crawlLog' 进行更新? 【发布时间】:2011-10-01 14:25:32 【问题描述】:

我正在尝试从公司表中获取一个 ID,但该 ID 尚未在 crawlLog 表中。然后我需要将该 companyId 插入到 crawlLog 表中。

我需要在一次调用中执行此操作,以便并行爬虫在其他爬虫选择了一个 url 但尚未将其插入爬虫日志后不会拉取相同的 url。我不想因为产生其他问题而锁定表。

我从以下两个查询中都得到了这个错误:

You can't specify target table 'crawlLog' for update in FROM clause

这是我尝试做同样事情的两个查询。

INSERT INTO crawlLog (companyId, timeStartCrawling)
VALUES
(
    (
        SELECT companies.id FROM companies
        LEFT OUTER JOIN crawlLog
        ON companies.id = crawlLog.companyId
        WHERE crawlLog.companyId IS NULL
        LIMIT 1
    ),
    now()
)

我也试过这个,但得到同样的错误:

INSERT INTO crawlLog (companyId, timeStartCrawling)
VALUES
(
    (
        SELECT id
        FROM companies
        WHERE id NOT IN
        (
            SELECT companyId
            FROM crawlLog
        )
        LIMIT 1
    ),
    now()
)

【问题讨论】:

这只能通过事务和一些锁定可靠地完成。如果你在 InnoDB 上,至少它只是一个行级锁而不是整个表锁。 您是否尝试为内部 crawlLog 设置别名? @Tocco - 你就是男人!!!做到了。这是一个绝妙的技巧。 嘿 .. 将此标记为精彩评论! 【参考方案1】:

为什么要使用子选择? INSERT INTO ... SELECT 存在:

INSERT INTO crawlLog (companyId, timeStartCrawling)
SELECT companies.id, NOW()
FROM companies
LEFT OUTER JOIN crawlLog
ON companies.id = crawlLog.companyId
WHERE crawlLog.companyId IS NULL
LIMIT 1

这样它就不应该抱怨在 INSERT 和 SELECT 部分都使用表

【讨论】:

【参考方案2】:

You can't update rows which you are querying。有一种方法可以强制 mysql 隐式使用临时表:

INSERT INTO crawlLog (companyId, timeStartCrawling)
VALUES
(
    SELECT id, when FROM
    (
    SELECT companies.id AS id, now() AS when FROM companies
    LEFT OUTER JOIN crawlLog
    ON companies.id = crawlLog.companyId
    WHERE crawlLog.companyId IS NULL
    LIMIT 1
    )
)

【讨论】:

【参考方案3】:

这很有效,似乎是最简单的解决方案:

使用我的问题中两个语句中较简单的一个,我按照 @Tocco 在 cmets 中的建议为内部 crawlLog 表创建了一个别名,然后在 VALUES() 中删除了必要的封装。

INSERT INTO crawlLog (companyId, timeStartCrawling)
SELECT id, now()
FROM companies
WHERE id NOT IN
(
    SELECT companyId
    FROM crawlLog AS crawlLogAlias
)
LIMIT 1

【讨论】:

不确定是否有问题,但在 VALUES 中放置 SELECT 似乎很奇怪(不必要)。【参考方案4】:

在临时表中进行选择,然后从临时表中插入选择。你不能在同一个语句中插入一个表并从中选择,所以使用一个临时表和两个语句。

【讨论】:

以上是关于如何重写此 MySQL 查询,使其不会引发此错误:您无法在 FROM 子句中指定目标表 'crawlLog' 进行更新?的主要内容,如果未能解决你的问题,请参考以下文章

如何重写这个嵌套的 SQL 查询以使其更快? [关闭]

如何在界面上隐藏 mySQL 错误? [关闭]

在此查询中,我需要添加啥使其永远不会显示空白或空值?

优化 SQL:如何重写此查询以提高性能? (使用子查询,摆脱 GROUP BY?)

网络表单中的此插入查询引发错误,我不知道为啥

为啥 Prometheus 不会在不可见的指标上引发错误?