Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/sql/81.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
您能否在一条语句中访问MySQL中的自动增量值?_Sql_Mysql_Insert_Auto Increment - Fatal编程技术网

您能否在一条语句中访问MySQL中的自动增量值?

您能否在一条语句中访问MySQL中的自动增量值?,sql,mysql,insert,auto-increment,Sql,Mysql,Insert,Auto Increment,我有一个MySQL数据库,其中包含一个用户表。表的主键是“userid”,设置为自动递增字段 我想做的是,当我在表中插入一个新用户时,使用自动递增在另一个字段“default_assignment”的“userid”字段中创建的相同值 e、 g 我想要一个这样的声明: INSERT INTO users ('username','default_assignment') VALUES ('barry', value_of_auto_increment_field()) 因此,我创建了用户“Ba

我有一个MySQL数据库,其中包含一个用户表。表的主键是“userid”,设置为自动递增字段

我想做的是,当我在表中插入一个新用户时,使用自动递增在另一个字段“default_assignment”的“userid”字段中创建的相同值

e、 g

我想要一个这样的声明:

INSERT INTO users ('username','default_assignment') VALUES ('barry', value_of_auto_increment_field())
因此,我创建了用户“Barry”,“userid”生成为16(例如),但我还希望“default_赋值”具有相同的值16

请问有没有办法做到这一点

谢谢

更新:

谢谢你的回复。默认的_分配字段不是多余的。默认配置可以引用users表中的任何用户。当创建一个用户时,我已经有了一个允许选择另一个用户作为默认_分配的表单,但是在某些情况下它需要设置为同一个用户,因此我的问题是

更新:

好的,我已经尝试过更新触发器的建议,但仍然无法让它工作。以下是我创建的触发器:

CREATE TRIGGER default_assignment_self BEFORE INSERT ON `users`  
FOR EACH ROW BEGIN
SET NEW.default_assignment = NEW.userid;
END;
但是,插入新用户时,默认的_分配始终设置为0

如果我手动设置userid,那么默认的_分配将设置为userid


因此,自动赋值生成过程显然发生在触发器生效后。

看到
最后一次插入\u id()
在这种情况下不起作用,是的,触发器将是实现这一点的唯一方法


我问自己:你需要这个功能做什么?为什么要将用户id存储两次?就个人而言,我不喜欢像这样存储冗余数据,我可能会在应用程序代码中解决这个问题,方法是将不祥的
default\u赋值
列设为NULL,如果
default\u赋值
为NULL,则在我的应用程序代码中使用用户id。

实际上,我只是尝试做了与上述建议相同的事情。但是,Mysql似乎没有在实际提交行之前生成插入的ID。因此NEW.userid在插入前触发器中始终返回0

除非是插入前触发器,否则上述操作也不会起作用,因为无法更新插入后查询中的值

从Mysql论坛的帖子来看,处理这个问题的唯一方法似乎是使用一个额外的表作为序列。这样您的触发器就可以从外部源中提取值

CREATE TABLE `lritstsequence` (
  `idsequence` int(11) NOT NULL auto_increment,
  PRIMARY KEY  (`idsequence`)
) ENGINE=InnoDB;

CREATE TABLE `lritst` (
`id` int(10) unsigned NOT NULL auto_increment,
`bp_nr` decimal(10,0) default '0',
`descr` varchar(128) default NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `dir1` (`bp_nr`)
) ENGINE=InnoDB;

DELIMITER $$

DROP TRIGGER /*!50032 IF EXISTS */ `lritst_bi_set_bp_nr`$$

CREATE TRIGGER `lritst_bi_set_bp_nr` BEFORE INSERT ON `lritst`
FOR EACH ROW
BEGIN
    DECLARE secuencia INT;
    INSERT INTO lritstsequence (idsequence) VALUES (NULL);
    SET secuencia = LAST_INSERT_ID();
    SET NEW.id = secuencia;
    SET NEW.bp_nr = secuencia;
END;$$

DELIMITER ;

INSERT INTO lritst (descr) VALUES ('test1');
INSERT INTO lritst (descr) VALUES ('test2');
INSERT INTO lritst (descr) VALUES ('test3');

SELECT * FROM lritst;

Result:

    id   bp_nr  descr 
------  ------  ------
     1       1  test1 
     2       2  test2 
     3       3  test3

这是从forums.mysql.com/read.php?99186171186241#msg-186241复制的,但我还不允许发布链接。

我发现,在没有额外表格的情况下,唯一可以解决这个问题的方法是计算self的下一个数字,并将其放入所需字段中

CREATE TABLE `Temp` (
  `id` int(11) NOT NULL auto_increment,
  `value` varchar(255) ,
  PRIMARY KEY  (`idsequence`)
) ENGINE=InnoDB;


