【问题标题】:Laravel 5.1 Create or Update on DuplicateLaravel 5.1 创建或更新重复
【发布时间】:2015-10-15 22:47:00
【问题描述】:

在 Laravel 5.1 中,对于 MySQL 插入,我想查看记录是否已经存在并在重复时更新或如果不存在则创建新记录。

我已经搜索过旧 laravel 版本的答案。在一个旧主题中,它说去年在核心中添加了一个新的updateOrCreate() 方法。但是当我尝试这样做时,我收到了错误:

Integrity constraint violation: 1062 Duplicate entry '1' for key 'app_id' 

这是我使用的查询:

AppInfo::updateOrCreate(array(
    'app_id' => $postData['appId'],
    'contact_email' => $postData['contactEmail']
));

app_id 是该表中唯一的外键,我想更新记录(如果存在)或创建新记录。我尝试搜索 5.1 文档,但找不到我需要的信息。有人可以在这里指导我吗...

【问题讨论】:

标签: laravel laravel-5 eloquent query-builder


【解决方案1】:

根据 Eloquent 模型方法“updateOrCreate()”的定义

函数 updateOrCreate(array $attributes, array $values = []){}

它需要两个参数...

  1. 一个是属性,如果记录存在,您要使用它来检查数据库
  2. second 是您要创建或更新的新属性值

AppInfo::updateOrCreate(['app_id' => $postData['appId']],
                        ['contact_email' => $postData['contactEmail']]);

【讨论】:

  • 完美!这正是我做错的。我认为在数据库中设置唯一键应该自动使用它,但我错了。非常感谢您指出@MrPandav 在向其添加 2 个参数后它现在运行良好。 :)
  • @Neel : 每当你在网上找不到任何文档(通常是最先进的 beta 框架的情况)或有疑问时,打开他们的源代码文件,他们总是有非常好的文档和场景在每个函数定义之前解释
  • 如果我还没有在数据库上创建记录,我怎么有id?
  • 它可以是您要在数据库中检查的任何列,即名称、电子邮件..等如果找到具有该值的行,则该行中的数据将更新为您的新数据已提供否则将创建新记录
  • 此方法是否有批量更新版本以减少数据库查询开销?与此 SQL 等效的内容: INSERT INTO table (id, col1, col2) VALUES (1, blue, shirt), (2, green,pants), (3, red, hat) ON DUPLICATE KEY UPDATE col1=VALUES(col1) , col2=VALUES(col2)
【解决方案2】:

我正在回答这个问题,因为我找不到任何与 ON DUPLICATE KEY UPDATE 相关的答案,尽管我使用的是 Laravel 5.4。 如果您查看 Laravel 核心代码中的 updateOrCreate 方法,您会发现毕竟 Laravel 使用了 2 个不同的查询:一个用于更新,另一个用于创建。因此,有时您可以在数据库中获得重复的数据。所以在某些情况下,编写这种原始查询会很有用:

DB::statement("INSERT INTO table_name (col_1, col_2) 值 (?, ?) 重复密钥更新 col_1 = col_1 + 1", ([val_1, val_2]));

希望它对某人有用。

