23

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

我想做的是,当我将新用户插入表中时,使用与自动增量在不同字段“default_assignment”的“userid”字段中创建的相同值。

例如

我想要这样的声明:

INSERT INTO users ('username','default_assignment') VALUES ('barry', value_of_auto_increment_field())

所以我创建了用户“Barry”,“userid”生成为 16(例如),但我也希望“default_assignment”具有相同的值 16。

请问有什么办法可以实现吗?

谢谢!

更新:

感谢您的回复。default_assignment 字段不是多余的。default_assigment 可以引用 users 表中的任何用户。创建用户时,我已经有一个允许选择另一个用户作为 default_assignment 的表单,但是在某些情况下需要将其设置为同一用户,因此我的问题是。

更新:

好的,我已经尝试了更新触发建议,但仍然无法使其正常工作。这是我创建的触发器:

CREATE TRIGGER default_assignment_self BEFORE INSERT ON `users`  
FOR EACH ROW BEGIN
SET NEW.default_assignment = NEW.userid;
END;

但是,当插入新用户时,default_assignment 始终设置为 0。

如果我手动设置用户 ID,则 default_assignment 确实设置为用户 ID。

因此,自动分配生成过程显然发生在触发器生效之后。

4

10 回答 10

34

不需要创建另一个表,max() 根据表的 auto_increment 值会有问题,这样做:

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

我声明了 next_id 变量,因为通常它会以其他方式使用(*),但您可以直接执行 new.field=(select ...)

CREATE TRIGGER trigger_name BEFORE INSERT ON tbl FOR EACH ROW
BEGIN
   SET NEW.field=(SELECT AUTO_INCREMENT FROM information_schema.TABLES WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='tbl');
END

同样在(SELECT 字符串字段)的情况下,您可以使用 CAST 值;

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 ) ) );
于 2010-07-26T11:46:27.727 回答
3

试试这个

INSERT INTO users (default_assignment) VALUES (LAST_INSERT_ID()+1);
于 2014-04-24T07:31:02.120 回答
2

看到这last_insert_id()在这种情况下不起作用,是的,触发器将是实现这一目标的唯一方法。

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

于 2009-01-22T13:16:13.147 回答
2

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

除非它是 BEFORE INSERT 触发器,否则上述内容也不会起作用,因为您无法更新 AFTER INSERT 查询中的值。

来自 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?99,186171,186241#msg-186241 复制的,但我还不允许发布链接。

于 2009-05-27T09:20:04.383 回答
2

我发现唯一可以在没有额外表格的情况下解决此问题的方法是计算 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;
于 2009-06-03T22:17:34.467 回答
0

基本上,解决方案就像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.使用功能:(未测试)

CREATE FUNCTION next_auto_inc(table TINYTEXT) RETURNS INT
BEGIN
DECLARE next_id INT;
SELECT AUTO_INCREMENT FROM information_schema.TABLES
    WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME=table INTO next_id;
RETURN next_id;
END

INSERT INTO users ('username','default_assignment')
    VALUES ('barry', next_auto_inc('users'))
于 2012-02-13T09:12:40.623 回答
0
$ret = $mysqli->query("SELECT Auto_increment FROM information_schema.tables WHERE table_schema = DATABASE() ");l
while ($row = mysqli_fetch_array($ret)) {
    $user_id=$row['Auto_increment'];
}
于 2013-07-27T03:11:07.290 回答
0

您可以使用简单的子查询可靠地做到这一点:

INSERT INTO users ('username','default_assignment')
    SELECT 'barry', Auto_increment FROM information_schema.tables WHERE TABLE_NAME='users'
于 2015-09-19T12:33:00.847 回答
0

我用 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() 那样工作,并且不是并发安全的。

于 2017-02-10T03:51:54.830 回答
0

我知道这篇文章是 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' 语句锁定计数器表中的行。这避免了多个线程进行重复。

于 2017-08-20T19:35:10.120 回答