【问题标题】:INSERT IGNORE using Codeigniter使用 Codeigniter 插入忽略
【发布时间】:2012-06-13 12:19:07
【问题描述】:

我正在尝试使用 Codeigniter 和 Active Records 向 MySQL 表中插入几行。

PHP 代码

$data = array('......');  // some rows of data to insert
$this->db->insert_batch('my_table', $data);

但是这可能会导致重复的行被插入到表中。为了处理重复数据的插入,如果行是重复的,我计划使用INSERT IGNORE 命令不插入行。

问题:我在 Active Records 中找不到 INSERT IGNORE 等效项,并且不想编辑 Active Record 类。还有其他选择吗?

以下看起来很有趣,但是如果我执行以下操作,查询不会运行两次吗?

$insert_query = $this->db->insert_batch('my_table', $data);  // QUERY RUNS ONCE
$insert_query = str_replace('INSERT INTO','INSERT IGNORE INTO',$insert_query);
$this->db->query($insert_query); // QUERY RUNS A SECOND TIME

【问题讨论】:

  • 阅读所有答案后,让我总结一下 - 没有好的方法可以批量执行此操作:(
  • 我用 MySQL 5.5.42 尝试了 INSERT IGNORE 并且可以工作。但是,即使没有插入任何内容,自动增量也会继续增加。

标签: php codeigniter activerecord


【解决方案1】:

避免在 codeigniter 中插入重复数据是一项棘手的任务。但是您可以使用自定义库方法以非常简单和准确的方式忽略重复值插入。 我在以下位置找到了合适的解决方案: how-to-avoid-duplicate-data-insertion-in-codeigniter-php

【讨论】:

    【解决方案2】:

    如果仍然有人在为 insert_batch 使用忽略而苦苦挣扎,

    检查这条路径,

    system/database/DB_query_builder.php
    

    搜索功能

    protected function _insert_batch
    

    改变

    INSERT INTO => INSERT IGNORE INTO
    

    稍后谢谢我。 :)

    【讨论】:

    • 它可能工作,但更改系统文件不是很酷
    【解决方案3】:

    解决方案:

    $sql = $this->db->set($data)->get_compiled_insert($table);
    $sql = str_replace('INSERT INTO', 'INSERT IGNORE INTO', $sql);
    $this->db->query($sql);
    

    适用于 codeigniter 3.0.6 :)

    【讨论】:

    • 一个非常丑陋的解决方法是一种解决方案;)
    【解决方案4】:

    添加到文件 DB_query_builder.php

    这两个功能

    public function insert_ignore_batch($table = '', $set = NULL, $escape = NULL) {
        if ($set !== NULL)
        {
            $this->set_insert_batch($set, '', $escape);
        }
    
        if (count($this->qb_set) === 0)
        {
            // No valid data array. Folds in cases where keys and values did not match up
            return ($this->db_debug) ? $this->display_error('db_must_use_set') : FALSE;
        }
    
        if ($table === '')
        {
            if ( ! isset($this->qb_from[0]))
            {
                return ($this->db_debug) ? $this->display_error('db_must_set_table') : FALSE;
            }
    
            $table = $this->qb_from[0];
        }
    
        // Batch this baby
        $affected_rows = 0;
        for ($i = 0, $total = count($this->qb_set); $i < $total; $i += 100)
        {
            $this->query($this->_insert_ignore_batch($this->protect_identifiers($table, TRUE, $escape, FALSE), $this->qb_keys, array_slice($this->qb_set, $i, 100)));
            $affected_rows += $this->affected_rows();
        }
    
        $this->_reset_write();
        return $affected_rows;
    }
    
    protected function _insert_ignore_batch($table, $keys, $values) {
        return 'INSERT IGNORE INTO '.$table.' ('.implode(', ', $keys).') VALUES '.implode(', ', $values);
    }
    

    使用:

    $this->db->insert_ignore_batch('TableName', $data);
    

    【讨论】:

    • 这会导致忽略不需要重复的其他错误吗?
    【解决方案5】:

    我也有同样的问题帮助我的是:

    打开:Codeigniter/system/database/DB_query_builder.php

    找到

    protected function _insert_batch($table, $keys, $values)
        {
            return 'INSERT INTO '.$table.' ('.implode(', ', $keys).') VALUES '.implode(', ', $values);
        }
    

    并替换为:

    protected function _insert_batch($table, $keys, $values)
    {
        return 'INSERT IGNORE INTO '.$table.' ('.implode(', ', $keys).') VALUES '.implode(', ', $values);
    }
    

    【讨论】:

    • 这可行,但不是一个很好的解决方案。一旦 CI 更新出现,您不记得这样做,您就会产生问题..
    【解决方案6】:

    这基本上是对 Rocket Hazmat 建议的修改,很好,但没有考虑到 str_replace 对整个字符串进行操作,可能会无意中影响数据。

    $insert_query = $this->db->insert_string('my_table', $data);
    $insert_query = preg_replace('/INSERT INTO/','INSERT IGNORE INTO',$insert_query,1);
    $this->db->query($insert_query);
    

    【讨论】:

      【解决方案7】:

      为此,我制作了这个辅助函数:

      function insert_batch_string($table='',$data=[],$ignore=false){
          $CI = &get_instance();
          $sql = '';
      
          if ($table && !empty($data)){
              $rows = [];
      
              foreach ($data as $row) {
                  $insert_string = $CI->db->insert_string($table,$row);
                  if(empty($rows) && $sql ==''){
                      $sql = substr($insert_string,0,stripos($insert_string,'VALUES'));
                  }
                  $rows[] = trim(substr($insert_string,stripos($insert_string,'VALUES')+6));
              }
      
              $sql.=' VALUES '.implode(',',$rows);
      
              if ($ignore) $sql = str_ireplace('INSERT INTO', 'INSERT IGNORE INTO', $sql);
          }
          return $sql;
      }
      

      它可以做批量插入和批量插入忽略。 为避免重复行,您必须在数据库表中为主字段设置唯一键。

      【讨论】:

        【解决方案8】:

        不强烈推荐,但这里有一个保留批量插入的技巧(这比 Mysql 更有效)

        // try to insert as usual first
        $this->db->insert_batch('my_table', $data);
        
        // if it fails resort to IGNORE
        if($this->db->_error_message())
        {
                $sql = $this->db->last_query();
                $sql = str_replace('INSERT INTO', 'INSERT IGNORE INTO', $sql);
                $this->db->query($sql);
        }
        

        【讨论】:

        • 我正在使用 ajax,但我仍然得到重复错误.. :( 我不想得到重复错误,如果重复存在,只需移动插入忽略..
        • 浏览器上发生的事情不会影响 mysql 的行为。你试过我提出的方法了吗?如果要抑制数据库错误,请调整数据库配置设置。如果您处理错误,则插入应该通过。
        【解决方案9】:

        由于我也遇到了类似的问题,所以我最终选择了像下面这样一个更“优雅”的解决方案。一个完整的 insert_batch 查询,它使用 Rocket 的答案和交易:

        $this->db->trans_start();
        foreach ($items as $item) {
               $insert_query = $this->db->insert_string('table_name', $item);
               $insert_query = str_replace('INSERT INTO', 'INSERT IGNORE INTO', $insert_query);
               $this->db->query($insert_query);
            }
        $this->db->trans_complete();
        

        这也将包装事务中的所有内容,从而产生更快的查询,例如执行 insert_batch()。不如 insert_batch() 快,但比每个条目的单个查询快。我希望它会对某人有所帮助。

        【讨论】:

        • 您还需要在 mysql 配置文件中添加这一行 innodb_autoinc_lock_mode = 0 以防止自动递增计数器即使在忽略条目时也不会递增。
        【解决方案10】:

        对于批量上传,您可能需要更多类似的东西:

        foreach ($data as $data_item) {
            $insert_query = $this->db->insert_string('my_table', $data_item);
            $insert_query = str_replace('INSERT INTO', 'INSERT IGNORE INTO', $insert_query);
            $this->db->query($insert_query);
        }
        

        更新:使用 dcostalis 的版​​本(在这个下面的 cmets 中),它会扩展得更好:-)

        【讨论】:

        • 我用它作为我的问题的快速解决方案(我只插入一个小数组),不确定它在大数组中的扩展效果如何。
        • $sql = ""; foreach ($data as $data_item) { $insert_query = $this-&gt;db-&gt;insert_string('my_table', $data_item); $sql.= str_replace('INSERT INTO', 'INSERT IGNORE INTO', $insert_query); } $this-&gt;db-&gt;query($sql);
        【解决方案11】:

        使用您的第二个想法的技术,您可以通过遍历数组并使用以下方法生成查询:

        $this->db->query($query_string);
        

        【讨论】:

        • 如果你仍然想只运行一条 SQL 命令来进行批量插入,你可以使用下面的代码...$batch_assignment = $this-&gt;db-&gt;set_insert_batch($batch_assignment); $query = $this-&gt;db-&gt;_insert_batch('users_to_tasks', array('task_id', 'user_id'), $this-&gt;db-&gt;ar_set);
        【解决方案12】:

        不要使用insert_batch,因为它实际上运行查询。你要insert_string

        $insert_query = $this->db->insert_string('my_table', $data);
        $insert_query = str_replace('INSERT INTO','INSERT IGNORE INTO',$insert_query);
        $this->db->query($insert_query);
        

        更新:这不适用于批量查询,一次只能一行。

        【讨论】:

        • 谢谢@Hazmat。对于那些想知道是插入还是忽略的人可以使用这个“$this->db->insert_id();”并检查它是否大于零。
        【解决方案13】:

        通过在数据库表中为至少一个字段设置唯一键来避免重复行。

        【讨论】:

        • 这会导致在尝试插入重复行时出现错误吗?
        • 如果您将“调试”的 database.php 配置变量设置为 false,则不会。
        • 仍然产生错误,codeigniter只是忽略它;这不是一个很好的解决方案
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-09-19
        • 1970-01-01
        • 2011-01-14
        • 1970-01-01
        • 1970-01-01
        • 2017-09-08
        • 1970-01-01
        相关资源
        最近更新 更多