【讨论】:

    【解决方案3】:

    我创建了一个包来使用 MySQL 在重复键上插入。

    可能对其他人有用:

    https://packagist.org/packages/yadakhov/insert-on-duplicate-key

    例子:

    /**
     * Class User.
     */
    class User extends Model
    {
        use Yadakhov\InsertOnDuplicateKey;
        ...
    }
    
    // associative array must match column names
    $users = [
        ['id' => 1, 'email' => 'user1@email.com', 'name' => 'User One'],
        ['id' => 2, 'email' => 'user2@email.com', 'name' => 'User Two'],
        ['id' => 3, 'email' => 'user3@email.com', 'name' => 'User Three'],
    ];
    
    User::insertOnDuplicateKey($users);
    

    【讨论】:

    • 它也适用于唯一键吗?我的表中没有主键。有 2 列是唯一的。所以我想根据前 2 列对第三列进行大规模更新。
    【解决方案4】:

    要使用 laravel 函数 updateOrCreate,您需要在表中自动增加 id

    他们在做什么

    select id from your_table where your_attributes

    之后获取自动递增 id

    然后

    update your_table set your_values where field_id

    【讨论】:

    • 就在我想使用没有自动增量的复合键时id..
    • 您需要主字段名称id。他们采取受保护的 $primaryKey = 'id';来自模型,否则您可以在模型中覆盖。
    【解决方案5】:

    将以下方法 insertUpdate 添加到您的模型中

    <?php
    
    namespace App;
    
    use Illuminate\Auth\Authenticatable;
    use Illuminate\Database\Eloquent\Model;
    use Illuminate\Auth\Passwords\CanResetPassword;
    use Illuminate\Foundation\Auth\Access\Authorizable;
    use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
    use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract;
    use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract;
    
    class User extends Model implements AuthenticatableContract,
                                        AuthorizableContract,
                                        CanResetPasswordContract
    {
        use Authenticatable, Authorizable, CanResetPassword;
    
        /**
         * The database table used by the model.
         *
         * @var string
         */
        protected $table = 'users';
    
        /**
         * The attributes that are mass assignable.
         *
         * @var array
         */
        protected $fillable = ['name', 'email', 'password'];
    
        /**
         * The attributes excluded from the model's JSON form.
         *
         * @var array
         */
        protected $hidden = ['password', 'remember_token'];
    
    
        public static function insertUpdate(array $attributes = [])
        {
            $model = new static($attributes);
    
            $model->fill($attributes);
    
            if ($model->usesTimestamps()) {
                $model->updateTimestamps();
            }
    
            $attributes = $model->getAttributes();
    
            $query = $model->newBaseQueryBuilder();
            $processor = $query->getProcessor();
            $grammar = $query->getGrammar();
    
            $table = $grammar->wrapTable($model->getTable());
            $keyName = $model->getKeyName();
            $columns = $grammar->columnize(array_keys($attributes));
            $insertValues = $grammar->parameterize($attributes);
    
            $updateValues = [];
    
            if ($model->primaryKey !== null) {
                $updateValues[] = "{$grammar->wrap($keyName)} = LAST_INSERT_ID({$keyName})";
            }
    
            foreach ($attributes as $k => $v) {
                $updateValues[] = sprintf("%s = '%s'", $grammar->wrap($k), $v);
            }
    
            $updateValues = join(',', $updateValues);
    
            $sql = "insert into {$table} ({$columns}) values ({$insertValues}) on duplicate key update {$updateValues}";
    
            $id = $processor->processInsertGetId($query, $sql, array_values($attributes));
    
            $model->setAttribute($keyName, $id);
    
            return $model;
        }
    }
    

    你可以使用:

    App\User::insertUpdate([
        'name' => 'Marco Pedraza',
        'email' => 'mpdrza@gmail.com'
    ]);
    

    它将执行的下一个查询:

    insert into `users` (`name`, `email`, `updated_at`, `created_at`) values (?, ?, ?, ?) on duplicate key update `id` = LAST_INSERT_ID(id),`name` = 'Marco Pedraza',`email` = 'mpdrza@gmail.com',`updated_at` = '2016-11-02 01:30:05',`created_at` = '2016-11-02 01:30:05'
    

    如果您已启用或禁用,该方法会自动添加/删除 Eloquent 时间戳。

    【讨论】:

    • 嗨,欢迎来到 SO。请不要只是转储代码,请解释您的思路,以便人们更好地理解您的答案。谢谢。
    • 有用的函数似乎正在工作,与 updateOrCreate 函数不同,这个函数还更新时间戳。但是为什么它会同时更新两者?它应该只更新 updated_at,目前它还更新 created_at。有没有办法将此功能与批量插入一起使用?这就是我来寻找这个的原因,否则 updateOrCreate 函数似乎工作正常,除了它没有自动更新时间戳。
    • 它也适用于唯一键吗?我的表中没有主键。有 2 列是唯一的。所以我想根据前 2 列对第三列进行大规模更新。
    【解决方案6】:

    在我的情况下,这是由于我的模型表中自动递增键的简单不匹配引起的。

    获取当前最大id:

    SELECT max(id) from companies
    

    然后将序列值设置高一:

    select setval('companies_id_seq', 164);
    

    消除了错误。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-01-12
      • 1970-01-01
      • 2016-02-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多