Quelle est la bonne façon d'écrire une requête contenant "NOT IN" à l'aide d'une déclaration de condition?
Ma requête est la suivante:
SELECT DISTINCT nid FROM node WHERE language NOT IN
(SELECT language
FROM languages WHERE language = 'ab');
J'ai essayé quelque chose comme ceci:
$query->condition('n.' . $key, $value, 'not in (select language from
languages where language = $value)');
Dans l'exemple spécifique, vous devez simplement écrire la condition comme suit:
$query->condition('n.language', 'ab', '<>');
Dans le cas générique, où vous devez sélectionner les lignes d'une base de données en fonction des valeurs renvoyées par une sous-requête, vous devez considérer ce qui suit:
"NOT IN" est accepté comme opérateur à partir de SelectQuery::condition()
. En fait, la requête suivante serait exécutée:
$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);
}
Comme indiqué dans Clauses conditionnelles ("Sous-sélections"), SelectQuery::condition()
accepte également un objet implémentant SelectQueryInterface
comme valeur pour $value
, Tel que celui retourné par db_select()
; le problème est qu'en fait, vous pouvez simplement l'utiliser lorsque la valeur de $operator
est égale à "IN"
. Voir Les sous-sélections ne fonctionnent pas dans les conditions DBTNG, sauf lorsqu'elles sont utilisées comme valeur pour IN .
La seule façon que je peux voir d'utiliser l'opérateur "NOT IN" avec une sous-requête dans condition
est de:
Exécutez la requête principale en définissant la condition comme dans l'extrait de code suivant
$query->condition($key, $subquery_result, 'NOT IN');
$subquery_result
Est le tableau contenant le résultat de la sous-requête.
Sinon, vous pourriez utiliser where()
comme d'autres l'ont dit, qui accepte une chaîne pour la partie de la requête que vous devez ajouter.
Gardez à l'esprit que db_select()
est plus lent que db_query()
; vous devez utiliser le premier lorsque vous savez que la requête peut être modifiée par d'autres modules. Sinon, si d'autres modules ne sont pas censés utiliser hook_query_alter()
pour modifier votre requête, vous devez utiliser db_query()
.
Dans le cas de l'accès aux nœuds, si vous devez obtenir uniquement les nœuds auxquels un utilisateur a accès, vous devez utiliser db_select()
et ajouter 'node_access'
Comme balise de la requête, avec SelectQuery::addTag()
. Par exemple, blog_page_last()
utilise le code suivant.
$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();
Un code similaire est utilisé par 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();
Lors de l'écriture de requêtes complexes, vous devez absolument utiliser db_query()
au lieu de db_select()
.
NOT IN
Avec une sous-requête avec l'actuelle Drupal (c'est un connaître le problème en cours d'élaboration).db_select()
.db_query()
.Concernant votre requête, je ne sais pas pourquoi vous souhaitez utiliser une sous-requête (sauf si vous avez simplifié votre exemple)? Vous pouvez l'écrire facilement comme ceci:
SELECT nid
FROM node n INNER JOIN languages l ON n.language = l.language
WHERE language NOT IN ('ab')
DISTINCT
n'est pas nécessaire car nid
est une clé primaire et ne sera donc pas dupliquée.
Il y a aussi where () qui permet d'ajouter une condition arbitraire where à la requête.
Exemple:
$query->where('n.language NOT IN (SELECT language FROMlanguages WHERE language = :lang)', array(':lang' => $value));
Comme Keithm l'a mentionné, vous devez utiliser db_select () et addTag ('node_access') lors de la sélection des nœuds qui sont ensuite affichés aux utilisateurs.
Un moyen plus simple d'utiliser db_select avec une sous-sélection NOT IN consiste simplement à utiliser le peu connu
pour ajouter une condition arbitraire where.
par exemple:
// 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);
Où $ subquery_values est un tableau de format $ key => $ nid résultant d'une sous-requête
$query->condition('node.nid', array_values($subquery_values), "NOT IN");
ça fonctionne bien.