การใช้ 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 ทั้งหมด แต่มีรายการที่ซ้ำกันอยู่ในนั้น

เมื่อค้นดูในตำราอาหาร ฉันพบตัวอย่างโดยใช้คีย์เวิร์ด 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
สิ่งนี้ไม่เพียงทำงานได้อย่างสมบูรณ์แบบสำหรับรายการ แต่ฉันยังใช้คำสั่ง set ในที่อื่นด้วย ขอบคุณสำหรับการแบ่งปันเกี่ยวกับห้องสมุดอันทรงพลังนี้! - 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
@Pyrite ดูเหมือนว่าจะไม่ถูกลบ book.cakephp org/3.0/en/core-libraries/hash.html - person DIDoS; 31.10.2015
comment
@ele ฉันเชื่อว่าฉันคงสับสน คลาส Set ถูกลบออกใน 3.0 (ซึ่งเป็นรุ่นก่อนของคลาส Hash) - person Pyrite; 10.11.2015

ลองใช้ 'จัดกลุ่มตาม' มันทำงานได้อย่างสมบูรณ์แบบ:

$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

ใช่ ปัญหาคือคุณกำลังใช้รายการที่ออกแบบมาสำหรับเอาต์พุต id / ค่า คุณอาจต้องทำการ find('all') แล้วสร้างรายการด้วยตัวเอง

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')
    ));
    // ...
}

จากตัวอย่างโค้ดข้างต้น ผลลัพธ์ vars จะมีลักษณะดังนี้:

$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