Bagaimana cara menggunakan "TIDAK DI" dalam kueri?

26

Apa cara yang tepat untuk menulis kueri yang berisi 'TIDAK DI' menggunakan pernyataan kondisi?

Permintaan saya adalah sebagai berikut:

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

Saya sudah mencoba sesuatu seperti berikut:

$query->condition('n.' . $key, $value, 'not in (select language from 
  languages where language = $value)');
JurgenR
sumber
Mungkin saya melewatkan yang jelas, tetapi apa perbedaan antara permintaan Anda dan SELECT nid FROM node WHERE language != 'ab'?
Елин Й.

Jawaban:

38

Dalam contoh spesifik, Anda cukup menuliskan kondisinya sebagai:

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

Dalam kasus umum, di mana Anda harus memilih baris dalam basis data berdasarkan nilai yang dikembalikan dari sub-kueri, Anda harus mempertimbangkan apa yang berikut:

  • "NOT IN" diterima sebagai operator dari SelectQuery::condition(). Bahkan, permintaan berikut akan dieksekusi:

    $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);
    }
  • Seperti yang dilaporkan dalam klausa Bersyarat ("Subselects"), SelectQuery::condition()menerima juga objek yang mengimplementasikan SelectQueryInterfacesebagai nilai untuk $value, seperti yang dikembalikan oleh db_select(); masalahnya adalah bahwa sebenarnya Anda bisa menggunakannya ketika nilai $operatorsama dengan "IN". Lihat Subselek tidak berfungsi dalam kondisi DBTNG, kecuali bila digunakan sebagai nilai untuk IN .

Satu-satunya cara saya dapat melihat untuk menggunakan operator "TIDAK DALAM" dengan sub-kueri conditionadalah dengan:

  • Jalankan subquery untuk mendapatkan array
  • Jalankan query utama yang mengatur kondisi seperti pada cuplikan berikut

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

    $subquery_result adalah array yang berisi hasil dari sub-query.

Jika tidak, Anda bisa menggunakan where()seperti kata orang lain, yang menerima string untuk bagian dari kueri yang perlu Anda tambahkan.

Ingatlah bahwa db_select()itu lebih lambat db_query(); Anda harus menggunakan yang pertama saat Anda tahu kueri dapat diubah oleh modul lain. Jika tidak, jika modul lain tidak seharusnya digunakan hook_query_alter()untuk mengubah kueri Anda, Anda harus menggunakan db_query().
Dalam hal mengakses node, jika Anda hanya perlu mendapatkan node yang dapat diakses oleh pengguna, maka Anda harus menggunakan db_select()dan menambahkan 'node_access'sebagai tag kueri, dengan SelectQuery::addTag(). Misalnya, blog_page_last()gunakan kode berikut.

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

Kode serupa digunakan oleh 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
sumber
Berikut adalah contoh subquery untuk filter kustom tampilan yang saya tulis: link
Roger
1
"Subselek tidak berfungsi dalam kondisi DBTNG, kecuali bila digunakan sebagai nilai untuk IN" ditetapkan dalam Drupal 8.3
Jonathan
3

Saat menulis query yang kompleks Anda pasti harus menggunakan db_query()bukan db_select().

  1. Anda tidak bisa menulis NOT INklausa dengan subquery dengan API database Drupal saat ini (ini adalah masalah yang diketahui sedang dikerjakan).
  2. Jika Anda tidak perlu kueri Anda menjadi dinamis (karenanya ditulis ulang oleh modul lain), jangan repot-repot mencoba menulis yang rumit seperti itu db_select().
  3. Subquery belum didukung dengan baik (lihat jawaban saya sebelumnya ) dan jika Anda terbiasa menulis SQL, cara ini lebih mudah digunakan db_query().

Mengenai kueri Anda, saya tidak yakin mengapa Anda ingin menggunakan subquery (kecuali Anda menyederhanakan contoh Anda)? Anda dapat menulisnya dengan mudah seperti ini:

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

DISTINCTtidak diperlukan seperti nidkunci primer sehingga tidak akan diduplikasi.

tostinni
sumber
2
Mengenai # 2, OP sedang memilih node. AFAIK db_select () adalah satu-satunya cara untuk memberikan tag 'node_access' yang diperlukan, dalam hal ini db_select () akan menjadi satu-satunya pilihan.
keithm
2

Ada juga where () yang memungkinkan untuk menambahkan kondisi arbitrary where ke kueri.

Contoh:

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

Seperti yang disebutkan oleh keithm, Anda harus menggunakan db_select () dan addTag ('node_access') ketika memilih node yang kemudian ditampilkan kepada pengguna.

Berdir
sumber
1

Cara yang lebih mudah untuk menggunakan db_select dengan subselect NOT IN hanya menggunakan sedikit yang diketahui

$ query-> di mana

untuk menambahkan kondisi tempat sewenang-wenang.

misalnya:

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

Di mana $ subquery_values ​​adalah larik format $ key => $ nid sebagai hasil dari subquery

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

ini bekerja dengan baik.

Riccardo Ravaro
sumber