【问题标题】:Build a single multiple insert query from an array in PHP从 PHP 中的数组构建单个多重插入查询
【发布时间】:2016-07-20 00:12:25
【问题描述】:

我有一个看起来像这样的数组

$users = array(
                    array('name'=>'aaa','age'=>2),
                    array('name'=>'bbb','age'=>9),
                    array('name'=>'ccc','age'=>7)
               );

我想创建一个函数,该函数将接受上述数组,为单个查询-多次插入创建一个子句,准备一个可以与 PDO 绑定的变量数组。

示例输出:

$clause = INSERT INTO tablename (`name`,`age`) 
          VALUES (:name_0,:age_0),(:name_1,:age_1),(:name_2,:age_2);

然后是上面的值对应的另一组数组:

$params => Array
        (
            [name_0] => aaa
            [age_0] => 2
            [name_1] => bbb
            [age_1] => 9
            [name_2] => ccc
            [age_2] => 7
        );

以便可以像这样执行它:

$prepared = $connection->prepare($clause);
$prepared->execute($params);

是否有可能在单个函数中实现这一点?

【问题讨论】:

    标签: php arrays function pdo


    【解决方案1】:

    此函数需要表名、您的原始数组和一个用作默认值的可选参数,前提是所有数组行中都不存在一个字段:

    function buildQuery( $table, $array, $default='NULL' )
    {
        /* Retrieve complete field names list: */
        $fields = array();
        foreach( $array as $row ) $fields = array_merge( $fields, array_keys( $row ) );
        $fields = array_unique( $fields );
    
        /* Analize each array row, then update parameters and values chunks: */
        $values = $params = array();
        foreach( $array as $key => $row )
        {
            $line = array();
            foreach( $fields as $field )
            {
                if( !isset( $row[$field] ) )
                { $line[] = $default; }
                else
                {
                    $line[] = ":{$field}_{$key}";
                    $params["{$field}_{$key}"] = $row[$field];
                }
            }
            $values[] = '('.implode(',',$line).')';
        }
    
        /* Compone MySQL query: */
        $clause = sprintf
        (
            "INSERT INTO `%s` (`%s`) VALUES %s;",
            $table,
            implode( '`,`', $fields ),
            implode( ',', $values )
        );
    
        /* Return array[ clause, params ]: */
        return compact( 'clause', 'params' );
    }
    

    这样调用:

    $query = buildQuery( 'mytable', $users );
    

    $query 将包含以下内容:

    Array
    (
        [clause] => INSERT INTO `mytable` (`name`,`age`) VALUES (:name_0,:age_0),(:name_1,:age_1),(:name_2,:age_2);
        [params] => Array
            (
                [name_0] => aaa
                [age_0] => 2
                [name_1] => bbb
                [age_1] => 9
                [name_2] => ccc
                [age_2] => 7
            )
    
    )
    

    eval.in demo

    【讨论】:

      【解决方案2】:

      您可以通过创建 QueryBuilderPDOStatementDecorator 以 OOP 样式执行此操作,如下所示:

      class QueryBuilder
      {
          const BUILD_TYPE_INSERT_MULTIPLE = 'INSERT_MULTIPLE';
      
          protected $table;
          protected $values;
          protected $buildType;
      
          public function __construct($table)
          {
              $this->table = $table;
          }
      
          public static function onTable($table)
          {
              return new self($table);
          }
      
          public function insertMultiple(Array $values = array())
          {
              $this->values = $values;
      
              $this->buildType = self::BUILD_TYPE_INSERT_MULTIPLE;
      
              return $this;
          }
      
          public function build()
          {
              switch ($this->buildType) {
                  case self::BUILD_TYPE_INSERT_MULTIPLE:
                      return $this->buildInsertMultiple();
              }
          }
      
          protected function buildInsertMultiple()
          {
              $fields = array_keys($this->values[0]);
      
              $query = "INSERT INTO {$this->table} (" . implode(',', $fields) . ") VALUES ";
      
              $values = array();
      
              for ($i = 0; $i < count($fields); $i++) {
                  $values[] = '(' . implode(', ', array_map(function($field) use ($i) {
                      return ':' . $field . $i;
                  }, $fields)) . ')';
              }
      
              $query .= implode(', ', $values);
      
              return $query;
          }
      }
      
      class PDOStatementDecorator
      {
          protected $pdoStatement;
      
          public function __construct(PDOStatement $pdoStatement)
          {
              $this->pdoStatement = $pdoStatement;
          }
      
          public function executeMultiple(Array $bindsGroup = array())
          {
              $binds = array();
      
              for ($i = 0; $i < count($bindsGroup); $i++) {
                  foreach ($bindsGroup[$i] as $key => $value) {
                      $binds[$key . $i] = $value;
                  }
              }
      
              return $this->execute($binds);
          }
      
          public function execute(Array $inputParemeters)
          {
              return $this->pdoStatement->execute($inputParemeters);
          }
      
          public function fetch($fetchStyle = null, $cursorOrientation = 'PDO::FETCH_ORI_NEXT', $cursorOffset = 0)
          {
              return $this->pdoStatement->fetch($fetchStyle, $cursorOrientation, $cursorOffset);
          }
      
          /**
           * TODO
           * Implement all public PDOStatement methods 
           */
      }
      

      可以增强查询构建器,使其能够为更新/删除语句构建查询。

      现在用法会很简单:

      $users = array(
          array('name' => 'aaa', 'age' => 2),
          array('name' => 'bbb', 'age' => 9),
          array('name' => 'ccc', 'age' => 7),
      );
      
      $query = QueryBuilder::onTable('users')->insertMultiple($users)->build();
      
      $stmt = new PDOStatementDecorator($pdo->prepare($query));
      
      $stmt->executeMultiple($users);
      

      【讨论】:

        【解决方案3】:

        是的,很有可能,我为我的自定义查询构建器类做了完全相同的事情:

        function INSERT_MULTIPLE_QUERY($ARRS = array()){
        
                   $raw_cols = '(`';
        
                   // PREPARE THE COLUMNS 
                   foreach($ARRS[0] as $key1 => $value):
                       $raw_cols .= $key1.'`,`'; 
                   endforeach;
                   $final_cols = rtrim($raw_cols,'`,`') . '`)';
                   $ctr1=0;  $raw_vals='';
        
                   // PREPARE THE VALUES
                   foreach($ARRS as $ARR_VALUE):
                       $raw_vals .= '(';
                       foreach($ARR_VALUE as $key => $value): $raw_vals .= ':'.$key.'_'.$ctr1.','; endforeach;
                       $raw_vals  = rtrim($raw_vals,',');
                       $raw_vals .= '),';
                       $ctr1++;
                   endforeach;
                   $final_vals = rtrim($raw_vals,',');
                   $ctr2 = 0; $param = array();
        
                   // PREPARE THE PARAMETERS
                   foreach($ARRS as $ARR_PARAM):
                       foreach($ARR_PARAM as $key_param => $value_param):$param[$key_param.'_'.$ctr2] = $value_param; endforeach;
                       $ctr2++;
                   endforeach;
        
                   // PREPARE THE CLAUSE 
                   $clause = 'INSERT INTO tablename '  . $final_cols . ' VALUES ' . $final_vals;
        
                   // RETURN THE CLAUSE AND THE PARAMETERS 
                   $return['clause'] = $clause;
                   $return['param']  = $param;
        
                   return $return; 
                }
        

        现在使用这个功能:

        $query = INSERT_MULTIPLE_QUERY($users); 
             //  $users is your example array above
        

        然后:

        $prepared = $connection->prepare($query['clause']);
        $prepared->execute($query['param']);
        

        【讨论】:

        • 我自己的查询构建器类也是如此。
        • 太棒了!那我可以投赞成票和正确答案吗?
        猜你喜欢
        • 2018-01-03
        • 2011-10-26
        • 2013-04-29
        • 1970-01-01
        • 2015-09-07
        • 2019-02-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多