【问题标题】:Insert and set value with max()+1 problems使用 max()+1 问题插入和设置值
【发布时间】:2011-07-18 14:56:45
【问题描述】:

我正在尝试插入一个新行并将 customer_id 设置为 max()+1。原因是该表已经在另一个名为 id 的列上有一个 auto_increatment,并且该表将有多个具有相同 customer_id 的行。

有了这个:

INSERT INTO customers
  ( customer_id, firstname, surname )
VALUES 
  ((SELECT MAX( customer_id ) FROM customers) +1, 'jim', 'sock')

...我不断收到以下错误:

#1093 - You can't specify target table 'customers' for update in FROM clause

另外,我将如何阻止同时添加 2 个不同的客户并且没有相同的 customer_id?

【问题讨论】:

    标签: mysql sql insert mysql-error-1093


    【解决方案1】:

    您的子查询不完整,仅此而已。请参阅下面的查询以及我的补充:

    INSERT INTO customers ( customer_id, firstname, surname ) 
    VALUES ((SELECT MAX( customer_id ) FROM customers) +1), 'jim', 'sock')
    

    【讨论】:

      【解决方案2】:

      这是选择来插入续集。

      我正在尝试获取 serial_no 最大 +1 值并给出正确的值。

      SELECT MAX(serial_no)+1 into @var FROM sample.kettle;
      Insert into kettle(serial_no,name,age,salary) values (@var,'aaa',23,2000);
      

      【讨论】:

        【解决方案3】:

        关于我的答案都不适用于我的情况。我从here得到了答案,我的SQL是:

        INSERT INTO product (id, catalog_id, status_id, name, measure_unit_id, description, create_time)
        VALUES (
          (SELECT id FROM (SELECT COALESCE(MAX(id),0)+1 AS id FROM product) AS temp),
          (SELECT id FROM product_catalog WHERE name="AppSys1"),
          (SELECT id FROM product_status WHERE name ="active"),
          "prod_name_x",
          (SELECT id FROM measure_unit WHERE name ="unit"),
          "prod_description_y",
          UNIX_TIMESTAMP(NOW())
        )
        

        【讨论】:

          【解决方案4】:

          可以使用INSERT ... SELECT语句获取MAX()+1值,同时插入:

          INSERT INTO 
          customers( customer_id, firstname, surname )
          SELECT MAX( customer_id ) + 1, 'jim', 'sock' FROM customers;
          

          注意:您需要从INSERT 中删除VALUES,并确保SELECT 所选字段与INSERT 声明的字段匹配。

          【讨论】:

          • 如果这些查询完全同时运行,可能会导致竞争条件。在此之前你必须lock table
          • @xmedeko 我认为 1 个查询总是原子的,所以既然这里只有 1 个 INSERT,那么它是原子操作并且是竞争安全的?
          • 不,至少 mysql 没有。此查询确实选择然后插入。它们不是原子的。出现竞争状况的可能性很小。
          • 今天我学到了一些新东西。我认为在一个查询中执行所有操作时,重复查询是不可能的,但是看,我错了。是时候重写一些东西以避免将来出现潜在问题。
          • 我有一个类似的场景,我可以打开一个单独的 SO 问题;选择 max() 也是我一直在寻找的方法,但是在阅读了上面的 cmets 之后,我在质疑自己。在我的例子中,我有一个 WHERE 子句,它将我返回的结果限制为一个特定的子集,所以我 THINK 我对竞争条件相对安全......对吗?
          【解决方案5】:

          我们声明一个变量'a'

          SET **@a** = (SELECT MAX( customer_id ) FROM customers) +1;
          
          INSERT INTO customers
            ( customer_id, firstname, surname )
          VALUES 
            (**@a**, 'jim', 'sock')
          

          【讨论】:

          • 您可能应该在代码中添加更多注释。这是否回答了所提出的两个问题?为什么?
          【解决方案6】:

          像这样为内部查询使用别名

          INSERT INTO customers
            ( customer_id, firstname, surname )
          VALUES 
            ((SELECT MAX( customer_id )+1 FROM customers cust), 'sharath', 'rock')
          

          【讨论】:

          • @xmedeko 这根本不是重复的。 EmCos 的答案演示了用 SELECT 替换 VALUES(),而这个答案演示了如何在 VALUES() 中嵌套 SELECT 语句(阅读 EmCos 答案的人可能会认为这是不可能的,因为他的“注释”)
          • @Wipqozn 你是对的,它不是重复的,但应该在答案中提到有什么不同。另外,我关于比赛条件的 cmets 也适用于这个答案。
          【解决方案7】:

          在子查询中使用表别名:

          INSERT INTO customers
            ( customer_id, firstname, surname )
          VALUES 
            ((SELECT MAX( customer_id ) FROM customers C) +1, 'jim', 'sock')
          

          【讨论】:

          • 我认为这将是:INSERT INTO customers ( customer_id, firstname, surname ) VALUES ((SELECT MAX( C.customer_id ) FROM customers C) +1, 'jim', 'sock') 但以下适用于我的安装 [mySQL 版本 5.5.41]:INSERT INTO table1(id1) SELECT (max(id1)+1) FROM table1;
          【解决方案8】:
          insert into table1(id1) select (max(id1)+1) from table1;
          

          【讨论】:

          • 不适用于 MySQL,它不允许在 INSERT 和 SELECT 子查询中使用同一张表。
          • 为我工作的 mySQL 版本 5.5.41
          【解决方案9】:

          SELECT MAX(col) +1 不安全——它不能确保您不会插入多个具有相同 customer_id 值的客户,无论是从同一张表还是从其他任何表中选择。 proper way to ensure a unique integer value is assigned on insertion into your table in MySQL is to use AUTO_INCREMENT。 ANSI 标准是使用序列,但 MySQL 不支持它们。 AUTO_INCREMENT 列只能在 CREATE TABLE 语句中定义:

          CREATE TABLE `customers` (
            `customer_id` int(11) NOT NULL AUTO_INCREMENT,
            `firstname` varchar(45) DEFAULT NULL,
            `surname` varchar(45) DEFAULT NULL,
            PRIMARY KEY (`customer_id`)
          )
          

          也就是说,这对我来说在 5.1.49 上运行良好:

          CREATE TABLE `customers` (
            `customer_id` int(11) NOT NULL DEFAULT '0',
            `firstname` varchar(45) DEFAULT NULL,
            `surname` varchar(45) DEFAULT NULL,
            PRIMARY KEY (`customer_id`)
          ) ENGINE=InnoDB DEFAULT CHARSET=latin1$$
          
          INSERT INTO customers VALUES (1, 'a', 'b');
          
          INSERT INTO customers 
          SELECT MAX(customer_id) + 1, 'jim', 'sock'
            FROM CUSTOMERS;
          

          【讨论】:

          • 这如何不能确保您选择的不超过一行?除非有group by,否则我想不出带有max() 的查询会返回多行的情况
          • 他不是在谈论使用 max() 检索多于一行,他正确地指出,通过使用 max(customer_id) + 1 插入新客户,您无法确定这一点id 的唯一性。同时执行两个这样的语句,会导致两个不同的客户拥有相同的 customer_id。
          【解决方案10】:

          正确,您不能在同一个查询中从同一个表中修改和选择。您必须在两个单独的查询中执行上述操作。

          最好的方法是使用事务,但如果您不使用 innodb 表,那么下一个最好的方法是 locking the tables,然后执行您的查询。所以:

          Lock tables customers write;
          
          $max = SELECT MAX( customer_id ) FROM customers;
          

          获取最大id,然后执行插入

          INSERT INTO customers( customer_id, firstname, surname )
          VALUES ($max+1 , 'jim', 'sock')
          
          unlock tables;
          

          【讨论】:

          • @RDL - 我正在使用 MyISAM,所以您的示例将正常工作并允许同时添加 2 个具有不同客户 ID 的客户?谢谢
          • @Cozzy,正确。当表被锁定时,只有当前会话可以访问它。对该表的所有其他请求都放在一个队列中,直到该表被解锁。因此,同时添加的记录不会发生重复。
          • 如果 customer_id 列上没有唯一键进行验证,则无法保证重复值可以存在。此外,由于隔离级别处理,可以为插入语句读取相同的最大值。为什么不使用原生 AUTO_INCREMENT?
          • @RDL - 我从第一个查询 #1064 中收到以下错误 - 您的 SQL 语法有错误;检查与您的 MySQL 服务器版本相对应的手册,以在第 1 行的“@max = SELECT MAX(customer_id) FROM customers”附近使用正确的语法
          • @Cozzy:你声明了@max 变量吗?为什么你想要这种方法令人困惑。
          【解决方案11】:

          您无法在单个查询中执行此操作,但您可以在事务中执行此操作。执行初始 MAX() 选择并锁定表,然后执行插入。事务确保没有任何东西会中断这两个查询,而锁确保没有其他东西可以同时尝试在其他地方做同样的事情。

          【讨论】:

          • 能否提供一个示例 sql 查询?使用事务和锁定时,如果同时添加 2 个客户,它会抛出任何错误吗?或者简而言之,它是否会将它们排队并一个接一个地运行?谢谢
          • 实际上,您可以在一条语句中完成操作——无需事务。并且 SELECT MAX 并不能确保始终返回唯一值——在几乎每个数据库的默认隔离级别中,读取查询优先于更新查询。
          • 仅仅一个事务是不够的,你必须先锁表。
          猜你喜欢
          • 2017-03-08
          • 1970-01-01
          • 2016-12-25
          • 1970-01-01
          • 2017-03-21
          • 1970-01-01
          • 1970-01-01
          • 2010-12-07
          • 1970-01-01
          相关资源
          最近更新 更多