Laravel - 多插入行和检索 id
Posted
技术标签:
【中文标题】Laravel - 多插入行和检索 id【英文标题】:Laravel - multi-insert rows and retrieve ids 【发布时间】:2014-09-23 18:38:45 【问题描述】:我使用的是 Laravel 4,我需要在 mysql 表中插入一些行,并且我需要取回它们插入的 ID。
对于单行,我可以使用->insertGetId()
,但是它不支持多行。如果我至少可以像普通 MySQL 那样检索第一行的 ID,就足以找出其他行了。
【问题讨论】:
【参考方案1】:这是mysql的行为 last-insert-id
重要 如果您使用单个 INSERT 语句插入多行,则 LAST_INSERT_ID() 仅返回为第一个插入行生成的值。这样做的原因是可以轻松地针对其他服务器重现相同的 INSERT 语句。
你可以尝试使用多个插入并获取它的 id 或保存后,尝试使用 $data->id 应该是最后插入的 id。
【讨论】:
是的,但我想使用多次插入来提高性能。 通过 LAST_INSERT_ID() 你得到插入到数据库中的那一堆的第一个 ID。然后为您插入的每一行增加 1。 @Xrymz 的缺点是,如果有多个用户同时插入多个数据怎么办?你不能只是增加它们,因为它可能会返回其他用户的插入数据【参考方案2】:正如用户 Xrymz 建议的那样,DB::raw('LAST_INSERT_ID();')
返回第一个。
根据Schema apiinsertGetId()
接受数组
public int insertGetId(array $values, string $sequence = null)
所以你必须能够做到
DB::table('table')->insertGetId($arrayValues);
也就是说,如果使用 MySQL,您可以通过此检索第一个 id 并计算其余部分。还有一个DB::getPdo()->lastInsertId();
函数,可以提供帮助。
或者如果它使用某些方法返回了最后一个 id,您也可以将其计算回第一个插入的 id。
编辑
根据cmets,我的建议可能是错误的。
关于'如果行被另一个用户插入中间'的问题,它在商店引擎上depends。如果使用具有表级锁定的引擎(MyISAM、MEMORY 和 MERGE),那么这个问题就无关紧要了,因为不能同时有两个写入表。
如果使用行级锁定引擎(InnoDB),那么,另一种可能是只插入数据,然后使用whereIn()
方法通过某个已知字段检索所有行,或找出table level locking。
【讨论】:
实际上接受的数组代表单行。如果您尝试插入包含多行的嵌套数组,则会引发错误。 是的,只是它搞砸了,需要修复。否则它会抛出批量插入错误,因为它无法处理绑定等 如果有人在我得到最后一个ID之前插入表格怎么办?会导致问题吗? 没关系,我已经找到了。实际上 MySQL 使用的 last_inserted_id 是相对于每个连接的,所以即使其他客户端插入新行,我们最后插入的 id 仍然是相同的。 与此同时,我对 repo 进行了修复:github.com/laravel/framework/pull/5290,因此您可以依赖insertGetId
批处理语句,它将返回该批处理插入的第一个 id。【参考方案3】:
如果您使用支持事务的INNODB,那么您可以轻松解决这个问题。 有多种方法可以解决这个问题。
假设有一个名为Users
的表,它有两列id, name
和对User
模型的表引用。
解决方案 1
你的数据看起来像
$data = [['name' => 'John'], ['name' => 'Sam'], ['name' => 'Robert']]; // this will insert 3 rows
假设桌子上的最后一个 id 是600
。您可以像这样在表中插入多行
DB::begintransaction();
User::insert($data); // remember: $data is array of associative array. Not just a single assoc array.
$startID = DB::select('select last_insert_id() as id'); // returns an array that has only one item in it
$startID = $startID[0]->id; // This will return 601
$lastID = $startID + count($data) - 1; // this will return 603
DB::commit();
现在,您知道行在601
和603
的范围之间
确保使用这个在顶部导入DB
外观
use Illuminate\Support\Facades\DB;
解决方案 2 此解决方案要求您具有 varchar 或某种文本字段
$randomstring = Str::random(8);
$data = [['name' => "John$randomstring"], ['name' => "Sam$randomstring"]];
你明白了。您将该随机字符串添加到 varchar
或 text
字段中。
现在插入这样的行
DB::beginTransaction();
User::insert($data);
// this will return the last inserted ids
$lastInsertedIds = User::where('name', 'like', '%' . $randomstring)
->select('id')
->get()
->pluck('id')
->toArray();
// now you can update that row to the original value that you actually wanted
User::whereIn('id', $lastInsertedIds)
->update(['name' => DB::raw("replace(name, '$randomstring', '')")]);
DB::commit();
现在您知道插入了哪些行。
【讨论】:
所以事务防止表上的竞争条件?有没有可能因为交易而在两者之间出现另一种用途?这是如何运作的?整个表都被锁定了吗?以上是关于Laravel - 多插入行和检索 id的主要内容,如果未能解决你的问题,请参考以下文章