Использование DISTINCT в функции поиска CakePHP

Я пишу приложение CakePHP 1.2. У меня есть список людей, которых я хочу, чтобы пользователь мог фильтровать по разным полям. Для каждого фильтруемого поля у меня есть раскрывающийся список. Выберите комбинацию фильтров, щелкните фильтр, и на странице будут показаны только соответствующие записи.

В people_controller у меня есть этот фрагмент кода:

$first_names = $this->Person->find('list', array(
    'fields'=>'first_name',
    'order'=>'Person.first_name ASC',
    'conditions'=> array('Person.status'=>'1')
));
$this->set('first_names', $first_names);

(Статус = 1, потому что я использую обратимое удаление.)

Это создает упорядоченный список всех имен first_names. Но там есть дубликаты.

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

$first_names = $this->Person->find('list', array(
    'fields'=>'DISTINCT first_name',
    'order'=>'Person.first_name ASC',
    'conditions'=> array('Person.status'=>'1')
));

Это дает мне ошибку SQL, подобную этой:

Query: SELECT `Person`.`id`, DISTINCT `Person`.` first_name` FROM `people` AS `Person`   WHERE `Person`.`status` = 1   ORDER BY `Person`.`first_name` ASC

Проблема очевидна. Фреймворк добавляет в запрос Person.id. Я подозреваю, что это происходит из-за использования «списка».

Я буду использовать выбранный фильтр для создания оператора SQL при нажатии кнопки фильтра. Мне не нужно поле is, но я не могу от него избавиться.

Спасибо, Фрэнк Люк.


person Frank Luke    schedule 11.11.2009    source источник


Ответы (12)


Вы правы, кажется, что вы не можете использовать DISTINCT с list. Поскольку вам не нужны id, а только имена, вы можете использовать find all, как указано выше, а затем $first_names = Set::extract($first_names, '/Person/first_name');. Это даст вам массив с разными именами.

person Marko    schedule 11.11.2009
comment
Это отлично сработало не только для списка, но и в других местах. Спасибо, что поделились информацией об этой мощной библиотеке! - person Frank Luke; 20.11.2009
comment
Я пробовал это, но я все еще нахожу дубликаты в результирующем списке. - person paullb; 28.06.2012
comment
@paullb вы можете удалить дубликаты из извлеченного массива с помощью array_unique. И пока вы этим занимаетесь, взгляните на Hash class (начиная с CakePHP 2.2), который со временем заменит Set. - person Jari Keinänen; 03.07.2012
comment
@koiyu Это правда, но предоставленный ответ не удалял дубликаты, как было указано в вопросе. (функция DISTINCT) - person paullb; 05.07.2012
comment
@ele Кажется, я запутался; класс Set был удален в 3.0 (который был предшественником класса Hash). - person Pyrite; 10.11.2015

Попробуйте использовать 'group by', он отлично работает:

$first_names = $this->Person->find('list', array(
  'fields'=>'first_name',
   'order'=>'Person.first_name ASC',
   'conditions'=> array('Person.status'=>'1'),
   'group' => 'first_name'));
person Kielanas    schedule 20.11.2009
comment
Это работает, но имеет непредвиденные последствия сортировки списка по first_name, что может быть нежелательно. - person paullb; 28.06.2012
comment
@paullb У меня тоже есть эта проблема. Есть ли решение проблемы с заказом? - person trante; 28.08.2012

Вы можете попробовать это. Здесь в качестве ключа используется идентификатор человека, поэтому дубликаты записей исключены.

    $first_names = $this->Person->find('list', array(
    'fields' => array('id','first_name'),
    'conditions' => array('Person.status' => '1'),
   ));
    $this->set('first_names', $first_names);
person shravan uchil    schedule 23.01.2014

Вот как я это сделал в CakePHP 3.x:

     $query = $this->MyTables->find('all');
     $result = $query->distinct()->toArray();
person Dawoodjee    schedule 27.08.2015

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

$first_names = $this->Person->find('list', array(
    'fields' => 'first_name',
    'order' => 'first_name',
    'group' => 'first_name',
    'conditions' => array('Person.status' => '1'),
));
$this->set('first_names', $first_names);
person Synexis    schedule 19.06.2013

