SQL WHERE .. DALAM klausa beberapa kolom

173

Saya perlu menerapkan kueri berikut di SQL Server:

select *
from table1
WHERE  (CM_PLAN_ID,Individual_ID)
IN
(
 Select CM_PLAN_ID, Individual_ID
 From CRM_VCM_CURRENT_LEAD_STATUS
 Where Lead_Key = :_Lead_Key
)

Tetapi klausa WHERE..IN hanya mengizinkan 1 kolom. Bagaimana saya bisa membandingkan 2 kolom atau lebih dengan SELECT dalam lainnya?

ala
sumber
Saya mencoba memberikan gambaran umum tentang solusi yang relevan, dengan peringatan yang diperlukan di sini: stackoverflow.com/a/54389589/983722
Dennis Jaheruddin

Jawaban:

110

Anda bisa membuat tabel turunan dari subquery, dan gabung table1 ke tabel turunan ini:

select * from table1 LEFT JOIN 
(
   Select CM_PLAN_ID, Individual_ID
   From CRM_VCM_CURRENT_LEAD_STATUS
   Where Lead_Key = :_Lead_Key
) table2
ON 
   table1.CM_PLAN_ID=table2.CM_PLAN_ID
   AND table1.Individual=table2.Individual
WHERE table2.CM_PLAN_ID IS NOT NULL
sleske
sumber
7
atau lebih umum SELECT * DARI tabel INNER GABUNGLain-lain ON (table.x = otherTable.a DAN table.y = otherTable.b)
ala
4
Bagaimana dengan beberapa baris yang akan ada jika tabel 2 adalah anak dari tabel 1? Dan mengapa KIRI BERGABUNG?
gbn
1
Ya, INNER BERGABUNG akan lebih banyak tampil di sini. Melakukan KIRI BERGABUNG dan menyaring nol dari tabel 2 hanyalah cara
lisan
Salah, ini mengirimkan baris beberapa kali, dengan asumsi tabel yang digabung dapat digabungkan beberapa kali ... jika tidak, lakukan penggabungan dalam dan Anda dapat menyisakan diri sendiri di mana.
Stefan Steiger
123

Anda akan ingin menggunakan sintaks WHERE EXISTS sebagai gantinya.

SELECT *
FROM table1
WHERE EXISTS (SELECT *
              FROM table2
              WHERE Lead_Key = @Lead_Key
                        AND table1.CM_PLAN_ID = table2.CM_PLAN_ID
                        AND table1.Individual_ID = table2.Individual_ID)
