ユーザーのテーブルを含む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に設定されます。
したがって、自動割り当て生成プロセスは、トリガーが有効になった後に明らかに発生します。
別のテーブルを作成する必要はありません。max()では、テーブルのauto_increment値に応じて問題が発生します。次のようにします。
CREATE TRIGGER trigger_name BEFORE INSERT ON tbl FOR EACH ROW
BEGIN
DECLARE next_id INT;
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 ...)を実行することもできます。
(*) To auto-name an image:
SET NEW.field = CONCAT('image_', next_id, '.gif');
(*) To create a hash:
SET NEW.field = CONCAT( MD5( next_id ) , MD5( FLOOR( Rand( ) *10000000 ) ) );
これを試して
INSERT INTO users (default_assignment) VALUES (LAST_INSERT_ID()+1);
この場合、last_insert_id()
が機能しないことを確認すると、はい、トリガーがそれを実現する唯一の方法になります。
しかし、私は自問します:この機能は何のために必要ですか?なぜユーザーIDを2回保存するのですか?個人的には、このような冗長データを保存するのは好きではありません。おそらく、その不吉なdefault_assignment
列をNULLにし、default_assignment
がNULLの場合はアプリケーションコードでユーザーIDを使用することで、アプリケーションコードでこれを解決します。 。
実際、私は上記で提案されたのと同じことをやろうとしました。しかし、Mysqlは、行が実際にコミットされる前に、挿入されたIDを生成しないようです。したがって、NEW.useridは、挿入前トリガーで常に0を返します。
上記は、AFTER INSERTクエリで値を更新できないため、BEFOREINSERTトリガーでない限り機能しません。
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からコピーされましたが、リンクを投稿することはまだ許可されていません。
追加のテーブルなしでこの問題を解決することがわかったのは、次の数値を自分で計算し、それを必要なフィールドに入力することだけです。
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 save that no other record can be inserted in the meanwhile
SET NEW.id = m;
END;
上記のトリガーのアイデアを10個の同時スレッドでテストし、挿入を実行しました。約25kの挿入後に、2つまたは3つの重複が1000件以上発生しました。
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()のようには機能せず、並行性に対して安全ではないようです。
単純なサブクエリを使用して、これを確実に行うことができます。
INSERT INTO users ('username','default_assignment')
SELECT 'barry', Auto_increment FROM information_schema.tables WHERE TABLE_NAME='users'
基本的に、解決策はResegueと言ったようなものです。
ただし、1つのステートメントでそれが必要な場合は、次のいずれかの方法を使用します。
1。 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'))
$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'];
}
この投稿が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'ステートメントは、countersテーブルの行をロックします。これにより、複数のスレッドによる重複が回避されます。