Daha hızlı nedir, MySQL'de DISTINCT veya GROUP BY'ı SEÇİN?


273

Bir masam varsa

CREATE TABLE users (
  id int(10) unsigned NOT NULL auto_increment,
  name varchar(255) NOT NULL,
  profession varchar(255) NOT NULL,
  employer varchar(255) NOT NULL,
  PRIMARY KEY  (id)
)

ve professionalanın tüm benzersiz değerlerini almak istiyorum , daha hızlı (veya önerilen):

SELECT DISTINCT u.profession FROM users u

veya

SELECT u.profession FROM users u GROUP BY u.profession

?


2
Soruyu sorar sormaz kendiniz test edebilirsiniz. Tahriş edici olarak, DISTINCT'in GROUP BY'den daha iyi performans gösterdiği bir senaryo oluşturmak neredeyse imkansızdır - bu can sıkıcıdır, çünkü bu açıkça GROUP BY'ın amacı değildir. Ancak, GROUP BY yanıltıcı sonuçlar üretebilir, bence bundan kaçınmak için yeterli bir neden.
Çilek

Farklı bir cevabı olan başka bir kopya var. bkz. MySql - Distinct vs Group By <<< diyor GROUP BY daha iyi
kolunar

Sorgunuzu çalıştırarak DISTINCT ve GROUP BY arasındaki zaman farkını ölçmek istiyorsanız lütfen buraya bakın .
kolunar

Yanıtlar:


258

Bunlar aslında birbirine eşdeğerdir (aslında bazı veritabanları DISTINCTkaputun altında bu şekilde uygulanır ).

Eğer onlardan biri daha hızlıysa, olacak DISTINCT. Bunun nedeni, ikisi aynı olmasına rağmen, bir sorgu iyileştiricisinin GROUP BYherhangi bir grup üyesinden değil, yalnızca anahtarlarından yararlanmadığı gerçeğini yakalamak zorunda kalmasıdır. DISTINCTbunu açıkça yapar, böylece biraz dumber optimize ediciden kurtulabilirsiniz.

Şüphe duyduğunuzda test edin!


76
DISTINCT yalnızca bir dizine sahip değilseniz (sıralamadığından) daha hızlı olacaktır. Bir indeksiniz olduğunda ve dizininiz kullanıldığında, eşanlamlıdır.
Quassnoi

10
Bunun tanımı DISTINCTve GROUP BYfarkı DISTINCT, çıktıyı sıralamak zorunda değildir ve GROUP BYvarsayılan olarak yapar. Ancak, MySQL bile DISTINCT+ ORDER BYolabilir hala daha hızlı bir daha olmasını GROUP BYSquareCog tarafından açıklandığı gibi bağlı optimizer için fazladan ipuçları.
rustyx

1
DISTINCT büyük miktarda verilerle çok daha hızlı.
Pankaj Wanjari

7
Bunu test ettim ve endeksli bir sütun, mysql, grup tarafından oldukça karmaşık bir sorgu ile yaklaşık 6 kat daha yavaş olduğunu bulundu. Bunu veri noktası olarak eklemeniz yeterli. Yaklaşık 100 bin satır. Bu yüzden test edin ve kendiniz görün.
Lizardx

bkz MySql - Grup By vs Farklı <<< o GRUP TARAFINDAN daha iyi olduğunu söylüyor
kolunar

100

Üzerinde bir dizininiz varsa profession, bu ikisi eşanlamlıdır.

Yapmazsanız, kullanın DISTINCT.

GROUP BYiçinde MySQLsıralar sonuçları. Hatta şunları yapabilirsiniz:

SELECT u.profession FROM users u GROUP BY u.profession DESC

ve mesleklerinizi DESCsırayla sıralayın.

DISTINCTgeçici bir tablo oluşturur ve yinelenenleri depolamak için kullanır. GROUP BYaynı şeyi yapar, ancak daha sonra farklı sonuçları sıralar.

Yani

SELECT DISTINCT u.profession FROM users u

üzerinde bir dizininiz yoksa daha hızlıdır profession.


6
Ekleyebilir ORDER BY NULLiçin GROUP BYsıralama önlemek için.
Ariel

Null tarafından gruplandırma ile bile daha yavaş
Thanh Trung

@ThanhTrung: Ne olduğundan daha yavaş nedir?
Quassnoi

@Quassnoi grouptarafından farklı olsa bile daha yavaş
Thanh Trung

Not: GROUP BY'daki sipariş niteleyicileri MySQL 8'de kullanımdan kaldırıldı
Matthew Lenz

18

