SQL WHERE .. IN yan tümcesi birden çok sütun


173

SQL Server'da aşağıdaki sorguyu uygulamak gerekir:

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
)

Ancak WHERE..IN deyimi yalnızca 1 sütuna izin verir. 2 veya daha fazla sütunu başka bir iç SELECT ile nasıl karşılaştırabilirim?


Burada gerekli uyarılarla ilgili çözümlere genel bir bakış sağlamaya çalıştım: stackoverflow.com/a/54389589/983722
Dennis Jaheruddin

Yanıtlar:


110

Alt sorgudan türetilmiş bir tablo oluşturabilir ve bu türetilmiş tabloya table1'i birleştirebilirsiniz:

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

7
veya daha genel olarak SEÇ * tablodan INNER JOIN otherTable ON'a katıl (table.x = otherTable.a AND table.y = otherTable.b)
ala

4
Tablo 2, tablo 1'in alt öğesiyse var olabilecek birden çok satıra ne dersiniz? Ve neden SOL KATILIR?
gbn

1
Evet, INNER JOIN burada daha iyi performans gösterirdi. SOL BİRLEŞTİRME yapmak ve boş tabloları tablo 2'den filtrelemek, bir İÇ
ORTAK

Yanlış, bu, birleştirilmiş masanın birkaç kez birleştirilebileceği varsayılarak, satırı birden çok kez sunar ... aksi takdirde, bir iç birleştirme yapın ve kendinizi nereye ayırabileceğinizi düşünün.
Stefan Steiger

123

Bunun yerine WHERE EXISTS sözdizimini kullanmak isteyeceksiniz.

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)

