SQL "alt sorguda olmayan yeri seçin" sonuç döndürmez


131

Sorumluluk reddi: Sorunu çözdüm (sanırım), ancak hiçbir yerde (kolayca) bulamadığım için bu sorunu Stack Overflow'a eklemek istedim. Ayrıca birisinin benden daha iyi bir cevabı olabilir.

Bir "Ortak" tablosunun diğer birkaç tablo tarafından başvurulduğu bir veritabanım var. Ortak tablodaki hangi kayıtların öksüz kaldığını görmek istedim (yani, diğer tabloların hiçbirinden referans yoktu).

Bu sorguyu çalıştırdım:

select *
from Common
where common_id not in (select common_id from Table1)
and common_id not in (select common_id from Table2)

Sahipsiz kayıtlar olduğunu biliyorum, ancak hiçbir kayıt iade edilmedi. Neden olmasın?

(Bu, önemliyse SQL Server'dır.)


Bu stackoverflow.com/a/129152/1667619 , NEDEN sorusunu oldukça iyi yanıtlıyor.
Rüçhan

Yanıtlar:


235

Güncelleme:

Blogumdaki bu makaleler, yöntemler arasındaki farkları daha ayrıntılı olarak açıklamaktadır:


Böyle bir sorgu yapmanın üç yolu vardır:

  • LEFT JOIN / IS NULL:

    SELECT  *
    FROM    common
    LEFT JOIN
            table1 t1
    ON      t1.common_id = common.common_id
    WHERE   t1.common_id IS NULL
  • NOT EXISTS:

    SELECT  *
    FROM    common
    WHERE   NOT EXISTS
            (
            SELECT  NULL
            FROM    table1 t1
            WHERE   t1.common_id = common.common_id
            )
  • NOT IN:

    SELECT  *
    FROM    common
    WHERE   common_id NOT IN
            (
            SELECT  common_id
            FROM    table1 t1
            )

Ne zaman table1.common_idnull değil, bütün bu sorgular anlamsal aynıdır.

Boş değer atanabilir olduğunda NOT IN, farklıdır, çünkü IN(ve bu nedenle NOT IN) NULLbir değer, bir NULL.

Bu kafa karıştırıcı olabilir, ancak bunun için alternatif sözdizimini hatırlarsak daha açık hale gelebilir:

common_id = ANY
(
SELECT  common_id
FROM    table1 t1
)

Bu koşulun sonucu, listedeki tüm karşılaştırmaların bir mantıksal ürünüdür. Tabii ki, tek bir NULLdeğer NULLsonucu da verir, bu da tüm sonucu verir NULL.

common_idDeğerlerden en az biri olduğu için, kesinlikle bu listedeki hiçbir şeye eşit olmadığını söyleyemeyiz NULL.

Şu verilere sahip olduğumuzu varsayalım:

common

--
1
3

table1

--
NULL
1
2

LEFT JOIN / IS NULLve NOT EXISTSgeri dönecek 3, hiçbir şeyNOT IN döndürmeyecek (çünkü her zaman ya da olarak değerlendirilecektir ).FALSENULL

In MySQL, non-nullable sütun üzerinde LEFT JOIN / IS NULLve NOT INbiraz (birkaç yüzde) daha etkilidir NOT EXISTS. Sütun boş değer atanabilirse, NOT EXISTSen verimli olanıdır (yine çok değil).

İçinde Oracle, üç sorgu da aynı planları (an ANTI JOIN) verir.

İn SQL Server, NOT IN/ NOT EXISTSare daha verimlidir, çünkü LEFT JOIN / IS NULLoptimize edicisi ANTI JOINtarafından bir için optimize edilemez .

İçinde PostgreSQL, LEFT JOIN / IS NULLve NOT EXISTSdaha verimli NOT IN, onlar için optimize edilmiştir sinüs Anti Joinise, NOT INkullanım hashed subplan(ya da bir düz subplanalt sorgu karma için çok büyük ise)


8
Mükemmel cevap! Teşekkürler!
StevenMcD

bu harika ve çok faydalı
kavun

1
+1 çünkü, dört buçuk yıl sonra, bu cevap beni şaşkına çeviren bir problemle bana yardımcı oldu!
Carson63000

@ Carson63000 Snap! Bu cevabı görmeden önce delirdiğimi sanıyordum
Bobby

1
@IstiaqueAhmed: NOT EXISTSiçindeki sorgu herhangi bir satır döndürürse DOĞRU olarak değerlendirilir. SELECT NULLolabilir SELECT *ya SELECT 1da başka bir şey olabilir, NOT EXISTSyüklem satırların değerlerine bakmaz, yalnızca onları sayar.
Quassnoi

36

Dünyanın iki değerli bir boole yeri olmasını istiyorsanız, boş (üçüncü değer) durumunu kendiniz engellemelisiniz.

Liste tarafında boş değerlere izin veren IN cümleleri yazmayın. Onları filtreleyin!

common_id not in
(
  select common_id from Table1
  where common_id is not null
)

6
Yan tümce-listesindeki boş değerler, eksik sorgu sonuçlarının yaygın bir nedenidir.
Amy B

@ Jeremy Stein'ın cevabından 'bir boş ile karşılaştırırken cevap bilinmiyor'. From common_id not in, biz hala common_iddeğerine sahip olabiliriz NULL. Yani sonuç alamama sorunu hala devam etmiyor mu?
Istiaque Ahmed

5

Table1 veya Table2, common_id için bazı boş değerlere sahiptir. Bunun yerine şu sorguyu kullanın:

select *
from Common
where common_id not in (select common_id from Table1 where common_id is not null)
and common_id not in (select common_id from Table2 where common_id is not null)

1
Ya bir tabloda veri varsa diğerinde yoksa? "Ve" veya "veya" orada mı?
Philip Kelley

1
Herhangi bir tabloda referans verilmeyen kayıtları arıyorum, bu yüzden AND istiyorum. Soruyu açıklığa kavuşturacağım.
Jeremy Stein

4
select *
from Common c
where not exists (select t1.commonid from table1 t1 where t1.commonid = c.commonid)
and not exists (select t2.commonid from table2 t2 where t2.commonid = c.commonid)

4

Sadece kafamın tepesinde ...

select c.commonID, t1.commonID, t2.commonID
from Common c
     left outer join Table1 t1 on t1.commonID = c.commonID
     left outer join Table2 t2 on t2.commonID = c.commonID
where t1.commonID is null 
     and t2.commonID is null

Birkaç test yaptım ve işte sonuçlarım wrt @ patmortech'in cevabı ve @ rexem'in yorumları.

Table1 veya Table2 commonID'de indekslenmemişse, bir tablo taraması alırsınız ancak @ patmortech'in sorgusu hala iki kat daha hızlıdır (100K satırlık bir ana tablo için).

Hiçbiri commonID'de indekslenmemişse, iki tablo taraması elde edersiniz ve fark önemsizdir.

Her ikisi de commonID'de dizine alınmışsa, "yok" sorgusu 1/3 saat içinde çalışır.


1
Bu, where cümlesindeki bir AND olmalıdır. Aksi takdirde işe yarıyor.
Jeremy Stein

1
yorumunuza göre değişti. Her iki tablodaki "veya" öksüzleri seçer.
Austin Salonen

1
Bu daha iyi. Bu arada, alt sorgu yerine dış birleşimleri kullanmamın bir nedeni var mı?
Jeremy Stein

3
Okunabilirlik birincildir. Daha iyi bir yürütme planının oluşturulacağından şüpheleniyorum ancak bir sorgu planı olmadan onaylayamıyorum.
Austin Salonen

2
Bu yaklaşım, NOT EXISTS'i kullanmaktan daha kötüdür - birleştirme, ihtiyaç duyduğundan daha fazla satır getirmeye neden olur, daha sonra sütunlar için karşılaştırılan sonuçlar boştur. Ve NOT EXISTS, önyükleme için daha okunabilir.
OMG Ponies

3
SELECT T.common_id
  FROM Common T
       LEFT JOIN Table1 T1 ON T.common_id = T1.common_id
       LEFT JOIN Table2 T2 ON T.common_id = T2.common_id
 WHERE T1.common_id IS NULL
   AND T2.common_id IS NULL

1
Bu yaklaşım, NOT EXISTS'i kullanmaktan daha kötüdür - birleştirme, ihtiyaç duyduğundan daha fazla satır getirmeye neden olur, daha sonra sütunlar için karşılaştırılan sonuçlar boştur. Çalışır, ancak performans o kadar iyi olmayacaktır - ilişkili alt sorgularla IN kullanmaktan muhtemelen daha kötüdür.
OMG Ponies

3

Common_id için şu değerleri varsayalım:

Common - 1
Table1 - 2
Table2 - 3, null

Common'daki satırın dönmesini istiyoruz çünkü diğer tabloların hiçbirinde yok. Ancak, sıfır bir maymun anahtarına atar.

Bu değerlerle sorgu şuna eşdeğerdir:

select *
from Common
where 1 not in (2)
and 1 not in (3, null)

Bu şuna eşdeğerdir:

select *
from Common
where not (1=2)
and not (1=3 or 1=null)

Sorunun başladığı yer burasıdır. Bir boş ile karşılaştırırken , cevap bilinmiyor . Böylece sorgu,

select *
from Common
where not (false)
and not (false or unkown)

yanlış veya bilinmeyen bilinmiyor:

select *
from Common
where true
and not (unknown)

true ve not unkown da bilinmemektedir:

select *
from Common
where unknown

Nerede koşulu, sonucun bilinmediği kayıtları döndürmez, bu nedenle geri kayıt almayız.

Bununla başa çıkmanın bir yolu, içinde değil de var operatörünü kullanmaktır. Varlıklar, sütunlar yerine satırlar üzerinde çalıştığı için hiçbir zaman bilinmeyen döndürmez. (Bir satır vardır veya yoktur; satır düzeyinde bu boş belirsizliğin hiçbiri!)

select *
from Common
where not exists (select common_id from Table1 where common_id = Common.common_id)
and not exists (select common_id from Table2 where common_id = Common.common_id)

2

bu benim için çalıştı :)

