Birden çok sütunda DISTINCT'i nasıl SEÇEBİLİRİM?


415

2 Sütun birleştirilmiş tüm farklı bir tablodan tüm satırları almak gerekir. Bu yüzden aynı günde gerçekleşen başka bir satışa sahip olmayan tüm satışları aynı fiyata istiyorum. Gün ve fiyata göre benzersiz olan satışlar etkin duruma güncellenir.

Bu yüzden düşünüyorum:

UPDATE sales
SET status = 'ACTIVE'
WHERE id IN (SELECT DISTINCT (saleprice, saledate), id, count(id)
             FROM sales
             HAVING count = 1)

Ama beynim bundan daha fazla acıyor.

Yanıtlar:


436
SELECT DISTINCT a,b,c FROM t

olduğu kabaca eşdeğer:

SELECT a,b,c FROM t GROUP BY a,b,c

Daha güçlü olduğu için GROUP BY sözdizimine alışmak iyi bir fikirdir.

Sorgunuz için bunu şu şekilde yaparım:

UPDATE sales
SET status='ACTIVE'
WHERE id IN
(
    SELECT id
    FROM sales S
    INNER JOIN
    (
        SELECT saleprice, saledate
        FROM sales
        GROUP BY saleprice, saledate
        HAVING COUNT(*) = 1 
    ) T
    ON S.saleprice=T.saleprice AND s.saledate=T.saledate
 )

117
Bu sorgu doğru ve yıllarca kabul edilirken son derece verimsiz ve gereksizdir. Bunu kullanma. Başka bir cevapta bir alternatif ve bazı açıklamalar sağladım.
Erwin Brandstetter

1
SELECT DISTINCT a, b, c FROM t a, b, c grubundan SELECT a, b, c ile tam olarak aynı şey değil mi?
famargar

8
@famargar henüz basit durum için, ama anlamsal olarak farklı anlamları vardır ve daha büyük bir sorgu oluştururken adım için neler yapabileceğiniz açısından farklıdırlar. Ayrıca, teknik forumlardaki insanlar genellikle şeyler hakkında son derece bilgiç olabilirler , bu bağlamda yayınlarıma çakal sözcükleri eklemenin genellikle yararlı olduğunu düşünüyorum.
Joel Coehoorn

344

Şimdiye kadar cevapları bir araya getirirseniz, temizleyip geliştirirseniz, bu üstün sorguya ulaşırsınız:

UPDATE sales
SET    status = 'ACTIVE'
WHERE  (saleprice, saledate) IN (
    SELECT saleprice, saledate
    FROM   sales
    GROUP  BY saleprice, saledate
    HAVING count(*) = 1 
    );

Hangisi çok daha hızlı. Şu anda kabul edilen cevabın performansını 10-15 faktörü (PostgreSQL 8.4 ve 9.1'deki testlerimde) yerine getirir.

Ama bu hala optimal olmaktan uzak. KullanınNOT EXISTSDaha da iyi performans için (anti-) yarı birleştirme . EXISTSstandart SQL'dir, sonsuza kadar olmuştur (en azından PostgreSQL 7.2'den bu soru sorulmadan çok önce) ve sunulan gereksinimlere mükemmel şekilde uyar:

UPDATE sales s
SET    status = 'ACTIVE'
WHERE  NOT EXISTS (
   SELECT FROM sales s1                     -- SELECT list can be empty for EXISTS
   WHERE  s.saleprice = s1.saleprice
   AND    s.saledate  = s1.saledate
   AND    s.id <> s1.id                     -- except for row itself
   )
AND    s.status IS DISTINCT FROM 'ACTIVE';  -- avoid empty updates. see below

db <> keman burada
Eski SQL Keman

Satırı tanımlamak için benzersiz anahtar

Tablo için birincil veya benzersiz bir anahtarınız yoksa (id örnekte), ctidbu sorgunun amacına yönelik olarak sistem sütununu kullanabilirsiniz (ancak başka bir amaçla kullanamazsınız):

   AND    s1.ctid <> s.ctid

Her tablonun birincil anahtarı olmalıdır. Henüz sahip değilseniz bir tane ekleyin. Bir serialveya birIDENTITYPostgres 10 + ' sütun .

İlişkili:

Bu nasıl daha hızlı?

EXISTSAnti-semi- join'deki alt sorgu ilk dupe bulunur bulunmaz değerlendirmeyi durdurabilir (daha fazla bakmanın anlamı yoktur). Birkaç kopya içeren bir temel tablo için bu sadece biraz daha verimlidir. Yinelenenler dolu bu hale yolu daha verimli.

Boş güncellemeleri hariç tut

status = 'ACTIVE'Bu güncelleştirmeyi zaten içeren satırlar için hiçbir şey değişmez, ancak yine de tam maliyetle yeni bir satır sürümü ekler (küçük istisnalar geçerlidir). Normalde bunu istemezsiniz. Başka birtane ekleWHERE kaçınmak ve daha da hızlı hale getirmek için yukarıda gösterildiği gibi koşul :

Eğer statustanımlanır NOT NULLşunları yapmanız kolaylaştırabilirsiniz:

AND status <> 'ACTIVE';

Sütunun veri türü <>operatörü desteklemelidir . Bazı türler jsonyok. Görmek:

