ฉันจะใช้คำว่า "ไม่ได้" ในแบบสอบถามได้อย่างไร


26

เป็นวิธีที่เหมาะสมในการเขียนแบบสอบถามที่มี 'ไม่ใน' โดยใช้คำสั่งเงื่อนไขคืออะไร?

แบบสอบถามของฉันเป็นหนึ่งต่อไปนี้:

SELECT DISTINCT nid FROM node WHERE language NOT IN 
  (SELECT language 
    FROM languages WHERE language = 'ab');

ฉันได้ลองทำสิ่งต่อไปนี้แล้ว:

$query->condition('n.' . $key, $value, 'not in (select language from 
  languages where language = $value)');

บางทีฉันอาจจะหายไปอย่างชัดเจน แต่สิ่งที่แตกต่างระหว่างการค้นหาของคุณและSELECT nid FROM node WHERE language != 'ab'?
ЕлинЙ

คำตอบ:


38

ในตัวอย่างเฉพาะคุณควรเขียนเงื่อนไขดังนี้:

$query->condition('n.language', 'ab', '<>');

ในกรณีทั่วไปที่คุณต้องเลือกแถวในฐานข้อมูลโดยยึดตามค่าที่ส่งคืนจากแบบสอบถามย่อยคุณควรพิจารณาสิ่งต่อไปนี้:

  • "ไม่อยู่ใน" SelectQuery::condition()ได้รับการยอมรับเป็นผู้ประกอบการจาก อันที่จริงแล้วแบบสอบถามต่อไปนี้จะถูกดำเนินการ:

    $query = db_select('node', 'n')->fields('n');
    $query->condition('n.nid', array(1, 2, 3), 'NOT IN');
    $nodes = $query->execute();
    
    foreach ($nodes as $node) {
      dsm($node->nid);
    }
  • ตามที่รายงานไว้ในข้อเงื่อนไข ("Subselects") SelectQuery::condition()ยอมรับวัตถุที่ใช้SelectQueryInterfaceเป็นค่า$valueเช่นคืนโดยdb_select(); ปัญหาคือว่าจริง ๆ แล้วคุณก็สามารถใช้มันเมื่อค่าของมีค่าเท่ากับ$operator "IN"ดูsubselects จะไม่ได้ทำงานในสภาพ DBTNG ยกเว้นเมื่อใช้เป็นค่าสำหรับ IN

วิธีเดียวที่ฉันสามารถเห็นการใช้ตัวดำเนินการ "ไม่ได้อยู่ใน" กับแบบสอบถามย่อยในconditionคือ:

  • ดำเนินการแบบสอบถามย่อยเพื่อรับอาร์เรย์
  • เรียกใช้งานเคียวรีหลักเพื่อตั้งเงื่อนไขเช่นเดียวกับข้อมูลโค้ดต่อไปนี้

    $query->condition($key, $subquery_result, 'NOT IN');

    $subquery_result เป็นอาร์เรย์ที่มีผลลัพธ์ของแบบสอบถามย่อย

มิฉะนั้นคุณสามารถใช้where()ตามที่คนอื่นพูดซึ่งยอมรับสตริงสำหรับส่วนของแบบสอบถามที่คุณต้องการเพิ่ม

โปรดจำไว้ว่าdb_select()ช้าลงdb_query(); คุณควรใช้คำสั่งแรกเมื่อคุณรู้ว่าอาจมีการเปลี่ยนแปลงโมดูลอื่น ๆ มิฉะนั้นถ้าโมดูลอื่น ๆ ไม่ควรที่จะใช้ในการปรับเปลี่ยนการค้นหาของคุณคุณควรใช้hook_query_alter() ในกรณีของการเข้าถึงโหนดถ้าคุณต้องการที่จะได้รับเพียงโหนดที่ผู้ใช้มีการเข้าถึงจากนั้นคุณจำเป็นต้องใช้และเพิ่มเป็นแท็กของแบบสอบถามที่มี ตัวอย่างเช่นใช้รหัสต่อไปนี้db_query()
db_select()'node_access'SelectQuery::addTag()blog_page_last()

  $query = db_select('node', 'n')->extend('PagerDefault');
  $nids = $query
  ->fields('n', array('nid', 'sticky', 'created'))
    ->condition('type', 'blog')
    ->condition('status', 1)
    ->orderBy('sticky', 'DESC')
    ->orderBy('created', 'DESC')
    ->limit(variable_get('default_nodes_main', 10))
    ->addTag('node_access')
    ->execute()
    ->fetchCol();

