¿Cómo uso "NO EN" en una consulta?

26

¿Cuál es la forma correcta de escribir una consulta que contenga 'NOT IN' usando una declaración de condición?

Mi consulta es la siguiente:

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

He intentado algo como lo siguiente:

$query->condition('n.' . $key, $value, 'not in (select language from 
  languages where language = $value)');
JurgenR
fuente
Tal vez me estoy perdiendo lo obvio, pero ¿cuál es la diferencia entre su consulta y SELECT nid FROM node WHERE language != 'ab'?
Елин Й.

Respuestas:

38

En el ejemplo específico, simplemente debe escribir la condición como:

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

En el caso genérico, donde necesita seleccionar las filas en una base de datos basándose en los valores devueltos por una subconsulta, debe considerar lo siguiente:

  • "NOT IN" se acepta como operador de SelectQuery::condition(). De hecho, se ejecutaría la siguiente consulta:

    $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);
    }
  • Como se informa en las cláusulas condicionales ("Subselecciones"), SelectQuery::condition()acepta también un objeto que se implementa SelectQueryInterfacecomo valor para $value, como el devuelto por db_select(); el problema es que en realidad puedes usarlo cuando el valor de $operatores igual a "IN". Consulte las Subselecciones no funcionan en condiciones DBTNG, excepto cuando se usan como valor para IN .

La única forma en que puedo ver el uso del operador "NO EN" con una subconsulta conditiones:

  • Ejecute la subconsulta para obtener una matriz
  • Ejecute la consulta principal estableciendo la condición como en el siguiente fragmento

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

    $subquery_result es la matriz que contiene el resultado de la subconsulta.

De lo contrario, podría usar where()como otros dijeron, lo que acepta una cadena para la parte de la consulta que necesita agregar.

Tenga en cuenta que db_select()es más lento eso db_query(); debe usar el primero cuando sepa que la consulta podría ser alterada por otros módulos. De lo contrario, si se supone que otros módulos no deben usarse hook_query_alter()para alterar su consulta, debe usarlos db_query().
En el caso de acceder a los nodos, si necesita obtener solo los nodos a los que tiene acceso un usuario, debe usar db_select()y agregar 'node_access'como etiqueta de la consulta, con SelectQuery::addTag(). Por ejemplo, blog_page_last()usa el siguiente código.

  $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();

Código similar es utilizado por 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
fuente
Aquí hay un ejemplo de una subconsulta para un filtro personalizado de vistas que he escrito: enlace
Roger
1
"Las subselecciones no funcionan en condiciones DBTNG, excepto cuando se usan como valor para IN" se corrige en Drupal 8.3
Jonathan
3

Al escribir consultas complejas definitivamente debe usar en db_query()lugar de db_select().

  1. No puede escribir una NOT INcláusula con una subconsulta con la API de base de datos de Drupal actual (es un problema conocido que se está resolviendo).
  2. Si no necesita que su consulta sea dinámica (por lo tanto, reescrita por otros módulos), no se moleste en intentar escribir una tan compleja db_select().
  3. Las subconsultas aún no son compatibles (vea una respuesta anterior mía) y si está acostumbrado a escribir SQL, es mucho más fácil de usar db_query().

Con respecto a su consulta, no estoy seguro de por qué desea utilizar una subconsulta (a menos que haya simplificado su ejemplo). Puedes escribirlo fácilmente así:

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

DISTINCTno es necesario, ya que nides una clave principal, por lo que no se duplicará.

tostinni
fuente
2
Con respecto al n. ° 2, el OP está seleccionando nodos. AFAIK db_select () es la única forma de proporcionar cualquier etiqueta 'node_access' requerida, en cuyo caso db_select () sería la única opción.
keithm
2

También hay where () que permite agregar una condición where arbitraria a la consulta.

Ejemplo:

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

Como mencionó keithm, debe usar db_select () y addTag ('node_access') al seleccionar nodos que luego se muestran a los usuarios.

Berdir
fuente
1

Una forma más fácil de usar db_select con una subselección NOT IN es simplemente usar el poco conocido

$ consulta-> donde

para agregar una arbitraria condición where.

p.ej:

  // 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
fuente
0

Donde $ subquery_values ​​es una matriz de $ key => $ nid formato como resultado de una subconsulta

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

funciona bien.

Riccardo Ravaro
fuente