【问题标题】:Laravel firstOrCreate method throws duplicate ID errorLaravel firstOrCreate 方法抛出重复 ID 错误
【发布时间】:2018-11-28 04:06:51
【问题描述】:

我正在我的 Laravel 项目中编写一个命令,该命令将类别插入数据库表,但取决于它们是否已经存在。

我研究了这样做的方法并遇到了firstOrCreate方法,所以我在我的命令中写了以下内容:

$comCats = new CommunicationsCategories();

$comCats->firstOrCreate(
    ['name' => 'Job Updates'], ['region_id' => 1]);
$comCats->firstOrCreate(
    ['name' => 'Alerts'], ['region_id' => 1]);

基本上,我需要在区域 ID 为 1 的communications_categories 表中创建这两个类别。 Job Updates 类别已经存在,因此它按预期跳过了该类别,但是当它尝试创建不存在的 Alerts 类别时我在控制台中收到以下错误:

SQLSTATE[23505]:唯一违规:7 错误:重复键值 违反唯一约束“communications_categories_pkey”

详细信息:键 (id)=(2) 已存在。 (SQL:插入 “communications_categories”(“名称”、“region_id”、“updated_at”、 “created_at”)值(警报,1,2018-06-19 09:38:20,2018-06-19 09:38:20)返回“id”)

当它已经存在时,它似乎正在尝试分配 2 的主键 ID - 但表结构在主键上有一个 nextval,我认为它会采用最后添加的 ID 并在此之后创建一个新 ID。根据关于 Eloquent Inserts here 的 Laravel 文档,没有提到必须自己规定实际的主键 id,可填充元素只有 nameregion_id

对此的任何帮助表示赞赏,因为我对 Laravel 和 eloqent 数据库方法相当陌生。如果有帮助,我正在使用 PostgreSQL 数据库。

【问题讨论】:

标签: laravel postgresql eloquent


【解决方案1】:

另一个答案似乎假设您需要向您解释 firstOrCreate,而实际上您实际上在 Laravel 框架中遇到了一个错误,并且还假设 Key (id)=(2) 指的是 region_id 而不是您的自动增量主键id

您很可能希望在 communications_categories.name 上有一个唯一索引。

您可以改为在 postgres 中使用此 SQL(假设您在名称上有唯一索引),它应该比 firstOrCreate 更可靠,但如果您计划将其他 laravel 功能与 firstOrCreate 一起使用(如观察者或更多查询构建器) ,它们的功能可能会丢失。

    INSERT INTO communications_categories (name, region_id, created_at, updated_at)
    VALUES ('Job Updates', 1, NOW(), NOW())
    ON CONFLICT (name) DO
    UPDATE SET region_id = excluded.region_id,
        updated_at = NOW()
    RETURNING *;

可以这样使用:

    $values = DB::select($sql); // will return Array of StdClass

如果您不需要 id 值,也可以不使用 returning *

    DB::insert($sql); // will return true

如果您需要返回 Eloquent 对象,我建议您包含 returning *returning id 并将其传递给 CommunicationsCategories::findOrFail($values[0]['id'])。将复杂的插入语句放入 Eloquent 选择中可能需要做很多工作。 Eloquent 还有一个名为 hydrate 的函数,无需额外的 SQL 调用即可工作。

SQL 语句将插入所需的值,除非唯一约束存在冲突,否则它将通过更新将因冲突排除的值从插入应用到预先存在的行。

【讨论】:

    【解决方案2】:

    firstOrCreate 方法将尝试使用给定的列/值对来定位数据库记录。如果在数据库中找不到模型,则会插入一条记录,其中包含第一个参数中的属性以及可选的第二个参数中的属性。

    $comCats->firstOrCreate(
        ['name' => 'Job Updates'], ['region_id' => 1]);
    

    这里你试图首先找到一个带有“Job Updates”的name和一个带有1的region_id,如果laravel找不到具体的数据,它会尝试创建或插入给定的参数,但是当系统尝试插入,region_id 的值为“1”已经存在。

    如果region_id 是您的主键,请尝试:

      $comCats->firstOrCreate(
            ['name' => 'Job Updates']);
    

    【讨论】:

    • region_id 不是我的主键。该表具有分配给相同区域 ID 的许多类别。正如我在上面的评论中提到的,我写它的方式是,检查是否存在 Job Updates 的名称,如果不存在,请将其添加到 region_id 为 1。这记录在 laravel.com/docs/5.6/eloquent#inserts 下其他创建方法部分。
    • 您能否检查表communications_categories_pkey 和communications_categories 上字段region_id 的索引,确保它不是唯一的。
    • 我不需要 - 它绝对 100% 不是独一无二的。这个数据已经在这里好几个月了。我只是接手这个项目——有很多类别分配给一个区域,否则就有点没有意义了。
    【解决方案3】:

    请原谅我还不能发表评论...

    如果有人在 postgresql 上遇到同样的问题,这绝对是一个错误。在我的情况下,这个错误是在我按照以下过程使用 heroku 导入本地 postgresql 数据库后发生的:

    Workaround for pushing to heroku database on windows machince.

    我尝试再次运行查询并且它工作了......它有时也会失败。现在这很糟糕。我会更新这个我找到了解决这个问题的另一种方法。

    编辑: 在查看了有关该问题的更多搜索后,我发现以下内容完全符合我的情况:

    PostgreSQL: Unique violation: 7 ERROR: duplicate key value violates unique constraint “users_pkey”

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-01-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-07-11
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多