book_block_view()รหัสที่คล้ายกันจะถูกใช้โดย

$select = db_select('node', 'n')
  ->fields('n', array('title'))
  ->condition('n.nid', $node->book['bid'])
  ->addTag('node_access');
$title = $select->execute()->fetchField();

นี่คือตัวอย่างของแบบสอบถามย่อยสำหรับตัวกรองที่กำหนดเองของมุมมองที่ฉันเขียนไว้: link
Roger

1
"Subselects ไม่ทำงานในเงื่อนไข DBTNG ยกเว้นเมื่อใช้เป็นค่า IN" ได้รับการแก้ไขใน Drupal 8.3
Jonathan

3

เมื่อมีการเขียนคำสั่งที่ซับซ้อนคุณแน่นอนควรจะใช้แทนdb_query()db_select()

  1. คุณไม่สามารถเขียนNOT INคำสั่งย่อยด้วยแบบสอบถามย่อยด้วย API ฐานข้อมูล Drupal ปัจจุบันได้ (เป็นปัญหาที่ทราบแล้ว)
  2. ถ้าคุณไม่จำเป็นต้องค้นหาของคุณจะเป็นแบบไดนามิก (เขียนใหม่ด้วยเหตุนี้โดยโมดูลอื่น ๆ ) ไม่รำคาญdb_select()พยายามที่จะเขียนดังกล่าวเป็นหนึ่งในที่ซับซ้อนด้วย
  3. subqueries จะไม่ได้รับการสนับสนุนอย่างดีเลย (ดูคำตอบก่อนหน้าของฉัน) และถ้าคุณใช้ในการเขียน SQL db_query()มันวิธีที่ง่ายต่อการใช้งาน

เกี่ยวกับคำค้นหาของคุณฉันไม่แน่ใจว่าทำไมคุณต้องการใช้แบบสอบถามย่อย (เว้นแต่ว่าคุณทำให้ตัวอย่างง่ายขึ้น) คุณสามารถเขียนได้อย่างง่ายดายเช่นนี้:

SELECT nid 
FROM node n INNER JOIN languages l ON n.language = l.language
WHERE language NOT IN ('ab')

DISTINCTไม่จำเป็นเนื่องจากnidเป็นคีย์หลักดังนั้นจึงไม่สามารถทำซ้ำได้


2
เกี่ยวกับ # 2 OP คือการเลือกโหนด AFAIK db_select () เป็นวิธีเดียวที่จะจัดเตรียมแท็ก 'node_access' ที่จำเป็นใด ๆ ซึ่งในกรณีนี้ db_select () จะเป็นตัวเลือกเท่านั้น
keithm

2

นอกจากนี้ยังมีที่ ()ซึ่งช่วยให้สามารถเพิ่มเงื่อนไขในการค้นหาโดยพลการ

ตัวอย่าง:

$query->where('n.language NOT IN (SELECT language FROMlanguages WHERE language = :lang)', array(':lang' => $value));

ตามที่กล่าวถึง keithm คุณต้องใช้ db_select () และ addTag ('node_access') เมื่อเลือกโหนดที่จะแสดงต่อผู้ใช้


1

วิธีที่ง่ายกว่าในการใช้ db_select ด้วยการเลือกย่อย IN IN คือการใช้สิ่งเล็กน้อยที่รู้จัก

$ query-> ที่

เพื่อเพิ่มเงื่อนไขโดยพลการ

เช่น:

  // Count query for users without rid 3
  $query = db_select('users', 'u');
  $query->fields('u', array('uid'));
  $query->where('u.uid NOT IN(select uid from {users_roles} where rid = :rid)', array(':rid' => 3));  
  $count = $query->countQuery()->execute()->fetchField();
  drupal_set_message($count);

0

โดยที่ $ subquery_values ​​เป็นอาร์เรย์ของ $ key => $ nid รูปแบบอันเป็นผลมาจากแบบสอบถามย่อย

$query->condition('node.nid', array_values($subquery_values), "NOT IN");

มันใช้งานได้ดี

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.