Как использовать «НЕ В» в запросе?

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)');
JurgenR
источник
Может быть, я упускаю очевидное, но в чем разница между вашим запросом и SELECT nid FROM node WHERE language != 'ab'?
Елин Й.

Ответы:

38

В конкретном примере вы должны просто написать условие как:

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

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

Единственный способ использования оператора 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является первичным ключом, поэтому он не будет дублироваться.

tostinni
источник
2
Что касается # 2, OP выбирает узлы. AFAIK db_select () - единственный способ предоставить любой необходимый тег 'node_access', и в этом случае db_select () будет единственным выбором.
Кит
2

Также есть where (), который позволяет добавить произвольное условие where в запрос.

Пример:

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

Как уже упоминалось, вы должны использовать db_select () и addTag ('node_access') при выборе узлов, которые затем отображаются для пользователей.

Berdir
источник
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");

это работает отлично.

Риккардо Раваро
источник