web-dev-qa-db-ja.com

MySQLテーブルに多くの行を挿入し、新しいIDを返すにはどうすればよいですか?

通常、MySQLテーブルに行を挿入し、last_insert_id戻る。しかし、今では、テーブルに大量の行を一括挿入して、IDの配列を取得したいと考えています。誰も私がこれを行う方法を知っていますか?

似たような質問がいくつかありますが、それらはまったく同じではありません。新しいIDを一時テーブルに挿入したくありません。 IDの配列を取得したいだけです。

一括挿入からlastInsertIdを取得できますか?

last_insert_id()を含むMySQLの複数行挿入選択ステートメント

67
Peacemoon

古いスレッドですが、これを調べたばかりなので、最近のバージョンのMySQLでInnoDBを使用している場合は、LAST_INSERT_ID()およびROW_COUNT()を使用してIDのリストを取得できます。

_innodb_autoinc_lock_mode_が0(従来)または1(連続)に設定されている場合、InnoDBは、一括挿入を行うときにAUTO INCREMENTの連続番号を保証します。したがって、LAST_INSERT_ID()からfirst IDを取得し、ROW_COUNT()-1を追加することでlastを取得できます。

63

私がそれを行うことができると思う唯一の方法は、挿入された行のセットごとに一意の識別子を格納し(ガイド)、行IDを選択する場合です。例えば:

_INSERT INTO t1
(SELECT col1,col2,col3,'3aee88e2-a981-1027-a396-84f02afe7c70' FROM a_very_large_table);
COMMIT;

SELECT id FROM t1 
WHERE guid='3aee88e2-a981-1027-a396-84f02afe7c70';
_

uuid()を使用して、データベースにGUIDを生成することもできます

13
Kevin Burton

2つのcols uidを持つtemptableと呼ばれるテーブルがあり、col1がuidが自動インクリメントフィールドであると仮定しましょう。以下のような操作を行うと、結果セットに挿入されたすべてのIDが返されます。結果セットをループして、IDを取得できます。これは古い投稿であり、このソリューションはすべてのケースで機能するとは限らないことを理解しています。しかし、他の人にとってはそうかもしれません。だから私はそれに返信しています。

# lock the table
lock tables temptable write;

#bulk insert the rows;
insert into temptable(col1) values(1),(2),(3),(4);

#get the value of first inserted row. when bulk inserting last_insert_id() #should give the value of first inserted row from bulk op.
set @first_id = last_insert_id();

#now select the auto increment field whose value is greater than equal to #the first row. Remember since you have write lock on that table other #sessions can't write to it. This resultset should have all the inserted #id's
select uid from temptable where uid >=@first_id;

#now that you are done don't forget to unlock the table.
unlock tables;
4
sundeep

これを完璧に行うには、アプリケーションでトランザクションIDを処理するか、アプリケーションでアイテムIDを処理する必要があると思います。

これを行う1つの方法は、すべての挿入が成功したと仮定して(!)、次のとおりです。

次に、lastid(一括挿入の最初に挿入されたID)から始まる、影響を受けた行の数のループを使用して、挿入されたIDを取得できます。したがって、私はそれが完全に動作することを確認しました。たとえばHeidiSQLがROW_COUNT()の正しい値を返さないことに注意してください、おそらくそれは私たちがそれを求めないランダムなたわごとを行うくだらないGUIだからです-しかし、どちらからも完全に正しいですコマンドラインまたはPHP mysqli-

START TRANSACTION;
BEGIN;
INSERT into test (b) VALUES ('1'),('2'),('3');
SELECT LAST_INSERT_ID() AS lastid,ROW_COUNT() AS rowcount;
COMMIT;

PHPは次のようになります(local_sqleはmysqli_queryの直接呼び出し、local_sqlecはmysqli_queryの呼び出し+結果セットをPHP配列)に変換します。

local_sqle("START TRANSACTION;
BEGIN;
INSERT into test (b) VALUES ('1'),('2'),('3');");
$r=local_sqlec("SELECT LAST_INSERT_ID() AS lastid,ROW_COUNT() AS rowcount;");
local_sqle("
COMMIT;");
$i=0;
echo "last id =".($r[0]['lastid'])."<br>";
echo "Row count =".($r[0]['rowcount'])."<br>";

while($i<$r[0]['rowcount']){
    echo "inserted id =".($r[0]['lastid']+$i)."<br>";
    $i++;
}

クエリが分離される理由は、それ以外の場合は独自の関数を使用して結果を取得しないためです。標準関数でこれを行うと、1つのステートメントに戻してから必要な結果を取得できます(結果番号である必要があります) 2-複数の結果セット/クエリを処理する拡張機能を使用すると仮定します)。

0
Morg.