5
Bu işe yarayacak olsa da, sorudaki ilişkisiz sorguyu ilişkili bir sorguya dönüştürür. Sorgu iyileştirici akıllı olmadığı sürece, bu size O (n ^ 2) performansı verebilir :-(. Ama belki de optimize
ediciyi

1
Her zaman böyle sözdizimlerini sorunsuz kullanıyorum. Daha eski bir optimize edici (6.5, 7, 8 vb.) Kullanmadığınız sürece bu sözdiziminde bir sorun olmamalıdır.
mrdenny

1
@sleske: EXISTS çok daha iyi: Cevabımda yorumlarıma bakın. Önce test edin. @mrdenny: İlk başta cevabını yanlış okudum,
EXISTS'i

6
Bu en verimli +1. Performans karşılaştırması için
blogumda

1
SQL 2000 bile sorguyu O (n ^ 2) haline getirmeden en ilişkili alt sorguları işleyebilir. 6.5'te bir sorun olabilirdi.
GilaMonster

14

ÇÖZÜMLER HAKKINDA UYARI:

BİRKAÇ MEVCUT ÇÖZÜM, SIRA BENZERSİZ DEĞİLSE YANLIŞ ÇIKTI VERECEK

Tablo oluşturan tek kişi sizseniz, bu uygun olmayabilir, ancak birkaç çözüm, tablolardan biri benzersiz satırlar içermeyebileceği zaman, söz konusu koddan farklı sayıda çıktı satırı verecektir.

SORUN BİLDİRİMİ HAKKINDA UYARI:

BİRDEN FAZLA KOLON VARSA, İSTEDİĞİNİZİ DİKKATLİCE DÜŞÜNÜN

İki sütunlu bir giriş gördüğümde, bunun iki anlama geldiğini hayal edebilirim:

  1. A sütununun ve b sütununun değeri diğer tabloda bağımsız olarak görünür
  2. A sütununun ve b sütununun değerleri diğer tabloda aynı satırda birlikte görünür

Senaryo 1 oldukça önemsizdir, sadece iki IN ifadesi kullanın.

Mevcut cevapların çoğuna uygun olarak, Senaryo 2 (ve kısa bir karar) için bahsedilen ve ek yaklaşımlara genel bir bakış sunuyorum:

EXISTS (Güvenli, SQL Server için önerilir)

@Mrdenny tarafından sağlanan EXISTS, tam olarak aradığınız gibi geliyor, işte onun örneği:

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

SOL SEMI JOIN (Güvenli, onu destekleyen lehçeler için önerilir)

Bu katılmak için çok kısa bir yol, ama ne yazık ki SQL sunucusu da dahil olmak üzere çoğu SQL lehçeleri şu anda desteklemiyor.

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

Çoklu IN ifadeleri (Güvenli, ancak kod çoğaltmasına dikkat edin)

@Cataclysm tarafından iki IN ifadesi kullanılarak belirtildiği gibi, hile yapabilir, belki de diğer çözümlerden daha iyi performans gösterebilir. Ancak, çok dikkatli olmanız gereken kod çoğaltmasıdır. Farklı bir tablodan seçim yapmak veya where deyimini değiştirmek isterseniz, mantığınızda tutarsızlıklar yaratma riskiniz artar.

Temel çözüm

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

Kod çoğaltma olmadan çözüm (Bu normal SQL Server sorgularında çalışmaz inanıyorum)

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 JOIN (teknik olarak güvenli hale getirilebilir, ancak genellikle bu yapılmaz)

Bir iç birleşimi filtre olarak kullanmanızı önermememin nedeni, pratikte insanlar genellikle sağ tablodaki kopyaların sol tablodaki kopyalara neden olmasına neden olur. Ve daha sonra, konuları daha da kötüleştirmek için, bazen nihai sonucu farklı kılarlar, ancak sol tablonun aslında benzersiz olması (veya seçtiğiniz sütunlarda benzersiz olmaması) gerekmeyebilir. Dahası, sol tabloda bulunmayan bir sütunu gerçekten seçme şansı verir.

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

En yaygın hatalar:

  1. Güvenli bir alt sorgu olmadan doğrudan T2'ye katılmak. Tekrarlama riskiyle sonuçlanan)
  2. SELECT * (T2'den sütun alma garantisi)
  3. SELECT c (Sütununun geldiğini ve her zaman T1'den geleceğini garanti etmez)
  4. Yanlış yerde DISTINCT veya DISTINCT yok

KOLONLARIN SEPARATÖR İLE KONTROL EDİLMESİ (Çok güvenli değil, korkunç performans)

İşlevsel sorun, bir sütunda meydana gelebilecek bir ayırıcı kullanırsanız, sonucun% 100 doğru olmasını sağlamak zorlaşır. Teknik sorun, bu yöntemin genellikle tür dönüşümleri gerçekleştirmesi ve dizinleri tamamen göz ardı etmesi ve muhtemelen korkunç bir performansla sonuçlanmasıdır. Bu sorunlara rağmen, itiraf etmeliyim ki bazen hala küçük veri kümelerindeki geçici sorgular için kullanıyorum.

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

Sütunlarınız sayısalsa, bazı SQL lehçelerinin önce bunları dizelere yayınlamanızı gerektireceğini unutmayın. SQL sunucusunun bunu otomatik olarak yapacağına inanıyorum.


Bir şeyleri toparlamak için: Her zamanki gibi bunu SQL'de yapmanın birçok yolu vardır, güvenli seçimler kullanmak sürprizlerden kaçınır ve uzun vadede size zaman ve baş ağrısı kazandırır.


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

Not:
Oracle, seçilen sütunlardan bir veya daha fazlasının NULL olduğu satırları yoksayar. Bu durumlarda, NULL değerini özel bir değere eşlemek için NVL -Funktion işlevini kullanmak istersiniz (bu değerlerde olmamalıdır);

select * from tab1
where (col1, NVL(col2, '---') in (select col1, NVL(col2, '---') from tab2)

2
postgres destekliyor, where (colA,colB) in (... some list of tuples...)ancak diğer veritabanlarının aynı şeyi yaptığından emin değilim. Bilmek isterdim.
Max Murphy

2
Bu sözdizimi Oracle ve DB2 / 400'de de desteklenir (muhtemelen DB2 de). İstek SQL Server destekledi.
CrazyIvan1974

DB2 bunu desteklemektedir.
Telmo Marques

SQLite bile bunu destekler.
Holger Jakobs

13

Basit bir EXISTS deyimi en temizdir

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
)

Korelasyonda birden çok satırınız varsa, bir JOIN çıktıda birden çok satır verir, bu yüzden farklı olmanız gerekir. Bu da genellikle EXISTS'i daha verimli hale getirir.

SELECT *Bir JOIN ile not ayrıca satır sınırlayıcı tablolardan sütunlar içerecektir


2

Normal bir iç birleşim yapabiliyorsanız neden NEREDEN VAR veya TÜREV TABLOLAR kullanmalısınız?

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

Durum tablosunda (CM_PLAN_ID, Individual_ID) çifti benzersiz değilse, bunun yerine SELECT DISTINCT t. * Seçeneğine ihtiyacınız olabilir.


3
Ve DISTINCT genellikle bir
EXISTS'in

0
Postgres SQL  : version 9.6
Total records on tables : mjr_agent = 145, mjr_transaction_item = 91800

1. EXISTS[Ortalama Sorgu Süresi: 1.42s] ile kullanma

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.İki satırla kullanma INMadde [Ortalama Sorgu Süresi: 0.37s]

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. INNNER JOINdesen ile kullanma [Ortalama Sorgu Süresi: 2.9 s]

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

Bu yüzden ikinci seçeneği seçtim.


Gelecekteki okuyucular için uyarı: Soruya uygun olarak, muhtemelen ANDifadeler yerine ORifadeler kullanmak isteyeceksiniz .
Dennis Jaheruddin

@DennisJaheruddin .. Yorumunuz ve cevabınızın çok güzel detay açıklamaları için teşekkür ederiz. Haklısın, ORifade muhtemelen çoğaltmaları artırır. Benim durumumda, aynı src_idve dest_idtek bir satır içeren herhangi bir satır yok. Yani, benim durumumda kopyalar olmayacak.
Cataclysm


-2

Bir tablo için istiyorsanız aşağıdaki sorguyu kullanın

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')

ve tablo verileri aşağıdaki gibi

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

Sonra aşağıdaki gibi çıktı

id|name|adde|city
1   a   ad  ca
2   b   bd  bd

id in (1,2) and studentName in ('a','b')ile tamamen aynı değildir (id, studentName) in ((1,'a'),(2,'b')). Sadece id = 2 ve name = 'a' olan bir kayıt düşünün. Elbette, ID benzersizse, etki azalır, ancak daha sonra ID benzersizse, adlara filtre uygulamamıza gerek yoktur.
quetzalcoatl

-2

Bunu basitçe yapabiliriz.

   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

-2

Sütunları bir şekilde birleştirmek bir "hack'tir", ancak ürün birden fazla sütun için yarı birleştirmeleri desteklemediğinde, bazen başka seçeneğiniz olmaz.

İç / dış birleşim çözümünün çalışmadığı örnek:

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

Sorgular doğada önemsiz değilse, bazen düzenli iç / dış birleşimler yapmak için ayarlanan temel tabloya erişemezsiniz.

Bu "hack" i kullanırsanız, alanları birleştirdiğinizde, yanlış yorumlardan kaçınmak için aralarına yeterince sınırlayıcı eklediğinizden emin olun. ColA + ":-:" + ColB


Bu cevap tutarsız görünüyor (birleştirme işleminden bahsediyor ve daha sonra farklı bir örnek sunuyor). Ayrıca, daha hafif bir notta: Her zaman bir seçeneğimiz var ;-) Burada, ilgili dipnotlarla birlikte genel görünümüme birleştirme örneği
ekledim

-3

Bu şekilde daha kolay kurdum

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 
) 

Umarım bu yardım :)