Tek bir sütunda DISTINCT, tek bir sütunda GROUP BY'a karşılık yukarıdaki tüm cevaplar doğrudur. Her db motorunun kendi uygulaması ve optimizasyonları vardır ve çok az farkla ilgileniyorsanız (çoğu durumda) o zaman belirli bir sunucuya ve belirli bir versiyona karşı test etmek zorundasınız! Uygulamalar değişebileceğinden ...

ANCAK, sorguda birden fazla sütun seçerseniz, DISTINCT aslında farklıdır! Çünkü bu durumda, tek bir sütun yerine tüm satırların TÜM sütunlarını karşılaştıracaktır.

Yani şöyle bir şey varsa:

// This will NOT return unique by [id], but unique by (id,name)
SELECT DISTINCT id, name FROM some_query_with_joins

// This will select unique by [id].
SELECT id, name FROM some_query_with_joins GROUP BY id

DISTINCT anahtar kelimesinin satırları belirttiğiniz ilk sütuna göre ayırdığını düşünmek yaygın bir hatadır, ancak DISTINCT bu şekilde genel bir anahtar kelimedir.

Bu yüzden, yukarıdaki yanıtları tüm durumlar için doğru olarak almamaya dikkat etmelisiniz ... İstediğiniz tek şey optimize etmek iken kafanız karışabilir ve yanlış sonuçlar elde edebilirsiniz!


3
Bu soru rağmen olan MySQL hakkında ikinci sorgu çalışacak unutulmamalıdır sadece MySQL. Neredeyse diğer tüm DBMS, GROUP BY işlecinin geçersiz kullanımı nedeniyle ikinci ifadeyi reddedecektir.
a_horse_with_no_name

Peki, "neredeyse" sorunlu bir tanım :-) Bu ifade için bir hata oluşturduğunu görmek için test ettiğiniz belirli bir DBMS belirtirseniz çok daha yararlı olacaktır .
daniel.gindi

3
Yeni başlayanlar için Postgres, Oracle, Firebird, DB2, SQL Server. MySQL: sqlfiddle.com/#!2/6897c/1 Postgres: sqlfiddle.com/#!12/6897c/1 Oracle: sqlfiddle.com/#!12/6897c/1 SQL Server: sqlfiddle.com/#!6/ 6897c / 1
a_horse_with_no_name

17

Mümkünse en basit ve en kısa yoldan gidin - DISTINCT aradığınız şeyden daha fazlası gibi görünüyor çünkü size tam olarak ihtiyacınız olan cevabı verecek ve sadece bu!


7

Group by, Distinct'den daha pahalıdır, çünkü Group by, sonuç üzerinde bir tür bir şey yaparken, farklı ise bundan kaçınır. Ama grup verimi ile ayrı sonuç vermek gibi aynı sonucu vermek istiyorsanız null ..

SELECT DISTINCT u.profession FROM users u

eşittir

SELECT u.profession FROM users u GROUP BY u.profession order by null

eşittirSELECT profession FROM users GROUP BY profession

6

posttres bazı durumlarda gruptan daha iyi olabilir (diğer dbs hakkında bilmiyorum).

test edilmiş örnek:

postgres=# select count(*) from (select distinct i from g) a;

count 

10001
(1 row)

Time: 1563,109 ms

postgres=# select count(*) from (select i from g group by i) a;

count
10001
(1 row)

Time: 594,481 ms

http://www.pgsql.cz/index.php/PostgreSQL_SQL_Tricks_I

yani dikkatli ol ... :)


5

Sorguların tamamen aynı olmadığı anlaşılıyor. En azından MySQL için.

Karşılaştırmak:

  1. northwind.products adresinden seçkin ürün adını seçin
  2. northwind.products grubundan ürün adını seçin

İkinci sorgu Ek olarak "filesort kullanma" ek verir.


1
Nasıl aldıkları açısından değil, aldıklarıyla aynıdırlar. İdeal bir optimize edici bunları aynı şekilde yürütür, ancak MySQL optimizer ideal değildir. Kanıtlarınıza dayanarak, DISTINCT'in daha hızlı gideceği anlaşılıyor - O (n) ve O (n * log n).
SquareCog

Yani, "filesort kullanmak" aslında kötü bir şey mi?
vava

Bu durumda, çünkü sıralamanız gerekmez (gruplara ihtiyacınız varsa yaparsınız). MySQL, aynı girişleri bir araya getirmek ve sıralanan dosyayı tarayarak grupları almak için sıralar. Sadece farklılara ihtiyacınız var, bu yüzden tek bir tablo taraması yaparken anahtarlarınızı hash etmeniz gerekiyor.
SquareCog

1
Ekle ORDER BY NULLiçin GROUP BYsürümü ve onlar aynı olacaktır.
Ariel

3

