Wie verwende ich "NOT IN" in einer Abfrage?

26

Wie schreibe ich eine Abfrage, die 'NOT IN' enthält, mit einer Bedingungsanweisung?

Meine Frage ist die folgende:

SELECT DISTINCT nid FROM node WHERE language NOT IN 
  (SELECT language 
    FROM languages WHERE language = 'ab');

Ich habe Folgendes versucht:

$query->condition('n.' . $key, $value, 'not in (select language from 
  languages where language = $value)');
JurgenR
quelle
Vielleicht fehlt mir das Offensichtliche, aber was ist der Unterschied zwischen Ihrer Anfrage und SELECT nid FROM node WHERE language != 'ab'?
Елин Й.

Antworten:

38

Im konkreten Beispiel schreiben Sie die Bedingung einfach wie folgt:

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

Im allgemeinen Fall, in dem Sie die Zeilen in einer Datenbank basierend auf den von einer Unterabfrage zurückgegebenen Werten auswählen müssen, sollten Sie Folgendes berücksichtigen:

  • "NOT IN" wird als Operator von akzeptiert SelectQuery::condition(). Tatsächlich würde die folgende Abfrage ausgeführt:

    $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);
    }
  • Akzeptiert , wie in den Bedingungsklauseln ("Unterauswahlen") angegeben, SelectQuery::condition()auch ein Objekt, das SelectQueryInterfaceals Wert für implementiert ist $value, z. B. das von zurückgegebene Objekt db_select(). Das Problem ist, dass Sie es tatsächlich nur verwenden können, wenn der Wert von $operatorgleich ist "IN". Siehe Unterauswahlen funktionieren nicht unter DBTNG-Bedingungen, außer wenn sie als Wert für IN verwendet werden .

Die einzige Möglichkeit, den Operator "NOT IN" mit einer Unterabfrage in zu verwenden, conditionbesteht darin,

  • Führen Sie die Unterabfrage aus, um ein Array abzurufen
  • Führen Sie die Hauptabfrage aus, indem Sie die Bedingung wie im folgenden Snippet festlegen

    $query->condition($key, $subquery_result, 'NOT IN');

    $subquery_result ist das Array, das das Ergebnis der Unterabfrage enthält.

Andernfalls könnten Sie, where()wie bereits erwähnt, eine Zeichenfolge für den Teil der Abfrage verwenden, den Sie hinzufügen müssen.

Denken Sie daran, dass db_select()das langsamer ist db_query(); Sie sollten die erste verwenden, wenn Sie wissen, dass die Abfrage von anderen Modulen geändert werden kann. Andernfalls sollten Sie verwenden, wenn andere Module nicht hook_query_alter()zum Ändern Ihrer Abfrage verwendet werden sollen db_query().
Wenn Sie beim Zugriff auf Knoten nur die Knoten abrufen müssen, auf die ein Benutzer Zugriff hat, müssen Sie mit als Tag der Abfrage verwenden db_select()und hinzufügen . Zum Beispiel verwendet den folgenden Code.'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();

Ein ähnlicher Code wird von verwendet 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();
kiamlaluno
quelle
Hier ist ein Beispiel einer Unterabfrage für einen benutzerdefinierten Filter für Ansichten, den ich geschrieben habe: link
Roger
1
"Unterauswahlen funktionieren nicht unter DBTNG-Bedingungen, außer wenn sie als Wert für IN verwendet werden" ist in Drupal 8.3
Jonathan,
3

Wenn Sie komplexe Abfragen schreiben, sollten Sie auf jeden Fall db_query()statt verwenden db_select().

  1. Sie können NOT INmit der aktuellen Drupal-Datenbank-API keine Klausel mit einer Unterabfrage schreiben (es ist ein bekanntes Problem, das derzeit bearbeitet wird).
  2. Wenn Sie nicht möchten, dass Ihre Abfrage dynamisch ist (daher von anderen Modulen umgeschrieben wird), versuchen Sie nicht , eine so komplexe Abfrage mit zu schreiben db_select().
  3. Unterabfragen werden noch nicht gut unterstützt (siehe meine vorherige Antwort ), und wenn Sie mit dem Schreiben von SQL vertraut sind, ist die Verwendung wesentlich einfacher db_query().

Bezüglich Ihrer Anfrage bin ich mir nicht sicher, warum Sie eine Unterabfrage verwenden möchten (es sei denn, Sie haben Ihr Beispiel vereinfacht). Sie können es leicht so schreiben:

SELECT nid 
FROM node n INNER JOIN languages l ON n.language = l.language
WHERE language NOT IN ('ab')

DISTINCTist nicht notwendig, da nides sich um einen Primärschlüssel handelt, damit er nicht dupliziert wird.

tostinni
quelle
2
In Bezug auf # 2 wählt das OP Knoten aus. AFAIK db_select () ist die einzige Möglichkeit, ein erforderliches 'node_access'-Tag bereitzustellen. In diesem Fall wäre db_select () die einzige Möglichkeit.
Keithm
2

Es gibt auch where () , mit dem der Abfrage eine beliebige where-Bedingung hinzugefügt werden kann.

Beispiel:

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

Wie bereits erwähnt, müssen Sie db_select () und addTag ('node_access') verwenden, um Knoten auszuwählen, die den Benutzern angezeigt werden.

Berdir
quelle
1

Eine einfachere Möglichkeit, db_select mit einer NOT IN-Unterauswahl zu verwenden, besteht darin, nur die wenig bekannten zu verwenden

$ query-> where

eine beliebige where-Bedingung hinzufügen.

z.B:

  // 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);
David Thomas
quelle
0

Dabei ist $ subquery_values ​​ein Array des Formats $ key => $ nid als Ergebnis einer Unterabfrage

$query->condition('node.nid', array_values($subquery_values), "NOT IN");

es funktioniert gut.

Riccardo Ravaro
quelle