Як використовувати "NOT IN" у запиті?


26

Який правильний спосіб написати запит, що містить "NOT IN", використовуючи оператор стану?

Мій запит такий:

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', '<>');

У загальному випадку, коли вам потрібно вибрати рядки в базі даних, спираючись на значення, повернуті з підзапиту, слід врахувати, що наступне:

  • "NOT IN" приймається оператором від 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);
    }
  • Як повідомляється в Умовних пунктах ("Підселекти"), SelectQuery::condition()приймає також об'єкт, що реалізує, SelectQueryInterfaceяк значення для $value, такого як повернений db_select(); проблема полягає в тому, що насправді ви можете просто використовувати його, коли значення $operatorдорівнює "IN". Див. Підселектори не працюють в умовах DBTNG, за винятком випадків, коли використовуються як значення для IN .

Єдиний спосіб я бачу використовувати оператор "NOT 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();

Ось приклад підзапросу для спеціального фільтра поглядів, про який я писав: посилання
Roger

1
"Підселектори не працюють в умовах DBTNG, за винятком випадків, коли вони використовуються як значення для IN" зафіксовано в Drupal 8.3
Джонатан

3

При написанні складних запитів ви обов'язково повинні використовувати db_query()замість них db_select().

  1. Ви не можете написати NOT INпункт із підзапитом у поточному API бази даних Drupal (це проблема відомості, яка розробляється).
  2. Якщо вам не потрібно, щоб ваш запит був динамічним (отже, переписали інші модулі), не намагайтеся написати такий складний db_select().
  3. Підзапити ще недостатньо підтримуються (див. Попередню мою відповідь), і якщо ви звикли писати 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, то ОП вибирає вузли. AFAIK db_select () - єдиний спосіб надати необхідний тег 'node_access', і в цьому випадку db_select () буде єдиним вибором.
кейт

2

Там також де (), що дозволяє додати до запиту довільну, де умова.

Приклад:

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

Як зазначалося в кефімі, ви повинні використовувати db_select () та addTag ('node_access') при виборі вузлів, які потім відображатимуться користувачам.


1

Найпростіший спосіб використання db_select з підселером NOT 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");

це чудово працює.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.