Есть ли способ получить ассоциативный массив, сгруппированный по значениям указанного столбца с PDO? - PullRequest
24 голосов
/ 19 марта 2011

Например, давайте использовать простой набор данных

+---------+------+------+------------+
| 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) после получения результатов?

Ответы [ 7 ]

53 голосов
/ 27 февраля 2014

Это довольно старая тема, но я нашел очень простое решение:

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

Первый столбец будет установлен как ключ, остальные будут установлены как значение.

Нет необходимостипройтись по массиву или использовать array_map.

13 голосов
/ 03 мая 2011

, чтобы уменьшить ненужный уровень вложенности массива:

$res = $pdo->query('SELECT * FROM employee')->fetchAll(PDO::FETCH_GROUP|PDO::FETCH_ASSOC);
$res = array_map('reset', $res);
11 голосов
/ 24 апреля 2015

Массив ключевых ассоциаций

PDO::FETCH_GROUP|PDO::FETCH_UNIQUE|PDO::FETCH_ASSOC
10 голосов
/ 21 февраля 2018

Принятым ответом является, по сути, код культа груза , который выполняет свою работу только случайно, но сам по себе не имеет смысла.

PDO::FETCH_GROUP и PDO::FETCH_UNIQUEявляются взаимоисключающими режимами выборки , которые нельзя использовать вместе.Только один из них будет работать.Когда вы объединяете их, последний берет верх, и \PDO::FETCH_GROUP|\PDO::FETCH_UNIQUE на самом деле просто PDO::FETCH_UNIQUE.

Помимо этого, вопрос сам по себе неоднозначен, ОП хочет, чтобы его массив был проиндексирован уникальным полем, в то время как он назвал это группировка , что также вызвало споры в ответах.

Итак, чтобы сделать это прямо:

  • index массив с уникальными значениями (если вы хотите, чтобы результирующий массив индексировался по имени сотрудника, если он уникален), режим выборки должен быть PDO :: FETCH_UNIQUE :

    $pdo->query('SELECT * FROM employee')->fetchAll(PDO::FETCH_UNIQUE);
    
  • до группировать результаты (например, если вы хотите сгруппировать сотрудников по отделам), режим выборки должен быть PDO ::FETCH_GROUP :

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

в обоих случаях поле, которое будет использоваться в качестве индекса массива первого уровня, должно быть указано первым в списке полей SELECT.

Записка на PDO::FETCH_ASSOC.Учитывая, что режим извлечения для предпочтительного формата результата может быть установлен в конструкторе один раз для всех, нет смысла также перечислять его явно.

3 голосов
/ 19 марта 2011

Этот ответ устарел, см. этот другой ответ .


Похоже, что нет способа сделать это как часть 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;
}
2 голосов
/ 25 сентября 2013

Мы можем сделать решение Чарльза немного лучше, расширив класс операторов:

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();
0 голосов
/ 20 февраля 2018

Не знаю, почему никто не опубликовал следующее решение, но оно отлично работает для меня:

PDO::FETCH_UNIQUE | PDO::FETCH_ASSOC

Итак, изменение вашего утверждения на:

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

должно быть именно темты хочешь.

...