web-dev-qa-db-fra.com

Comment utiliser "NOT IN" dans une requête?

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)');
26
JurgenR

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 sous-requête pour obtenir un tableau
  • 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();
38
kiamlaluno

Lors de l'écriture de requêtes complexes, vous devez absolument utiliser db_query() au lieu de db_select() .

  1. Vous ne pouvez pas écrire une clause NOT IN Avec une sous-requête avec l'actuelle Drupal (c'est un connaître le problème en cours d'élaboration).
  2. Si vous n'avez pas besoin que votre requête soit dynamique (donc réécrite par d'autres modules), ne vous embêtez pas essayez d'écrire une requête aussi complexe avec db_select().
  3. Les sous-requêtes ne sont pas encore bien prises en charge (voir un réponse précédente à moi) et si vous avez l'habitude d'écrire SQL, il est beaucoup plus facile d'utiliser 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.

3
tostinni

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.

2
Berdir

Un moyen plus simple d'utiliser db_select avec une sous-sélection NOT IN consiste simplement à utiliser le peu connu

$ query-> où

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);
1
David Thomas

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.

0
Riccardo Ravaro