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。如果使用具有表级锁定的引擎(MyISAMMEMORYMERGE),那么这个问题就无关紧要了,因为不能同时有两个写入表。

如果使用行级锁定引擎(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();

现在,您知道行在601603 的范围之间 确保使用这个在顶部导入DB 外观

use Illuminate\Support\Facades\DB;

解决方案 2 此解决方案要求您具有 varchar 或某种文本字段

$randomstring = Str::random(8);
$data = [['name' => "John$randomstring"], ['name' => "Sam$randomstring"]];

你明白了。您将该随机字符串添加到 varchartext 字段中。

现在插入这样的行

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的主要内容,如果未能解决你的问题,请参考以下文章

在数据透视表 laravel 中删除行和插入行

Laravel 插入和检索关系

Laravel 计数和检索多对多关系

无法在 Laravel 4 中检索一对多关系

使用 laravel 从多对多检索数据

如何在多对多关系 Laravel 中检索所有相关模型?