CREATE TRIGGER temp_before_insert BEFORE INSERT ON `Temp`
FOR EACH ROW 
BEGIN
    DECLARE m INT;

    SELECT IFNULL(MAX(id), 0) + 1 INTO m FROM Temp;
    SET NEW.value = m;
    -- NOT NEEDED but to be safe that no other record can be inserted in the meanwhile
    SET NEW.id = m;   
END;

无需创建另一个表,max()将根据表的自动增量值出现问题,请执行以下操作:

CREATE TRIGGER trigger_name BEFORE INSERT ON tbl FOR EACH ROW
BEGIN
   DECLARE next_id;
   SET next_id = (SELECT AUTO_INCREMENT FROM information_schema.TABLES WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='tbl');
   SET NEW.field = next_id;
END
我声明下一个_id变量,因为它通常会以其他方式使用(*),但您可以直接使用new.field=(选择…)

在(选择字符串字段)的情况下,也可以使用强制转换值

CREATE TRIGGER trigger_name BEFORE INSERT ON tbl FOR EACH ROW
BEGIN
   SET NEW.field=CAST((SELECT aStringField FROM information_schema.TABLES WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='tbl') AS UNSIGNED);
END
(*)要自动命名图像,请执行以下操作:

SET NEW.field = CONCAT('image_', next_id, '.gif');
(*)要创建哈希,请执行以下操作:

SET NEW.field = CONCAT( MD5( next_id ) , MD5( FLOOR( RAND( ) *10000000 ) ) );

基本上,解决方案类似于Resegue所说的。
但是,如果您希望在一个声明中使用它,您将使用以下方法之一:

1.一个长篇陈述:

INSERT INTO `t_name`(field) VALUES((SELECT AUTO_INCREMENT FROM information_schema.TABLES WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='t_name'))
或对于带有数字的文本:

INSERT INTO `t_name`(field) VALUES(CONCAT('Item No. ',CONVERT((SELECT AUTO_INCREMENT FROM information_schema.TABLES WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='t_name') USING utf8)))
它在PHP中看起来更清晰:

$pre_name='Item No. ';
$auto_inc_id_qry = "(SELECT AUTO_INCREMENT FROM information_schema.TABLES
    WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='$table')";
$new_name_qry = "CONCAT('$pre_name',CONVERT($auto_inc_id_qry USING utf8))";
mysql_query("INSERT INTO `$table`(title) VALUES($new_name_qry)");
2.使用函数:(尚未测试)

试试这个

INSERT INTO users (default_assignment) VALUES (LAST_INSERT_ID()+1);

您可以使用一个简单的子查询可靠地执行此操作:

INSERT INTO users ('username','default_assignment')
    SELECT 'barry', Auto_increment FROM information_schema.tables WHERE TABLE_NAME='users'

我用10个并发线程执行插入测试了上述触发器思想,在插入~25k后,我得到了1000多个2或3个重复的实例

DROP TABLE IF EXISTS test_table CASCADE;

CREATE TABLE `test_table` (
  `id`             INT         NOT NULL  AUTO_INCREMENT,
  `update_me`      VARCHAR(36),
  `otherdata`      VARCHAR(36) NOT NULL,

  PRIMARY KEY (`id`)
)
  ENGINE = InnoDB
  DEFAULT CHARSET = utf8
  COMMENT 'test table for trigger testing';

delimiter $$

DROP TRIGGER IF EXISTS setnum_test_table;
$$
CREATE TRIGGER setnum_test_table
BEFORE INSERT ON test_table FOR EACH ROW
-- SET OLD.update_me = CONCAT(NEW.id, 'xyz');

BEGIN
   DECLARE next_id INT;
   SET next_id = (SELECT AUTO_INCREMENT FROM information_schema.TABLES WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='test_table' LOCK IN SHARE MODE );
--    SET NEW.update_me = CONCAT(next_id, 'qrst');
    SET NEW.update_me = next_id;
END

$$

delimiter ;

-- SELECT AUTO_INCREMENT FROM information_schema.TABLES WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='test_table'

INSERT INTO test_table (otherdata) VALUES ('hi mom2');

SELECT count(*) FROM test_table;
SELECT * FROM test_table;