Я знаю, что это вопрос для CakePHP 1.2, но я искал это и в CakePHP версии 3. И в этой версии есть метод для формирования запроса в отдельный:

$first_names = $this->Persons->find(
    'list',
    [
        'fields'=> ['name']
    ]
)
  ->distinct();
;

Это сгенерирует запрос sql следующим образом:

SELECT DISTINCT Persons.name AS `Persons__name` FROM persons Persons

Но метод distinct немного мощнее, чем просто вставка DISTINCT в запрос.

Если вы хотите просто выделить результат в одном поле, поэтому просто отбрасываете строку с повторяющимся именем, вы также можете использовать отдельный метод с массивом полей в качестве параметра:

$first_names = $this->Persons->find(
    'list',
    [
        'fields'=> ['id', 'name'] //it also works if the field isn't in the selection
    ]
)
  ->distinct(['name']); //when you use more tables in the query, use: 'Persons.name'
;

Это будет общий запрос sql, подобный этому (наверняка это групповой запрос):

SELECT DISTINCT
    Persons.id AS `Persons__id`, Persons.name AS `Persons__name`
FROM persons Persons
GROUP BY name #respectively Persons.name

Надеюсь, я помогу некоторым для CakePHP 3. :)

person TheFRedFox    schedule 04.06.2015

Да, проблема в том, что вы используете листинг, предназначенный для вывода идентификатора/значения. Вам, вероятно, придется найти («все»), а затем составить список самостоятельно.

person PetersenDidIt    schedule 11.11.2009

Да, я также пытался получить уникальные результаты с помощью «списка», но это не работает. Затем я исправил проблему, используя «все».

person Muthukumaran    schedule 12.11.2009

В примере теперь в книге для версии 2 это заявляет следующее:

public function some_function() {
    // ...
    $justusernames = $this->Article->User->find('list', array(
        'fields' => array('User.username')
    ));
    $usernameMap = $this->Article->User->find('list', array(
        'fields' => array('User.username', 'User.first_name')
    ));
    $usernameGroups = $this->Article->User->find('list', array(
        'fields' => array('User.username', 'User.first_name', 'User.group')
    ));
    // ...
}

В приведенном выше примере кода результирующие переменные будут выглядеть примерно так:

$justusernames = Array
(
    //[id] => 'username',
    [213] => 'AD7six',
    [25] => '_psychic_',
    [1] => 'PHPNut',
    [2] => 'gwoo',
    [400] => 'jperras',
)

$usernameMap = Array
(
    //[username] => 'firstname',
    ['AD7six'] => 'Andy',
    ['_psychic_'] => 'John',
    ['PHPNut'] => 'Larry',
    ['gwoo'] => 'Gwoo',
    ['jperras'] => 'Joël',
)

$usernameGroups = Array
(
    ['User'] => Array
    (
        ['PHPNut'] => 'Larry',
        ['gwoo'] => 'Gwoo',
    )

    ['Admin'] => Array
    (
        ['_psychic_'] => 'John',
        ['AD7six'] => 'Andy',
        ['jperras'] => 'Joël',
    )

)

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

person Andrew Myers    schedule 12.04.2013

В некоторых случаях вы хотите сгруппировать по какому-либо ключу, но вам нужен уникальный элемент в результатах. Например, у вас есть приложение-календарь с двумя типами событий. Одно событие в 1-й день, а другое во 2-й день месяца 1. И вы хотите показать или, вернее, подсчитать все события, сгруппированные по дням и по типам. Если вы используете только DISTINCT, это довольно сложно. Самое простое решение — сгруппировать дважды:

$this->Events->virtualFields['count'] = 'COUNT(*)';                   
$acts  = $this->Activity->find('all',array(                   
             'group' => array('DAY(Event.start)','EventType.id'),
               ));
person gdm    schedule 03.04.2014

Просто сгруппируйте по полям, которые вы хотите выделить... или используйте Set::extract(), а затем array_unique()

person Eric Wong    schedule 03.12.2013

В версии 2.7-RC это работает

$this->Model1->find('list', array(
      'fields' => array('Model1.field1', Model1.field1')
));
person Joshua H    schedule 04.07.2015