mrdenny
sumber
5
Meskipun ini akan berhasil, ia mengubah kueri yang tidak berkorelasi dalam pertanyaan menjadi kueri yang berkorelasi. Kecuali pengoptimal kueri cerdas, ini mungkin memberi Anda O (n ^ 2) kinerja :-(. Tapi mungkin aku meremehkan pengoptimal ...
sleske
1
Saya menggunakan sintaksis seperti ini setiap saat tanpa masalah. Kecuali jika Anda menggunakan pengoptimal yang lebih lama (6.5, 7, 8, dll) itu seharusnya tidak memiliki masalah dengan sintaks ini.
mrdenny
1
@sleske: EXISTS jauh lebih baik: lihat komentar saya di jawaban saya. Dan mengujinya terlebih dahulu, @ Mrdenny: Saya salah membaca jawaban Anda pada awalnya, saya akan menggunakan EXISTS juga
gbn
6
Ini paling efisien, +1. Lihat artikel ini di blog saya untuk perbandingan kinerja: explainextended.com/2009/06/17/efficient-exists
Quassnoi
1
Bahkan SQL 2000 dapat menangani sebagian besar subqueries yang berkorelasi tanpa mengubah kueri menjadi O (n ^ 2). Mungkin masalah kembali ke 6.5.
GilaMonster
14

PERINGATAN TENTANG SOLUSI:

SOLUSI YANG BANYAK ADA AKAN MEMBERIKAN OUTPUT YANG SALAH JIKA ROW TIDAK UNIK

Jika Anda adalah satu-satunya orang yang membuat tabel, ini mungkin tidak relevan, tetapi beberapa solusi akan memberikan jumlah baris output yang berbeda dari kode yang dimaksud, ketika salah satu tabel mungkin tidak mengandung baris unik.

PERINGATAN TENTANG PERNYATAAN MASALAH:

DALAM DENGAN KOLOM GANDA TIDAK ADA, BERPIKIR DENGAN SEKSAMA APA YANG ANDA INGINKAN

Ketika saya melihat sebuah dengan dua kolom, saya bisa membayangkan itu berarti dua hal:

  1. Nilai kolom a dan kolom b muncul di tabel lainnya secara independen
  2. Nilai kolom a dan kolom b muncul bersama di tabel lainnya pada baris yang sama

Skenario 1 cukup sepele, cukup gunakan dua pernyataan IN.

Sejalan dengan sebagian besar jawaban yang ada, saya dengan ini memberikan tinjauan umum tentang pendekatan yang disebutkan dan tambahan untuk Skenario 2 (dan penilaian singkat):

EXISTS (Aman, disarankan untuk SQL Server)

Seperti yang disediakan oleh @mrdenny, EXISTS terdengar persis seperti yang Anda cari, berikut adalah contohnya:

SELECT * FROM T1
WHERE EXISTS
(SELECT * FROM T2 
 WHERE T1.a=T2.a and T1.b=T2.b)

LEFT SEMI JOIN (Aman, disarankan untuk dialek yang mendukungnya)

Ini adalah cara yang sangat ringkas untuk bergabung, tetapi sayangnya sebagian besar dialek SQL, termasuk server SQL saat ini tidak mendukungnya.

SELECT * FROM T1
LEFT SEMI JOIN T2 ON T1.a=T2.a and T1.b=T2.b

Berbagai pernyataan IN (Aman, tetapi waspadai duplikasi kode)

Seperti yang disebutkan oleh @cataclysm menggunakan dua pernyataan IN dapat melakukan trik juga, mungkin bahkan akan mengungguli solusi lain. Namun, Anda harus sangat berhati-hati dengan duplikasi kode. Jika Anda ingin memilih dari tabel yang berbeda, atau mengubah pernyataan where, itu adalah risiko yang meningkat bahwa Anda membuat inkonsistensi dalam logika Anda.

Solusi dasar

SELECT * from T1
WHERE a IN (SELECT a FROM T2 WHERE something)
AND b IN (SELECT b FROM T2 WHERE something)

Solusi tanpa duplikasi kode (saya percaya ini tidak berfungsi dalam permintaan SQL Server reguler)

WITH mytmp AS (SELECT a, b FROM T2 WHERE something);
SELECT * from T1 
WHERE a IN (SELECT a FROM mytmp)
AND b IN (SELECT b FROM mytmp)

INNER BERGABUNG (secara teknis dapat dibuat aman, tetapi seringkali ini tidak dilakukan)

Alasan mengapa saya tidak merekomendasikan menggunakan gabungan dalam sebagai filter, adalah karena dalam praktiknya orang sering membiarkan duplikat di tabel kanan menyebabkan duplikat di tabel sebelah kiri. Dan kemudian membuat keadaan menjadi lebih buruk, mereka kadang-kadang membuat hasil akhir yang berbeda sementara tabel kiri sebenarnya mungkin tidak perlu unik (atau tidak unik di kolom yang Anda pilih). Lebih lanjut itu memberi Anda kesempatan untuk benar-benar memilih kolom yang tidak ada di tabel sebelah kiri.

SELECT T1.* FROM T1
INNER JOIN 
(SELECT DISTINCT a, b FROM T2) AS T2sub
ON T1.a=T2sub.a AND T1.b=T2sub.b

Kesalahan paling umum:

  1. Bergabung langsung di T2, tanpa subquery yang aman. Menghasilkan risiko duplikasi)
  2. SELECT * (Dijamin mendapatkan kolom dari T2)
  3. SELECT c (Tidak menjamin bahwa kolom Anda datang dan selalu akan datang dari T1)
  4. Dilarang MENYERAH atau MENYANGKAL di tempat yang salah

KONCATENASI KOLOM DENGAN SEPARATOR (Tidak terlalu aman, kinerja mengerikan)

Masalah fungsionalnya adalah jika Anda menggunakan pemisah yang mungkin muncul dalam kolom, sulit untuk memastikan bahwa hasilnya 100% akurat. Masalah teknisnya adalah bahwa metode ini sering menimbulkan konversi jenis dan mengabaikan indeks sepenuhnya, sehingga menghasilkan kinerja yang mengerikan. Terlepas dari masalah ini, saya harus mengakui bahwa kadang-kadang saya masih menggunakannya untuk permintaan ad-hoc pada dataset kecil.

SELECT * FROM T1
WHERE CONCAT(a,"_",b) IN 
(SELECT CONCAT(a,"_",b) FROM T2)

Perhatikan bahwa jika kolom Anda berupa angka, beberapa dialek SQL akan meminta Anda untuk memasukkannya terlebih dahulu ke string. Saya percaya SQL server akan melakukan ini secara otomatis.


