【问题标题】:INSERT IGNORE using Laravel's Fluent使用 Laravel 的 Fluent 插入忽略
【发布时间】:2012-09-19 07:05:47
【问题描述】:

有没有一种快速的方法可以将Laravel's Fluent 生成的SQL 查询修改为INSERT IGNORE 而不是通常的INSERT

我正在尝试插入一个包含五十个元素的数组。手动写出整个查询会使代码膨胀并使其更容易受到人为错误的影响。

【问题讨论】:

  • 数组是否是键 => 值对,对应于列 => 值设置?如果是这样,只需编写一个循环来生成它。
  • 是的! :) 你的意思是写一个循环来生成SQL查询语句?
  • 谢谢,不知道其他数据库也可以用!我应该如何改变它?

标签: php mysql laravel


【解决方案1】:
$your_array = array('column' => 'value', 'second_column' => 'value');

DB::table('your_table')->insert($your_array);

请记住,我不知道您的数据来自哪里,但您应该始终对其进行清理。如果您有多个记录,只需循环遍历即可。

至于INSERT IGNORE,在fluent库中找到INSERT方法,新建一个名为insert_ignore的方法,与insert方法完全相同,修改为IGNORE即可。

【讨论】:

  • 您的查询是否执行INSERT IGNORE
  • 你为什么需要那个,你从来没有指定
  • 这个答案没有解释如何在 Laravel 中使用 INSERT IGNORE。 Laravel 不支持INSERT IGNORE
【解决方案2】:

对于这项工作,您需要创建一个新的语法,其中包含正确的字符串:

grammar.php(1)

语法是DB 或在本例中为Database 存储连接的公共属性。这不是很直接,但是从属性的可见性来看,您应该能够将您的特殊语法注入数据库层。

我还建议您在项目中提出问题,他们可能有更好的想法如何使此类情况更加灵活。


(1) 这是以前的,直到答案参考的日期。如果你今天看到这个,你需要采用你使用的 Laravel 版本,例如Grammar.php for 4.0,这些课程已移至laravel/framework

【讨论】:

  • 该链接现在给出 404。
  • @wiggles:容易修复。然而,OP 似乎没有花时间报告这一点。
  • 不再需要这个了,因为它在 Laravel 中至少从 5.8 版本开始支持。请参阅下面的答案...
【解决方案3】:

在你的模型中试试这个魔法:

public static function insertIgnore($array){
    $a = new static();
    if($a->timestamps){
        $now = \Carbon\Carbon::now();
        $array['created_at'] = $now;
        $array['updated_at'] = $now;
    }
    DB::insert('INSERT IGNORE INTO '.$a->table.' ('.implode(',',array_keys($array)).
        ') values (?'.str_repeat(',?',count($array) - 1).')',array_values($array));
}

这样使用:

Shop::insertIgnore(array('name' => 'myshop'));

如果 'name' 属性是唯一键,这是防止在多用户环境中使用 firstOrCreate 可能发生的约束违规的好方法。

【讨论】:

  • 应该注意,这样做可能是正确的方法,但是您需要确保在运行查询之前清除所有用户生成的变量,因为这是原始 SQL!
【解决方案4】:

不确定是否对任何人有帮助,但最近我已将 hakre 的方法应用于 Laravel 5:

您必须更改以下 3 个文件才能让插入忽略工作:

  1. 在 Builder.php (vendor/laravel/framework/src/illuminate/database/query/Builder.php) 中,您必须克隆函数 insert,并将名称更改为 insertIgnore 并更改语法调用作用于:$sql = $this->grammar->compileInsertIgnore($this, $values);)

  2. 在 Grammar.php (vendor/laravel/framework/src/illuminate/database/query/grammars/Grammar.php) 中,您必须克隆 compileInsert 函数并将其重命名为 compileInsertIgnore,在此处您将返回更改为: return "insert ignore into $table ($columns) values $parameters";

  3. 在 Connection.php (vendor/laravel/framework/src/illuminate/database/Connection.php) 中,您只需克隆函数 insert 并将其重命名为 insertIgnore

现在您应该完成了,connection 能够识别函数 insertIgnore,builder 能够将其指向正确的语法,并且语法在语句中包含 'ignore'。请注意,这适用于 MySQL,但可能不适用于其他数据库。