Ortaktan * seçin

nerede

common_id not in ( Tablo1'den ISNULL (ortak_id, 'kukla veri') seçin)

ve common_id not in ( Tablo2'den ISNULL (common_id, 'dummy-data') seçin)


@marlar, alt sorgular bir değer listesi değil her zaman 1 veya 0 döndürür. Peki NOT INorada nasıl performans gösterecek ?
Istiaque Ahmed

0
select *,
(select COUNT(ID)  from ProductMaster where ProductMaster.CatID = CategoryMaster.ID) as coun 
from CategoryMaster

0

Baktığım bir örneğim vardı ve bir tablo değeri double, diğeri string olarak tuttuğu için eşleşmiyorlardı (veya cast olmadan eşleşmiyorlardı). Ama sadece İÇİNDE DEĞİL . Gibi SELECT'in ... IN ... çalıştı. Tuhaf, ancak başka birinin bu basit düzeltmeyle karşılaşması durumunda paylaşacağımı düşündüm.


0

Yukarıdaki konuyu anlamak için lütfen aşağıdaki örneği izleyin:

Ayrıca katılmama karşıtı bilmek için aşağıdaki bağlantıyı ziyaret edebilirsiniz.

select department_name,department_id from hr.departments dep
where not exists 
    (select 1 from hr.employees emp
    where emp.department_id=dep.department_id
    )
order by dep.department_name;
DEPARTMENT_NAME DEPARTMENT_ID
Benefits    160
Construction    180
Contracting 190
.......

Ancak NOT INbu durumda kullanırsak herhangi bir veri alamayız.

select Department_name,department_id from hr.departments dep 
where department_id not in (select department_id from hr.employees );

veri bulunamadı

Bu, ( select department_id from hr.employees) boş bir değer döndürdüğünden ve sorgunun tamamı yanlış olarak değerlendirildiğinden gerçekleşir. SQL'i aşağıdaki gibi biraz değiştirirsek ve NVL fonksiyonu ile null değerleri ele alırsak bunu görebiliriz.

select Department_name,department_id from hr.departments dep 
where department_id not in (select NVL(department_id,0) from hr.employees )

Şimdi veri alıyoruz:

DEPARTMENT_NAME DEPARTMENT_ID
Treasury    120
Corporate Tax   130
Control And Credit  140
Shareholder Services    150
Benefits    160
....

NVL fonksiyonu ile boş değeri ele aldığımız için yine veri alıyoruz.


SQl sonuçları tablo şeklinde görünmüyor, lütfen çıplak bırakın.
Rajesh Sarkar
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.