-- select count(*) from (
select * from (
SELECT count(*) as cnt ,update_me FROM test_table group by update_me) q1
where cnt > 1
order by cnt desc
我使用了以下10种方法:

while true ; do echo "INSERT INTO test_table (otherdata) VALUES ('hi mom2');" | mysql --user xyz testdb ; done &
并运行最后一个查询以查看重复项

示例输出: '3', '4217' '3', '13491' '2', '10037' '2', '14658' '2', '5080' '2', '14201'


注意“锁定共享模式”没有改变任何内容。有和没有以大约相同的速率给出重复。MySQL AUTO_INCREMENT似乎不像Postgres的next_val()那样工作,也不是并发安全的。

我知道这篇文章是从2010年开始的,但我找不到一个好的解决方案。 我通过创建一个单独的表来保存计数器来解决这个问题。当我需要为列生成唯一标识符时,我只需调用存储的过程:

CREATE DEFINER=`root`@`localhost` PROCEDURE `IncrementCounter`(in id varchar(255))
BEGIN
declare x int;
-- begin;
start transaction;
-- Get the last counter (=teller) and mark record for update.
select Counter+1 from tabel.Counter where CounterId=id into x for update;
-- check if given counter exists and increment value, otherwise create it.
if x is null then
    set x = 1;
    insert into tabel.Counters(CounterId, Counter) values(id, x);
else
    update tabel.Counters set Counter = x where CounterId = id;
end if;
-- select the new value and commit the transaction
select x;
commit;
END

“for update”语句锁定计数器表中的行。这可以避免多个线程进行重复操作。

可能希望触发器在插入之后而不是之前运行,因为用户ID字段直到插入完成后才有值。请尝试以下操作:
CREATE trigger default\u assignment\u self BEFORE insert ON
users
为每行开始设置新的默认\u assignment=(从information_schema.TABLES中选择AUTO_INCREMENT,其中TABLE_schema=DATABASE()和TABLE_NAME='users');END/
这真是一团糟……对我来说,最好的解决方案是在插入后立即用主键的值更新
userid
。+1
SET next\u id=(从information_schema.TABLES中选择AUTO_INCREMENT,其中TABLE_schema=DATABASE()和TABLE_NAME='tbl');
为我完成了这项任务。感谢您的支持(从information_schema.TABLES中选择AUTO_INCREMENT,其中TABLE_schema=DATABASE()和TABLE_NAME='tbl'))在高度并发的innoDB表格式中,它可以锁定子查询的结果,避免一行选择另一行的自动递增。我在这方面做了一些测试,在10到1000毫秒的随机时间内放置了1200个请求,每秒插入500次,受服务器容量的限制。多记录是一个问题事实上,如果可以,请指出一个更好的解决方案。将“锁定在共享模式”添加到从信息架构中选择会解决并发竞态条件吗?如果您不处于可能出现
MAX(id)
函数竞态条件的并发情况,这似乎是最简单的解决方案
DROP TABLE IF EXISTS test_table CASCADE;

CREATE TABLE `test_table` (
  `id`             INT         NOT NULL  AUTO_INCREMENT,
  `update_me`      VARCHAR(36),
  `otherdata`      VARCHAR(36) NOT NULL,

  PRIMARY KEY (`id`)
)
  ENGINE = InnoDB
  DEFAULT CHARSET = utf8
  COMMENT 'test table for trigger testing';

delimiter $$

DROP TRIGGER IF EXISTS setnum_test_table;
$$
CREATE TRIGGER setnum_test_table
BEFORE INSERT ON test_table FOR EACH ROW
-- SET OLD.update_me = CONCAT(NEW.id, 'xyz');

BEGIN
   DECLARE next_id INT;
   SET next_id = (SELECT AUTO_INCREMENT FROM information_schema.TABLES WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='test_table' LOCK IN SHARE MODE );
--    SET NEW.update_me = CONCAT(next_id, 'qrst');
    SET NEW.update_me = next_id;
END

$$

delimiter ;

-- SELECT AUTO_INCREMENT FROM information_schema.TABLES WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='test_table'

INSERT INTO test_table (otherdata) VALUES ('hi mom2');

SELECT count(*) FROM test_table;
SELECT * FROM test_table;

-- select count(*) from (
select * from (
SELECT count(*) as cnt ,update_me FROM test_table group by update_me) q1
where cnt > 1
order by cnt desc
while true ; do echo "INSERT INTO test_table (otherdata) VALUES ('hi mom2');" | mysql --user xyz testdb ; done &
CREATE DEFINER=`root`@`localhost` PROCEDURE `IncrementCounter`(in id varchar(255))
BEGIN
declare x int;
-- begin;
start transaction;
-- Get the last counter (=teller) and mark record for update.
select Counter+1 from tabel.Counter where CounterId=id into x for update;
-- check if given counter exists and increment value, otherwise create it.
if x is null then
    set x = 1;
    insert into tabel.Counters(CounterId, Counter) values(id, x);
else
    update tabel.Counters set Counter = x where CounterId = id;
end if;
-- select the new value and commit the transaction
select x;
commit;
END