【讨论】:

    【解决方案5】:

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

    <?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 insertIgnore(array $attributes = [])
        {
            $model = new static($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));
            $values = $grammar->parameterize($attributes);
    
            $sql = "insert ignore into {$table} ({$columns}) values ({$values})";
    
            $id = $processor->processInsertGetId($query, $sql, array_values($attributes));
    
            $model->setAttribute($keyName, $id);
    
            return $model;
        }
    }
    

    你可以使用:

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

    它将执行的下一个查询:

    insert ignore into `users` (`name`, `email`, `updated_at`, `created_at`) values (?, ?, ?, ?)
    

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

    【讨论】:

      【解决方案6】:

      我无法按照 Rastislav 的回答中的建议进行修补。

      这对我有用:

      1. 在自定义 Query Grammar 类中重写 compileInsert 方法,该类扩展了框架的 MySqlGrammar 类。

      2. 通过从数据库连接实例调用setQueryGrammar 方法来使用此自定义语法类的实例。

      所以,类代码是这样的:

      <?php
      
      namespace My\Namespace;
      
      use Illuminate\Database\Query\Builder;
      use Illuminate\Database\Query\Grammars\MySqlGrammar;
      
      /**
       * Changes "INSERT" to "INSERT IGNORE"
       */
      class CustomMySqlGrammar extends MySqlGrammar
      {
          /**
           * Compile an insert statement into SQL.
           *
           * @param  \Illuminate\Database\Query\Builder  $query
           * @param  array  $values
           * @return string
           */
          public function compileInsert(Builder $query, array $values)
          {
              // Essentially we will force every insert to be treated as a batch insert which
              // simply makes creating the SQL easier for us since we can utilize the same
              // basic routine regardless of an amount of records given to us to insert.
              $table = $this->wrapTable($query->from);
      
              if (! is_array(reset($values))) {
                  $values = [$values];
              }
      
              $columns = $this->columnize(array_keys(reset($values)));
      
              // We need to build a list of parameter place-holders of values that are bound
              // to the query. Each insert should have the exact same amount of parameter
              // bindings so we will loop through the record and parameterize them all.
              $parameters = collect($values)->map(function ($record) {
                  return '('.$this->parameterize($record).')';
              })->implode(', ');
      
              return "insert ignore into $table ($columns) values $parameters";
          }
      }
      

      我从框架的类中复制了compileInsert 方法,然后在方法内部,我只将insert 更改为insert ignore。其他一切都保持不变。

      然后,在代码的特定位置,在应用程序(计划任务)中,我需要“插入忽略”,我简单地做了如下操作:

      <?php
      
      use DB;
      use My\Namespace\CustomMySqlGrammar;
      
      class SomeClass
      {
          public function someMethod()
          {
              // Changes "INSERT" to "INSERT IGNORE"
              DB::connection()->setQueryGrammar(new CustomMySqlGrammar());
      
              // et cetera... for example:
              ModelClass::insert($data);
          }
      }
      

      【讨论】:

      • 这是拉德。我只是需要它用于 DB::insert 并将它设置为使用新语法让我继续前进。
      【解决方案7】:

      我终于找到了这个https://github.com/yadakhov/insert-on-duplicate-key,它对我帮助很大

      用户::insertIgnore($users); 这是我正在使用的方法,为其提供行数组及其返回的受影响行

      通过composer安装:composer require yadakhov/insert-on-duplicate-key

      【讨论】:

        【解决方案8】:

        2018 年 Laravel Eloquent 的更新答案

        这也处理多个同时插入(而不是一次一条记录)。

        警告:Eric's comment below 可能是正确的。此代码适用于我过去的项目,但在再次使用此代码之前,我会仔细查看它并添加测试用例并调整功能,直到它始终按预期工作。这可能就像将 TODO 行向下移动到 if 大括号外一样简单。

        要么把它放在你的模型的类中,要么放在你的模型扩展的 BaseModel 类中:

        /**
         * @see https://stackoverflow.com/a/25472319/470749
         * 
         * @param array $arrayOfArrays
         * @return bool
         */
        public static function insertIgnore($arrayOfArrays) {
            $static = new static();
            $table = with(new static)->getTable(); //https://github.com/laravel/framework/issues/1436#issuecomment-28985630
            $questionMarks = '';
            $values = [];
            foreach ($arrayOfArrays as $k => $array) {
                if ($static->timestamps) {
                    $now = \Carbon\Carbon::now();
                    $arrayOfArrays[$k]['created_at'] = $now;
                    $arrayOfArrays[$k]['updated_at'] = $now;
                    if ($k > 0) {
                        $questionMarks .= ',';
                    }
                    $questionMarks .= '(?' . str_repeat(',?', count($array) - 1) . ')';
                    $values = array_merge($values, array_values($array));//TODO
                }
            }
            $query = 'INSERT IGNORE INTO ' . $table . ' (' . implode(',', array_keys($array)) . ') VALUES ' . $questionMarks;
            return DB::insert($query, $values);
        }
        

        这样使用:

        Shop::insertIgnore([['name' =&gt; 'myShop'], ['name' =&gt; 'otherShop']]);

        【讨论】:

        • if($static-&gt;timestamps)不应该只覆盖添加时间戳的部分吗?在当前的代码中,如果 $timestamps 为假,它似乎根本不起作用
        • @eric.itzhak 我将用注释编辑我的答案,因为您可能是对的。
        【解决方案9】:

        避免编写代码的选项是: https://github.com/guidocella/eloquent-insert-on-duplicate-key

        我刚刚对其进行了测试 - 它一次可以处理我的 5000 个插入,有时会出现重复...

        有了它你会得到这些功能:

        User::insertOnDuplicateKey($data);
        User::insertIgnore($data);
        

        【讨论】:

          【解决方案10】:

          回答 Laravel 5.8.33+

          如果现在有人读到这篇文章:不需要任何黑客或查询生成器扩展。查询生成器本身提供了一个 insertOrIgnore 方法来做这件事。

          随便用

          DB::table('tablename')->insertOrIgnore([
              ['column_name' => 'row1', 'column2_name' => 'row1'],
              ['column_name' => 'row2', 'column2_name' => 'row2']
          ]);
          

          See the documentationthe API docs 了解详情。

          【讨论】:

            猜你喜欢
            • 2012-06-13
            • 1970-01-01
            • 2013-06-09
            • 1970-01-01
            • 2013-05-20
            • 1970-01-01
            • 2011-01-14
            • 1970-01-01
            • 2013-03-19
            相关资源
            最近更新 更多