Untuk menyelesaikannya: Seperti biasa ada banyak cara untuk melakukan ini dalam SQL, menggunakan pilihan yang aman akan menghindari kejutan dan menghemat waktu dan waktu Anda dalam jangka panjang.

Dennis Jaheruddin
sumber
13
select * from tab1 where (col1,col2) in (select col1,col2 from tab2)

Catatan:
Oracle mengabaikan baris di mana satu atau lebih kolom yang dipilih adalah NULL. Dalam kasus ini, Anda mungkin ingin menggunakan NVL -Funktion untuk memetakan NULL ke nilai khusus (yang seharusnya tidak ada dalam nilai-nilai);

select * from tab1
where (col1, NVL(col2, '---') in (select col1, NVL(col2, '---') from tab2)
tommes-pommes
sumber
2
postgres mendukung where (colA,colB) in (... some list of tuples...)tetapi saya tidak yakin apa yang dilakukan oleh basis data lain. Saya tertarik untuk tahu.
Max Murphy
2
Sintaks ini didukung di Oracle dan DB2 / 400 juga (mungkin DB2 juga). Berharap SQL Server mendukungnya.
CrazyIvan1974
DB2 mendukung ini.
Telmo Marques
Bahkan SQLite mendukungnya.
Holger Jakobs
13

Klausa EXISTS sederhana adalah terbersih

select *
from table1 t1
WHERE
EXISTS
(
 Select * --or 1. No difference...
 From CRM_VCM_CURRENT_LEAD_STATUS Ex
 Where Lead_Key = :_Lead_Key
-- correlation here...
AND
t1.CM_PLAN_ID = Ex.CM_PLAN_ID AND t1.CM_PLAN_ID =  Ex.Individual_ID
)

Jika Anda memiliki beberapa baris dalam korelasi maka GABUNG memberikan beberapa baris dalam output, jadi Anda harus berbeda. Yang biasanya membuat EXIS lebih efisien.

Catatan SELECT *dengan GABUNG juga akan menyertakan kolom dari tabel pembatas baris

gbn
sumber
2

Mengapa menggunakan WHERE EXISTS atau DERIVED TABLES ketika Anda hanya bisa melakukan inner join normal:

SELECT t.*
FROM table1 t
INNER JOIN CRM_VCM_CURRENT_LEAD_STATUS s
    ON t.CM_PLAN_ID = s.CM_PLAN_ID
    AND t.Individual_ID = s.Individual_ID
WHERE s.Lead_Key = :_Lead_Key

Jika pasangan (CM_PLAN_ID, Individual_ID) tidak unik di tabel status, Anda mungkin perlu SELECT DISTINCT t. * Sebagai gantinya.

BradC
sumber
3
Dan DISTINCT biasanya berarti ADA lebih efisien
gbn
0
Postgres SQL  : version 9.6
Total records on tables : mjr_agent = 145, mjr_transaction_item = 91800

1.Menggunakan dengan EXISTS[Waktu Permintaan Rata-rata: 1.42s]

SELECT count(txi.id) 
FROM 
mjr_transaction_item txi
WHERE 
EXISTS ( SELECT 1 FROM mjr_agent agnt WHERE agnt.agent_group = 0 AND (txi.src_id = agnt.code OR txi.dest_id = agnt.code) ) 

2.Menggunakan dengan dua baris INAyat [Waktu Permintaan Rata-rata: 0,37d]

SELECT count(txi.id) FROM mjr_transaction_item txi
WHERE 
txi.src_id IN ( SELECT agnt.code FROM mjr_agent agnt WHERE agnt.agent_group = 0 ) 
OR txi.dest_id IN ( SELECT agnt.code FROM mjr_agent agnt WHERE agnt.agent_group = 0 )

3.Menggunakan dengan INNNER JOINpola [Waktu Permintaan Rata-rata: 2,9s]

SELECT count(DISTINCT(txi.id)) FROM mjr_transaction_item txi
INNER JOIN mjr_agent agnt ON agnt.code = txi.src_id OR agnt.code = txi.dest_id
WHERE 
agnt.agent_group = 0

Jadi, saya memilih opsi kedua.

Bencana alam
sumber
Peringatan untuk pembaca di masa depan: Sejalan dengan pertanyaan, Anda mungkin ingin menggunakan ANDpernyataan daripada ORpernyataan.
Dennis Jaheruddin
@DennisJaheruddin .. Terima kasih atas komentar Anda dan penjelasan detail yang sangat bagus dari jawaban Anda. Anda benar, ORpernyataan mungkin meningkatkan duplikasi. Dalam kasus saya, belum ada baris yang berisi sama src_iddan dest_iddalam satu baris. Jadi, duplikasi tidak akan terjadi dalam kasus saya.
Bencana alam
-2

Jika Anda menginginkan satu tabel maka gunakan kueri berikut

SELECT S.* 
FROM Student_info S
  INNER JOIN Student_info UT
    ON S.id = UT.id
    AND S.studentName = UT.studentName
where S.id in (1,2) and S.studentName in ('a','b')

dan data tabel sebagai berikut

id|name|adde|city
1   a   ad  ca
2   b   bd  bd
3   a   ad  ad
4   b   bd  bd
5   c   cd  cd

Kemudian output sebagai berikut

id|name|adde|city
1   a   ad  ca
2   b   bd  bd
Somnath Kadam
sumber
id in (1,2) and studentName in ('a','b')sama sekali tidak sama dengan (id, studentName) in ((1,'a'),(2,'b')). Pikirkan saja sebuah catatan yang memiliki id = 2 dan name = 'a'. Tentu saja, jika ID unik, maka efeknya berkurang, tetapi kemudian, jika ID unik, kita tidak perlu menyaring nama sama sekali.
quetzalcoatl
-2

Kita bisa melakukan ini.

   select *
   from 
    table1 t, CRM_VCM_CURRENT_LEAD_STATUS c
    WHERE  t.CM_PLAN_ID = c.CRM_VCM_CURRENT_LEAD_STATUS
    and t.Individual_ID = c.Individual_ID
Rpant
sumber
-2

Menggabungkan kolom bersama dalam beberapa bentuk adalah "retas", tetapi ketika produk tidak mendukung setengah bergabung untuk lebih dari satu kolom, kadang-kadang Anda tidak punya pilihan.

Contoh di mana solusi gabungan luar / dalam tidak akan berfungsi:

select * from T1 
 where <boolean expression>
   and (<boolean expression> OR (ColA, ColB) in (select A, B ...))
   and <boolean expression>
   ...

Ketika pertanyaan tidak sepele di alam kadang-kadang Anda tidak memiliki akses ke tabel dasar yang ditetapkan untuk melakukan gabungan dalam / luar biasa.

Jika Anda menggunakan "retasan" ini, saat Anda menggabungkan bidang, pastikan untuk menambahkan cukup pembatas di antaranya untuk menghindari salah tafsir, mis. ColA + ":-:" + ColB

John K.
sumber
Jawaban ini tampaknya tidak konsisten (menyebutkan rangkaian dan kemudian memberikan contoh yang berbeda). Juga, dengan catatan yang lebih ringan: Kami selalu punya pilihan ;-) Saya memang menambahkan contoh gabungan untuk ikhtisar saya di sini, dengan catatan kaki yang relevan: stackoverflow.com/a/54389589/983722
Dennis Jaheruddin
-3

