Как мне использовать "НЕ В" в запросе?
Как правильно написать запрос, содержащий "НЕ В", используя оператор условия?
Мой запрос следующий:
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)');
5 answers
В конкретном примере вы должны просто записать условие как:
$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); }
Как сообщается в Условных предложениях ("Подзапросы"),
SelectQuery::condition()
также принимает объект реализацияSelectQueryInterface
в качестве значения для$value
, такого как значение, возвращаемоеdb_select()
; проблема в том, что на самом деле вы можете просто использовать его, когда значение$operator
равно"IN"
. См. Подзапросы не работают в условиях DBTNG, за исключением случаев, когда они используются в качестве значения для В.
Единственный способ, который я вижу, чтобы использовать оператор "НЕ В" с подзапросом в 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();
При написании сложных запросов вам обязательно следует использовать db_query()
вместо того, чтобы db_select()
.
- Вы не можете написать предложение
NOT IN
с подзапросом с текущим API базы данных Drupal (это известная проблема , которая разрабатывается). - Если вам не нужно, чтобы ваш запрос был динамическим (следовательно, переписанным другими модулями), не беспокойтесь , пытаясь написать такой сложный с
db_select()
. - Подзапросы еще недостаточно поддерживаются (см. мой предыдущий ответ ), и если вы привыкли писать SQL, его намного проще использовать
db_query()
.
Что касается вашего запроса, я не уверен, почему вы хотите использовать подзапрос (если только вы не упростили свой пример)? Вы можете легко написать это так:
SELECT nid
FROM node n INNER JOIN languages l ON n.language = l.language
WHERE language NOT IN ('ab')
DISTINCT
не обязательно, так как nid
является первичным ключом, поэтому он не будет дублироваться.
Также есть where(), который позволяет добавить в запрос произвольное условие where.
Пример:
$query->where('n.language NOT IN (SELECT language FROMlanguages WHERE language = :lang)', array(':lang' => $value));
Как упоминал Кейтм, вы должны использовать db_select() и addTag('node_access') при выборе узлов, которые затем отображаются пользователям.
Более простой способ использовать db_select с НЕ В подвыборке - это просто использовать малоизвестный
Для добавления произвольного условия where.
Например:
// 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);
Где $subquery_values - это массив формата $key => $nid в результате подзапроса
$query->condition('node.nid', array_values($subquery_values), "NOT IN");
Это прекрасно работает.