NULL kullanımda küçük fark

Bu sorgu ( Joel tarafından şu anda kabul edilen cevabın aksine ) NULL değerlere eşit muamele etmemektedir. Aşağıdaki iki satır (saleprice, saledate)"farklı" olarak nitelendirilir (insan gözüyle aynı görünmesine rağmen):

(123, NULL)
(123, NULL)

Benzersiz bir dizinde ve hemen hemen her yerde de geçer, çünkü NULL değerleri SQL standardına göre eşit değildir. Görmek:

OTOH, GROUP BY, DISTINCTya da DISTINCT ON ()eşit olarak muamele NULL değerleri. Neyi başarmak istediğinize bağlı olarak uygun bir sorgu stili kullanın. Bu daha hızlı sorguyu kullanmak IS NOT DISTINCT FROMyerine=NULL karşılaştırmayı eşitlemek için herhangi bir veya tüm karşılaştırmalar . Daha:

Karşılaştırılan tüm sütunlar tanımlanmışsa NOT NULL, anlaşmaya yer yoktur.


16
İyi cevap. Ben bir sql sunucusu adamım, bu yüzden IN () onaylı bir tuple kullanarak ilk öneri bana olmazdı. Mevcut olmayan öneri genellikle sql sunucusunda iç birleşim ile aynı yürütme planı ile sonuçlanacaktır.
Joel Coehoorn

2
Güzel. Açıklama, cevabın değerini büyük ölçüde arttırmaktadır. Planların Postgres ve SQLServer ile nasıl karşılaştırıldığını görmek için Oracle ile bazı testler yapmaya neredeyse cazip geliyorum.
Peter

2
@alairock: Bunu nereden buldun? Postgres için bunun tersi geçerlidir. Tüm satırları sayarken count(*), daha verimlidir count(<expression>). Sadece dene. Postgres, toplama işlevinin bu varyantı için daha hızlı bir uygulamaya sahiptir. Belki Postgres'i diğer bazı RDBMS'lerle karıştırıyorsunuz?
Erwin Brandstetter

6
@alairock: Bu sayfanın ortak yazarı oldum ve bu tür bir şey söylemiyor.
Erwin Brandstetter

2
@ErwinBrandstetter, her zaman cevaplarınızda her zaman çok kararlısınız. Yıllar boyunca neredeyse düşünülemez miktarda yardım ettiniz. Bu örneğe gelince, sorunumu çözmek için birkaç farklı yol biliyordum, ancak birisinin olasılıklar arasındaki verimliliği test ettiğini görmek istedim. Teşekkür ederim.
WebWanderer

24

Sorgunuzla ilgili sorun, bir GROUP BY deyimi kullanırken (esas olarak farklı kullanarak), yalnızca gruplama veya işlevleri bir araya getirdiğiniz sütunları kullanabilmenizdir. Potansiyel olarak farklı değerler olduğundan sütun kimliğini kullanamazsınız. Sizin durumunuzda HAVING yan tümcesi nedeniyle her zaman yalnızca bir değer vardır, ancak çoğu RDBMS bunu tanıyacak kadar akıllı değildir.

Ancak bunun çalışması gerekir (ve katılmaya gerek yoktur):

UPDATE sales
SET status='ACTIVE'
WHERE id IN (
  SELECT MIN(id) FROM sales
  GROUP BY saleprice, saledate
  HAVING COUNT(id) = 1
)

MIN yerine MAX veya AVG de kullanabilirsiniz, yalnızca bir eşleşen satır varsa sütunun değerini döndüren bir işlev kullanmak önemlidir.


1

Bir sütun 'GrondOfLucht' farklı değerleri seçmek istiyorum, ancak sütun 'sıralayıcı' olarak verilen sırada sıralanmalıdır. Kullanarak yalnızca bir sütunun farklı değerlerini alamıyorum

Select distinct GrondOfLucht,sortering
from CorWijzeVanAanleg
order by sortering

Ayrıca 'sıralayıcı' sütununu verecektir ve 'GrondOfLucht' VE 'sıralayıcı' benzersiz olmadığından sonuç TÜM satırlar olacaktır.

'Sorgulama' tarafından verilen sırada 'GrondOfLucht' kayıtlarını seçmek için GROUP'u kullanın

SELECT        GrondOfLucht
FROM            dbo.CorWijzeVanAanleg
GROUP BY GrondOfLucht, sortering
ORDER BY MIN(sortering)

Bu temel olarak kabul edilen cevabın ne yaptığını açıklar, ancak bir örnek için bu tür isimler kullanılmamasını tavsiye ederim (en azından tercüme et). Not: Hollandalı olsanız bile, her projede her zaman İngilizce her şeyi adlandırmanızı öneririm.
Kerwin Sneijders

0

DBMS'niz aşağıdakine benzer birden çok sütunla farklı özellikleri desteklemiyorsa:

select distinct(col1, col2) from table

Genel olarak çoklu seçim aşağıdaki gibi güvenli bir şekilde yürütülebilir:

select distinct * from (select col1, col2 from table ) as x

Bu, DBMS'nin çoğunda çalışabileceğinden ve gruplama işlevinden kaçındığından, bu çözümün gruptan daha hızlı olması beklenir.

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.