MySQL INSERT 在同一张表上使用带有 COUNT() 的子查询

Posted

技术标签:

【中文标题】MySQL INSERT 在同一张表上使用带有 COUNT() 的子查询【英文标题】:MySQL INSERT Using Subquery with COUNT() on the Same Table 【发布时间】:2011-09-03 01:43:25 【问题描述】:

我无法正确执行 INSERT 查询,而且我似乎无法在 Google 或 Stack Overflow 上找到任何解决此特定问题的内容。

我正在尝试为精选条目创建一个简单的表格,其中entry_id 与其当前订单一起保存到表格中。

我想要的输出是这样的:

如果featured 表当前有这三个条目:

featured_id    entry_id    featured_order
1              27          0
2              54          1
4              23          2

我想用featured_order=3 保存下一个条目。

我正在尝试使以下查询无法正常工作:

INSERT INTO `featured`
(
    `entry_id`, `featured_order`
)
VALUES
(
    200,
    (SELECT COUNT(*) AS `the_count` FROM `featured`)
)

我得到的错误是:You can't specify target table 'featured' for update in FROM clause

任何人都可以提供一个在不导致错误的情况下获取计数的解决方案吗?

提前致谢!

【问题讨论】:

可能重复***.com/questions/45494/… 不是重复的——那个问题是要删除的;这是为了插入。在这里根本不适用。 【参考方案1】:
INSERT INTO `featured`
(
    `entry_id`, `featured_order`
)
VALUES
(
    200,
    (SELECT COUNT(*) AS `the_count` FROM `featured` F1)
)

更正只是添加“F1”表别名。

这个标准的 sql 解决方案适用于各种 dbms(不仅是 mysql


我还建议对以下方面进行改进:

SELECT COUNT(*) +1(问题:如果某些行被删除,您可能会与现有索引发生冲突)

SELECT MAX(featured_order)+1(问题:空表的第一个插入出错)

SELECT (COALESCE(MAX(featured_order), 0)+1)(没问题)

【讨论】:

我只是想知道...如果同时有两个插入,这会正常工作吗? 我认为你是对的,“第二个”应该失败。【参考方案2】:

这是一件很酷的事情:MySQL 的INSERT . . . SELECT

INSERT INTO `featured`
(
    `entry_id`, `featured_order`
)
SELECT 200, COUNT(*) + 1
FROM `featured`

不需要子查询。


@Bohemian 有一个很好的观点:

如果你使用这种方法,最好使用 max(featured_order) + 1

所以更好的查询可能是:

INSERT INTO `featured`
(
    `entry_id`, `featured_order`
)
SELECT 200, MAX(`featured_order`) + 1
FROM `featured`

他在回答中描述的触发方法也是实现您想要的好方法。


查询 1 的潜在问题是,如果您删除了一行,排名将被取消,并且您将在 featured_order 中有一个重复项。对于第二个查询,这不是问题,但您会有间隙,就像您使用自动增量列一样。

如果您绝对必须有一个没有间隙的订单,我所知道的最佳解决方案是运行这一系列查询:

SET @pos:=0;

DROP TABLE IF EXISTS temp1;

CREATE TEMPORARY TABLE temp1 LIKE featured;

ALTER TABLE featured ORDER BY featured_order ASC;

INSERT INTO temp1 (featured_id, entry_id, featured_order) 
SELECT featured_id, entry_id, @pos:=@pos+1 FROM words;

UPDATE featured 
JOIN temp1 ON featured.featured_id = temp1.featured_id 
SET featured.rank = temp1.rank;

DROP TABLE temp1;

每当你删除一行时

【讨论】:

我认为这行不通。来自INSERT ... SELECT 的文档:“但是,您不能在子查询中插入表并从同一个表中进行选择。” 这个问题:a) 你想在 count() 中加一个,b) 如果编号与 count() 不一致怎么办 - 例如,如果行被删除。如果您使用这种方法,最好使用 max(featured_order) + 1 @JIStone - 我承认我没有尝试过。文档(这是 5.5 的)看起来很清楚。但是,这可能是错误的。你用的是什么版本的 MySQL? 本示例中的 SELECT 语句不被视为子查询。 这很奇怪,因为关于子查询的警告在关于 INSERT...SELECT 的手册页上(以及关于子查询语法的页面上)。【参考方案3】:

你必须简单地使用别名来解决问题:

INSERT INTO `featured`
(
    `entry_id`, `featured_order`
)
VALUES
(
    200,
    (SELECT COUNT(*) AS `the_count` FROM `featured` as f1)
)

【讨论】:

如果在eith -vote中提供解释会更好,这会有所帮助【参考方案4】:

使用触发器:

drop trigger if exists featured_insert_trigger; 

delimiter //
create trigger featured_insert_trigger before insert on featured
for each row
begin
  set new.featured_order = ifnull((select max(featured_order) from featured), -1) + 1;
end; //
delimiter ;

现在您的插入看起来像这样:

insert into featured (entry_id) values (200);

featured_order 将设置为 features_order 的最高值加一。这迎合了被删除/更新的行并始终保证唯一性。

ifnull 存在于表中没有行的情况下,在这种情况下第一个值将为零。

此代码已经过测试,可以正常工作。

【讨论】:

我对触发器做了一些研究(我以前从未听说过它们),这听起来绝对是最好的解决方案。然而,当我尝试它时,我得到了这个错误:TRIGGER command denied to user 'xxxx'@'xxxx' for table 'featured' 根据 HostGator,我已经给 MySQL 用户“所有权限”,所以看起来这个项目的卡片中没有触发器。 :// 如果同时有两个插入,这是否有效? @xms 从来没有任何更新/插入发生在“完全相同的时间”。数据库序列化所有变异查询。每个总是以原子方式开始和完成。【参考方案5】:

来自MySQL manual关于子查询:

另一个限制是目前您不能修改表并在子查询中从同一个表中进行选择。

也许子查询中的别名或连接(否则无用)在这里会有所帮助。

编辑:事实证明有一个解决方法。解决方法描述为http://www.xaprb.com/blog/2006/06/23/how-to-select-from-an-update-target-in-mysql/。

【讨论】:

那个“变通”涉及到随后的update 声明——几乎没有“优雅”。 我希望我没有暗示“优雅”:)

以上是关于MySQL INSERT 在同一张表上使用带有 COUNT() 的子查询的主要内容,如果未能解决你的问题,请参考以下文章

MYSQL UPDATE 表上 INSERT 到同一个表

mysql通过在同一张表上查询的自定义顺序

MySQL 在同一张表上有 2 个 LEFT JOIN

Mysql在同一张表上的多个左连接

使用多线程代码在一张表上发生 MySQL 死锁

更新时在同一张表上执行触发器