9
Ah, burada hiçbir dizin kullanımı dize concat için yapmak.
mrdenny

9
Çok tehlikeli olduğu için buna oy verdim! Eğer CM_PLAN_ID = 45ve Individual_ID = 3sonra birleştirme ortaya çıkarsa 453- ki bu durumdan ayırt edilemez CM_PLAN_ID = 4ve Individual_ID = 53... sorun sormayı düşünürdüm
El Ronnoco

5
Tabii ki keyfi bir özel karakterle birleştirebilirsiniz 45_3ya da 45:3ama yine de hoş bir çözüm değil ve tabii ki mrdenny, sütunlarda bir dönüşüm gerçekleştiğinden endekslerin kullanılmayacağını söylüyor.
El Ronnoco

1
Ben de oy verdim, çünkü bu çözüm sadece hızlı bir "hack". Yavaş ve El Ronnoco'nun dediği gibi, hatalara yol açabilir.

-4

Basit ve yanlış bir yol, + kullanarak iki sütunu birleştirmek veya bir sütun birleştirmek ve oluşturmak olabilir.

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

Bu oldukça yavaş olurdu. Programlamada kullanılamaz, ancak doğrulamak için sorgulama yapıyorsanız bir şey kullanılabilir.


10
Gerçekten de, örneğin 'ab' + 'c' = 'a' + 'bc' olduğu için hatalara yol açabilir
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.