In MySQL , " Group By" fazladan bir adım kullanır: filesort. Daha DISTINCThızlı olduğunu fark ettim GROUP BYve bu bir sürprizdi.


3

Ağır testlerden sonra GROUP BY'ın daha hızlı olduğu sonucuna vardık

Sql_no_cache opnamegroep_intern telwerken NEREDEN opnemergroep(7,8,9,10,11,12,13) ​​gruptan opnamegroep_intern'e göre SEÇİN

635 toplam 0.0944 saniye Weergave van kayıtları 0 - 29 (635 toplam, sorgu duurde 0.0484 sn)

Sql_no_cache farklı (opnamegroep_intern) telwerken NEREDEN GİRİN opnemergroep(7,8,9,10,11,12,13)

635 toplam 0.2117 saniye (neredeyse% 100 daha yavaş) Weergave minibüs kayıtları 0 - 29 (635 toplam, sorgu duurde 0.3468 sn)


2

(daha işlevsel bir not)

GROUP BY kullanmanız gereken durumlar vardır, örneğin işveren başına çalışan sayısını almak istiyorsanız:

SELECT u.employer, COUNT(u.id) AS "total employees" FROM users u GROUP BY u.employer

Böyle bir senaryoda DISTINCT u.employerdoğru çalışmaz. Belki de bir yolu vardır, ama bilmiyorum. (Birisi DISTINCT ile böyle bir sorguyu nasıl yapacağını bilirse lütfen bir not ekleyin!)


2

İşte her sorgu için 2 farklı geçen süreyi yazdıracak basit bir yaklaşım.

DECLARE @t1 DATETIME;
DECLARE @t2 DATETIME;

SET @t1 = GETDATE();
SELECT DISTINCT u.profession FROM users u; --Query with DISTINCT
SET @t2 = GETDATE();
PRINT 'Elapsed time (ms): ' + CAST(DATEDIFF(millisecond, @t1, @t2) AS varchar);

SET @t1 = GETDATE();
SELECT u.profession FROM users u GROUP BY u.profession; --Query with GROUP BY
SET @t2 = GETDATE();
PRINT 'Elapsed time (ms): ' + CAST(DATEDIFF(millisecond, @t1, @t2) AS varchar);

VEYA SET İSTATİSTİK ZAMANI (Transact-SQL) deneyin

SET STATISTICS TIME ON;
SELECT DISTINCT u.profession FROM users u; --Query with DISTINCT
SELECT u.profession FROM users u GROUP BY u.profession; --Query with GROUP BY
SET STATISTICS TIME OFF;

Her ifadeyi ayrıştırmak, derlemek ve yürütmek için gereken milisaniye sayısını aşağıdaki gibi görüntüler:

 SQL Server Execution Times:
   CPU time = 0 ms,  elapsed time = 2 ms.

1

Bu bir kural değil

Her sorgu için .... ayrı ayrı deneyin ve sonra gruplandırmak ... her sorguyu tamamlamak için zaman karşılaştırmak ve daha hızlı kullanın ....

Projemde bazen gruba göre ve diğerlerini farklı kullanıyorum


0

Herhangi bir grup işlevi (tabloya sayısal veri eklemek istemeniz durumunda toplam, ortalama vb.) Yapmak zorunda değilseniz, SELECT DISTINCT işlevini kullanın. Daha hızlı olduğundan şüpheleniyorum, ancak gösterecek hiçbir şeyim yok.

Her durumda, hız konusunda endişeleniyorsanız, sütunda bir dizin oluşturun.


0

SELECT DISTINCT her zaman GROUP BY ile aynı veya daha hızlı olacaktır. Bazı sistemlerde (yani Oracle), çoğu sorgu için DISTINCT ile aynı olacak şekilde optimize edilebilir. Diğerlerinde (SQL Server gibi), çok daha hızlı olabilir.


0

Sorun buna izin veriyorsa, sonuç bulunur bulunmaz bitecek şekilde optimize edildiğinden (Ve herhangi bir yanıtı arabelleğe almayın) bu nedenle EXISTS ile deneyin, bu nedenle, böyle bir WHERE yan tümcesi için verileri normalleştirmeye çalışıyorsanız

SELECT FROM SOMETHING S WHERE S.ID IN ( SELECT DISTINCT DCR.SOMETHING_ID FROM DIFF_CARDINALITY_RELATIONSHIP DCR ) -- to keep same cardinality

Daha hızlı bir yanıt:

SELECT FROM SOMETHING S WHERE EXISTS ( SELECT 1 FROM DIFF_CARDINALITY_RELATIONSHIP DCR WHERE DCR.SOMETHING_ID = S.ID )

Bu her zaman mümkün değildir, ancak mevcut olduğunda daha hızlı bir yanıt görürsünüz.

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.