【问题标题】:Is there a way to fetch associative array grouped by the values of a specified column with PDO?有没有办法使用 PDO 获取按指定列的值分组的关联数组?
【发布时间】:2011-07-18 17:12:39
【问题描述】:

例如,让我们使用一些简单的数据集

+---------+------+------+------------+
| name    | age  | sex  | position   |
+---------+------+------+------------+
| Antony  |   34 | M    | programmer |
| Sally   |   30 | F    | manager    |
| Matthew |   28 | M    | designer   |
+---------+------+------+------------+

我们想要得到的是以这种方式组织的数组

Array
(
  [Antony] => Array
    (
      [age] => 34
      [sex] => M
      [position] => programmer
    )

  [Sally] => Array
    (
      [age] => 30
      [sex] => F
      [position] => manager
    )

  [Matthew] => Array
    (
      [age] => 28
      [sex] => M
      [position] => designer
    )
)

作为一个粗略的近似值,我们可以使用

$pdo->query('SELECT * FROM employee')->fetchAll(PDO::FETCH_GROUP|PDO::FETCH_ASSOC);

但是结果我们有不必要的嵌套级别

Array
(
    [Antony] => Array
        (
            [0] => Array
                (
                    [age] => 34
                    [sex] => M
                    [position] => programmer
                )

        )

    [Sally] => Array
        (
            [0] => Array
                (
                    [age] => 30
                    [sex] => F
                    [position] => manager
                )

        )

    [Matthew] => Array
        (
            [0] => Array
                (
                    [age] => 28
                    [sex] => M
                    [position] => designer
                )

        )

)

我试图通过使用回调函数摆脱这种不必要的嵌套层次

$stmt->fetchAll(PDO::FETCH_GROUP|PDO::FETCH_ASSOC|PDO::FETCH_FUNC, 'current');

但由于某些原因它没有通过

Array
  (
   [0] => Array
    (
        [age] => 34
        [sex] => M
        [position] => programmer
    )
  ) 