Saya mendirikan lebih mudah dengan cara ini

Select * 
from table1 
WHERE  (convert(VARCHAR,CM_PLAN_ID) + convert(VARCHAR,Individual_ID)) 
IN 
(
 Select convert(VARCHAR,CM_PLAN_ID) + convert(VARCHAR,Individual_ID)
 From CRM_VCM_CURRENT_LEAD_STATUS 
 Where Lead_Key = :_Lead_Key 
) 

Semoga bantuan ini :)

Lisandro Acosta
sumber
9
Aduh, tidak ada indeks yang digunakan di sini untuk string concat.
mrdenny
9
Saya telah memilih ini karena sangat berbahaya! Jika CM_PLAN_ID = 45dan Individual_ID = 3kemudian hasil gabungan 453- yang tidak dapat dibedakan dari kasus di mana CM_PLAN_ID = 4dan Individual_ID = 53... meminta masalah saya akan berpikir
El Ronnoco
5
..tentu saja Anda dapat menggabungkan dengan char khusus semena-mena misalnya 45_3atau 45:3tetapi itu masih bukan solusi yang bagus dan tentu saja seperti @mrdenny mengatakan indeks tidak akan digunakan sekarang karena transformasi telah terjadi pada kolom.
El Ronnoco
1
Saya juga memilih ini, karena solusi ini benar-benar "hack" cepat saja. Ini lambat dan seperti yang dikatakan El Ronnoco, itu dapat menyebabkan bug.
-4

Cara sederhana dan salah akan menggabungkan dua kolom menggunakan + atau menyatukan dan membuat satu kolom.

Select *
from XX
where col1+col2 in (Select col1+col2 from YY)

Ini akan menjadi sangat lambat. Tidak dapat digunakan dalam pemrograman tetapi jika seandainya Anda hanya ingin memverifikasi sesuatu dapat digunakan.

Vijay
sumber
10
Memang, dan itu dapat menyebabkan kesalahan, karena misalnya 'ab' + 'c' = 'a' + 'bc'