但只是一堆标量34, 'M', 'programmer' 到回调函数:(

你可以使用回调之类的函数来查看它

function what_do_you_pass_me() {

  $numargs = func_num_args();
  $arg_list = func_get_args();
  for ($i = 0; $i < $numargs; $i++) {
    echo "Argument $i is: " . $arg_list[$i] . "\n";
  };
  echo "\n\n";
};

那么有没有办法在获取结果后使用PDO::FETCH_* 模式而不使用array_map('current', $result) 来获得所需的结果集?

【问题讨论】:

  • 在您的查询中执行分组。
  • $pdo-&gt;query('SELECT * FROM employee GROUP BY name')-&gt;fetchAll(PDO::FETCH_GROUP|PDO::FETCH_ASSOC); 没有帮助。结果是一样的。
  • fetchAll(\PDO::FETCH_GROUP|\PDO::FETCH_UNIQUE) 会这样做。

标签: php arrays pdo fetch


【解决方案1】:

这是一个相当老的话题,但我找到了非常简单的解决方案:

->fetchAll(\PDO::FETCH_GROUP|\PDO::FETCH_UNIQUE)

第一个 col 将被设置为 key,rest 将被设置为 value。

无需遍历数组或使用array_map。

【讨论】:

  • PDO:: 前面的斜线是什么意思?
  • @Gideon 简短的回答是避免任何Namespace 冲突。像这样的常量前面的 \ 将使用全局命名空间。在这种情况下,我们不确定代码将在哪里结束,在常量前面加上 \ 是安全的,以防代码被放入命名空间中的类而不是全局类中。 Stack Overflow 和其他地方的常见做法是省略 \,因为它取决于最终实现。
  • 这应该是正确的答案 - fetchAll 的参数也可以与 PDO::FETCH_OBJ 进行 OR'd 以获得对象数组
  • 包含 FETCH_GROUP 有什么作用?当然,仅使用 FETCH_UNIQUE 就能达到预期的效果——这就是 FETCH_UNIQUE 的全部意义所在。
  • @BradKent 我建议你测试一下。 var_dump((PDO::FETCH_GROUP|PDO::FETCH_UNIQUE) === PDO::FETCH_UNIQUE); // true
【解决方案2】:

减少不必要的嵌套数组级别:

$res = $pdo->query('SELECT * FROM employee')->fetchAll(PDO::FETCH_GROUP|PDO::FETCH_ASSOC);
$res = array_map('reset', $res);

【讨论】:

  • 这样从query 的结果调用fetchAll 是不安全的,因为query 可以返回false。当 PHP 尝试调用非对象上的方法时,这将导致致命错误。
【解决方案3】:

公认的答案本质上是一个货物崇拜代码,它的工作只是偶然,但它本身没有任何意义。

PDO::FETCH_GROUPPDO::FETCH_UNIQUE 是互斥的获取模式,不能一起使用。只有其中一个会起作用。当你将它们结合起来时,后者会接管,\PDO::FETCH_GROUP|\PDO::FETCH_UNIQUE 实际上只是PDO::FETCH_UNIQUE

除此之外,这个问题本身就是模棱两可的,OP 希望他的数组被唯一字段 indexed,而他称之为 grouping 这在答案也是如此。

所以直截了当:

  • index 具有唯一值的数组(当您希望结果数组由员工姓名索引时,假设它们是唯一的),获取模式必须是 PDO::FETCH_UNIQUE:

    $pdo->query('SELECT name, e.* FROM employee e')->fetchAll(PDO::FETCH_UNIQUE);
    
  • 分组结果(例如,当您想按部门对员工进行分组时),获取模式必须是 PDO::FETCH_GROUP:

    $pdo->query('SELECT dept_id, e.* FROM employee e')->fetchAll(PDO::FETCH_GROUP);
    

在这两种情况下,要用作第一级数组索引的字段必须列在 SELECT 字段列表的首位。

关于PDO::FETCH_ASSOC 的注释。鉴于首选结果格式的获取模式可以在构造函数中设置一次,因此也明确列出它是没有意义的。

【讨论】:

    【解决方案4】:

    键关联数组

    PDO::FETCH_GROUP|PDO::FETCH_UNIQUE|PDO::FETCH_ASSOC
    

    【讨论】:

    • 解释: FETCH_ASSOC 获取字段为column_name =&gt; valueGROUP 占用第一列并使其成为结果数组的主键,但它仍然是单项数组数组 (假设结果集中每个主键只有 1 个项目) - UNIQUE 删除不需要的级别,基本上是说“嘿,伙计,每个主键只有 1 个项目 - 整理一下!:)"。
    【解决方案5】:

    此答案已过时,请参阅this other answer


    作为fetchAll 的一部分,似乎没有办法做到这一点。

    最好的办法是创建一个扩展 PDO 的类,并为其添加一个实用方法。

    public function queryKeyedAssoc($query, $params, $key) {
        $sth = $this->prepare($query);
        $sth->execute($params);
        $res = array();
        while($row = $sth->fetch(PDO::FETCH_ASSOC))
            $res[ $row[$key] ] = $row;
        return $res;
    }
    

    【讨论】:

      【解决方案6】:

      我们可以通过扩展语句类来使 Charles 的解决方案更好一点:

      class MyPdo extends PDO {
          function __construct($host, $database_name, $username, $password, $options=array()) {
              $options = self::merge(array(
                  PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
                  PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
                  PDO::ATTR_STATEMENT_CLASS => array('PdoPlusStatement', array()),
                  PDO::ATTR_EMULATE_PREPARES => true,
                  PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8',
              ), $options);
              $dsn = "mysql:host=$host;dbname=$database_name;charset=utf8";
              parent::__construct($dsn, $username, $password, $options);
          }
      }
      
      class PdoPlusStatement extends PDOStatement {
          protected function __construct() {}
      
          /**
           * @param array|mixed $input_parameters An array of values with as many elements as there are bound parameters in the SQL statement being executed, or one or more non-array arguments to be matched with sequential parameter markers.
           * @throws PDOException
           * @return PdoPlusStatement
           */
          public function execute($input_parameters=null) {
              $args = func_get_args();
              $argc = func_num_args();
              if($argc===0) {
                  parent::execute();
              } else {
                  if($argc===1 && is_array($args[0])) {
                      $args = $args[0];
                  }
                  parent::execute($args);
              }
              return $this;
          }
      
          /**
           * Returns an array containing all of the remaining rows in the result set
           * @return array An associative array using the first column as the key, and the remainder as associative values
           */
          public function fetchKeyAssoc() {
              return array_map('reset', $this->fetchAll(PDO::FETCH_GROUP|PDO::FETCH_ASSOC));
          }
      }
      

      用法:

      $users = $pcs->query("SELECT name, user_id, discipline_id FROM wx_user")->fetchKeyAssoc();
      

      【讨论】:

      • @YourCommonSense 是的。你说得对。当我第一次写这篇文章时,我找不到正确的 PDO 提取标志组合。
      【解决方案7】:

      似乎没有人提到过这种变化,因此,为了未来的 Google 员工的利益:

      结合\PDO::FETCH_GROUP\PDO::FETCH_COLUMN 标志。这极大地简化了我的 SQL 语句并返回了我想要的确切结果集。它也很快。

      $this->database->query('SELECT t.fk, t.id FROM my_table t ORDER BY t.fk ASC, t.id ASC')
        ->fetchAll(\PDO::FETCH_GROUP|\PDO::FETCH_COLUMN);
      

      其中t.fkt.id 具有一对多关系。

      我不必担心GROUP BY 语句或 MySQL 对多个字段分组的挑剔处理。最重要的是,我收到了以下形式的结果:

      [
        foreign_key_1 => [
          0 => 11111,
          1 => 22222,
          2 => 33333,
        ],
        foreign_key_2 => [
          0 => 44444,
          1 => 55555,
          2 => 66666,
        ],
        foreign_key_3 => [
          0 => 77777,
          1 => 88888,
          2 => 99999,
        ],
      ];
      

      而不是:

      [      
        foreign_key_1 => [
          0 => [
            id => 11111,
          ],
          1 => [
            id => 22222,
          ],
          2 => [
            id => 33333,
          ],
        ],
        foreign_key_2 => [
          0 => [
            id => 44444,
          ],
          1 => [
            id => 55555,
          ],
          2 => [
            id => 66666,
          ],
        ],
        foreign_key_3 => [
          0 => [
            id => 77777,
          ],
          1 => [
            id => 88888,
          ],
          2 => [
            id => 99999,
          ],
        ],
      
      ];
      

      希望它可以帮助那里的人!


      供参考:https://phpdelusions.net/pdo/fetch_modes

      【讨论】:

      • 您的示例有点令人困惑,因为它只显示了一组,并且看起来像是单独 PDO::FETCH_COLUMN 的结果。我建议添加另一个组只是为了展示它是如何工作的
      • 感谢您的反馈,@YourCommonSense。我已经用更多说明性的代码示例更新了答案。
      【解决方案8】:

      不知道为什么没有人发布以下解决方案,但它对我来说非常有效:

      PDO::FETCH_UNIQUE | PDO::FETCH_ASSOC
      

      因此,将您的声明更改为:

      $pdo->query('SELECT * FROM employee')->fetchAll(PDO::FETCH_UNIQUE|PDO::FETCH_ASSOC);
      

      应该正是你想要的。

      【讨论】:

      • 它会起作用,但它与 OP 中的措辞相矛盾,因为它没有对结果进行分组。此外,PDO::FETCH_ASSOC 是多余的。
      • @YourCommonSense 我相信你在这两个方面都不正确。 1) PDO::FETCH_UNIQUE 对结果进行分组。 2) PDO::FETCH_ASSOC 具有消除数字索引的好处,因此您只剩下关联索引。根据 OP,这是他们正在寻找的确切输出。
      • 相信你还不太明白“组”是什么意思。
      猜你喜欢
      • 2018-02-23
      • 2019-09-17
      • 1970-01-01
      • 1970-01-01
      • 2016-08-22
      • 2014-11-21
      • 1970-01-01
      • 2011-05-05
      • 2020-10-24
      相关资源
      最近更新 更多