SQL Server 2012'de select * hala büyük bir hayır mı?


41

Yıllar öncesindeki günlerde, yapılacak performans nedeniyle select * from tableya da yapılacak büyük bir hayır olarak kabul edildi select count(*) from table.

SQL Server'ın sonraki sürümlerinde bu hala geçerli mi (2012 kullanıyorum, ancak sorunun 2008-2014 için geçerli olacağını düşünüyorum)?

Düzenleme: İnsanlar burada beni biraz yavaşlatıyor gibi gözüktüğü için, bunu yapmak için "doğru" bir şey olup olmadığına bakmadan, bir değerlendirme / akademik bakış açısıyla bakıyorum.

Yanıtlar:


50

Eğer SELECT COUNT(*) FROM TABLEsadece bir satır döndürürseniz (sayım), göreceli olarak hafiftir ve bu referansı alma yoludur.

Ve SELECT *fiziksel bir hayır-hayır değildir, çünkü yasaldır ve izin verilir.

Ancak, sorun SELECT *şu ki, çok daha fazla veri hareketine neden olabilirsiniz. Tablodaki her sütunda çalışıyorsunuz. Eğer senin SELECTsadece birkaç sütunları içerir, sen I / O ve ayrıca sunucu cache üzerindeki etkisini azaltan bir dizin veya endeksler, adresinin cevap almak mümkün olabilir.

Bu nedenle, genel bir uygulama olarak Evet , kaynaklarınız için boşa harcandığından tavsiye edilir.

Bunun tek yararı SELECT *tüm sütun adlarını yazmak değildir. Ancak SSMS'den sorgunuzdaki sütun adlarını almak ve ihtiyacınız olmayanları silmek için sürükle ve bırak özelliğini kullanabilirsiniz.

Bir benzetme: Birisi kullandığı takdirde SELECT *onlar her sütun gerekmez, onlar olurdu da kullanmak SELECTbir olmadan WHEREonlar her satır gerekmediğinde (veya başka bir sınırlama maddesi)?


24

Zaten sağlayıcının yanıtına ek olarak, Entity Framework gibi modern ORM'lerle çalışırken geliştiricilerin genellikle çok tembel olduğunu belirtmeye değer olduğunu düşünüyorum. DBA'lar önlemek için ellerinden geleni yapmaya çalışsa da SELECT *, geliştiriciler genellikle semantik olarak eşdeğerini yazarlar, örneğin, c # Linq:

var someVariable = db.MyTable.Where(entity => entity.FirstName == "User").ToList();

Temelde, bu aşağıdakilerle sonuçlanacaktır:

SELECT * FROM MyTable WHERE FirstName = 'User'

Daha önce kapatılmayan ilave bir ek yük de vardır. Her satırdaki her sütunu ilgili nesneye işlemek için gereken kaynaklar budur. Ayrıca, bellekte tutulan her nesne için, o nesnenin temizlenmesi gerekir. Yalnızca gereksinim duyduğunuz sütunları seçtiyseniz, 100mb ram aşağısından kolayca tasarruf edebilirsiniz. Tek başına büyük bir miktar olmasa da, çöp toplama vb.

Yani evet, en azından benim için, her zaman büyük bir hayır olacak ve olacak. Bunu yapmanın “gizli” maliyetleri hakkında da eğitim almamız gerekiyor.

ek

İşte açıklamalarda istendiği gibi sadece ihtiyacınız olan verileri çekmeye bir örnek:

var someVariable = db.MyTable.Where(entity => entity.FirstName == "User")
                             .Select(entity => new { entity.FirstName, entity.LastNight });

13

Performans: SELECT * ile bir sorgu muhtemelen asla bir kaplama sorgusu olmayacak ( Basit konuşma açıklaması , Yığın Taşması açıklaması ).

Gelecekte prova: Sorgunuz bugün yedi sütunun tümünü döndürebilir, ancak birisi gelecek yıl boyunca beş sütun eklerse, o zaman bir yıl içinde sorgunuz on iki sütun döndürerek IO ve CPU'yu boşa harcar.

Dizin Oluşturma: Görünümlerinizin ve tablo değerli işlevlerin SQL Server'da dizin oluşturmaya katılmasını istiyorsanız, bu görünümlerin ve işlevlerin, SELECT * kullanımını yasaklayan şema oluşturma ile oluşturulması gerekir.

En iyi uygulama : SELECT *üretim kodunda asla kullanmayın .

Alt sorgular için tercih ederim WHERE EXISTS ( SELECT 1 FROM … ).

Düzenleme : Craig Young'ın aşağıdaki yorumuna hitap etmek için, bir alt sorguda "SELECT 1" i kullanmak bir "" optimizasyon "değildir - bu yüzden sınıfımın önünde ayağa kalkıp" SELECT * kullanmayın, istisnalar yok "diyebilirim! "

Aklıma gelen tek istisna hakkında, müşterinin bir tür pivot-tablo işlemi yaptığı ve şimdiki ve gelecekteki tüm sütunları gerektirdiği yerdir.

CTE'leri ve türetilmiş tabloları içeren bir istisnayı kabul edebilirim, ancak yürütme planlarını görmek istiyorum.

COUNT(*)"*" Nin farklı bir sözdizimsel kullanımı olduğundan, bunun bir istisna olduğunu düşündüğümü unutmayın .


10

SQL Server 2012'de (veya 2005'ten sonraki herhangi bir sürümde), SELECT *...bir sorgunun en üst düzey SELECT ifadesinde yalnızca olası bir performans sorunudur.

O CTEs var maddelerinde alt sorgular içinde Görünümler (*),, sorunun değil, ne Yani SELECT COUNT(*)..vs vs Not, bu Oracle için de muhtemelen doğrudur ve DB2 ve belki PostgreS (değil emin) Ancak, birçok durumda hala MySql için bir sorun olması muhtemeldir.

Nedenini (ve neden hala üst düzey bir SEÇİM'de bir sorun olabileceğini) anlamak için, bunun neden bir sorun olduğunu anlamakta fayda var, bunun nedeni SELECT *.." sütunların TÜMÜNÜ döndür " anlamına gelmesidir . Genel olarak bu, gerçekten istediğinizden çok daha fazla veri döndürecektir ; bu, hem disk hem de ağ üzerinde çok daha fazla GÇ'ye neden olabilir.

Daha az belirgin olan şey, bunun bir SQL optimizer'ın kullanabileceği endeksleri ve sorgu planlarını da sınırlandırmasıdır, çünkü sonuçta tüm veri sütunlarını döndürmesi gerektiğini bilir. Önceden yalnızca belirli sütunlar istediğinizi biliyorsa, yalnızca bu sütunları içeren dizinlerden yararlanarak daha verimli sorgu planları kullanabilir. Neyse ki, bunu önceden bilmenizin bir yolu var, bu da sütun listesinde istediğiniz sütunları açıkça belirtmeniz içindir. Ama "*" kullandığın zaman, bunu "bana her şeyi ver, ihtiyacım olanı çözeceğim" lehine bekliyorsun.

Evet, ayrıca her sütunu işlemek için ek CPU ve bellek kullanımı da vardır, ancak bu iki şeye kıyasla neredeyse her zaman küçüktür: İhtiyacınız olmayan sütunlar için gereken önemli ekstra disk ve ağ bant genişliği ve daha az kullanmanız gerekir Her sütunu içermesi gerektiği için optimize edilmiş sorgu planı.

Peki ne değişti? Temel olarak, SQL Optimizers başarılı bir şekilde "Sütun Optimizasyonu" adında bir özellik eklediler ki bu, sorgunun üst seviyelerinde bir sütun kullanacaksanız, şimdi alt seviye alt sorgularda çözebileceklerini şimdi anlayabiliyorlar.

Bunun bir sonucu olarak, bir sorgunun alt / iç seviyelerinde 'SELECT * ..' kullanmanız artık önemli değil. Bunun yerine, asıl önemli olan, üst düzey SELECT'in sütun listesinde bulunan şeydir. SELECT *..En üstte kullanmıyorsanız , bir kez daha kullanın, sütunların TÜMÜNÜ istediğinizi varsayalım ve bu nedenle sütun optimizasyonlarını etkin bir şekilde kullanamazsınız.

(* - *"*" kullanıldığında sütun listelerindeki değişikliği her zaman kaydetmedikleri Görünümlerde farklı, küçük bir ciltleme sorunu olduğunu unutmayın . Bunu ele almanın başka yolları da vardır ve performansı etkilemez.)


5

Kullanmamak için küçük bir neden daha var SELECT *: döndürülen sütunların sırası değişirse, başvurunuz kesilir ... eğer şanslıysanız. Değilse, uzun süre tespit edilemeyecek ince bir böcek yaşayacaksınız. Bir tablodaki alanların sırası bir kullanırsanız bile görülebilir is sadece zaman olarak, uygulamalar tarafından düşünülmemelidir bir uygulama ayrıntıdır SELECT *.


4
Bu konu dışı. Uygulama kodunuzdaki sütun dizinine göre sütunlara erişiyorsanız, bozuk bir uygulamaya sahip olmayı hak ediyorsunuz. Sütunlara ada göre erişmek, her zaman çok daha okunabilir bir uygulama kodu oluşturur ve neredeyse hiçbir zaman performans darboğazı olmaz.
Lie Ryan

3

Fiziksel ve problematik olarak kullanmasına izin verilir select * from table, ancak bu kötü bir fikirdir. Neden?

Her şeyden önce, ihtiyacınız olmayan sütunları döndürdüğünüzü (ağır kaynak) göreceksiniz.

İkincisi, sütunları adlandırmaktan daha uzun sürer çünkü * 'yi seçtiğinizde, aslında veritabanındaki sütun adlarını seçersiniz ve "bana bu listede başka bir addaki sütunları içeren verileri verin" diyorsunuz. ." Programcı için bu hızlı olsa da, bir bankanın bilgisayarında bir dakika içinde yüz binlerce arama yapmış olabilecek bir arama yapmayı düşünün.

Üçüncüsü, bunu yapmak aslında geliştirici için zorlaşıyor. Tüm sütun adlarını bulmak için SSMS'den VS'ye ne sıklıkla geri gitmeniz gerekir?

Dördüncü olarak, tembel bir programlama işaretidir ve hiçbir geliştiricinin bu itibarı isteyeceğini düşünmüyorum.


Bu güncel formdaki ikinci argümanınızın bazı küçük hatalar var. İlk olarak, tüm RDBMS tabloların şemasını önbelleğe alır, çünkü çoğunlukla şemada sorgulama aşamasında hangi sütunun var olduğunu ya da sorgudaki tabloda eksik olanı belirlemek için sorgu ayrıştırma aşamasında yüklenir. Bu nedenle, sorgu çözümleyici zaten sütun adı listesini kendi başına sorguladı ve anında * bir sütun listesiyle değiştirir. Ardından, çoğu RDBMS motoru, elinden gelenin en iyisini önbelleğe almaya çalışır, bu nedenle SELECT * FROM tablosunu verirseniz, derleme işlemi her seferinde gerçekleşmemesi için derlenmiş sorgu önbelleğe alınır. Ve geliştiriciler tembel :-)
Gabor Garami

İkinci argümanınıza gelince, bu yaygın bir yanılgıdır - SELECT * ile ilgili sorun meta veri araması değildir, çünkü sütunları adlandırırsanız, SQL Server hala adlarını doğrulamak, veri türlerini kontrol etmek zorunda kalır.
Aaron Bertrand

@Gabor SELECT * ile ilgili sorunlardan biri, bunu bir görünüme sokarken olur. Altta yatan şemayı değiştirirseniz, görüşün kafası karışabilir - artık tablonun şemasını (kendi) kavramını tablonun kendisinden farklı bir şeye sahiptir. Burada bunun hakkında konuşuyorum .
Aaron Bertrand

3

Select * ...Kodu bir programa yerleştirirseniz sorun olabilir , çünkü daha önce belirtildiği gibi veritabanı zaman içinde değişebilir ve sorguyu yazarken beklediğinizden daha fazla sütun içerebilir. Bu, programın başarısız olmasına (en iyi durum) yol açabilir veya program neşeli yoluna devam edebilir ve bazı verileri bozabilir çünkü işlemek için yazılmadığı alan değerlerine bakar. Kısacası, üretim kodu HER ZAMAN. İçinde iade edilecek alanları belirtmelidir SELECT.

Bunu söylediğimde Select *, programın bir EXISTScümlesinin parçası olduğu zaman sorunum daha az olacaktır , çünkü programa geri gönderilecek olanların hepsi, seçimin başarısını veya başarısızlığını gösteren bir booleandır. Diğerleri bu duruşa katılmıyor olabilir ve bu konudaki görüşlerine saygı duyuyorum. Kodlamada Select *, bir EXISTSmaddede 'Select 1'i kodlamaktan biraz daha az verimli olabilir , ancak herhangi bir şekilde veri bozulması tehlikesi olmadığını da düşünüyorum.


Aslında evet, EXISTS maddesine atıfta bulunmak istemiştim. Benim hatam.
Mark Ross,

2

Neden select *yanlış cevaplar çok, bu yüzden doğru ya da en azından Tamam hissettiğimde anlatacağım.

1) Bir EXISTS'te, sorgunun SELECT bölümünün içeriği göz ardı edilir, bu nedenle yazabilirsiniz SELECT 1/0ve hata yapmaz. EXISTSsadece bazı verilerin geri döneceğini ve buna dayanarak bir boolean döndürdüğünü doğrular.

IF EXISTS(
    SELECT * FROM Table WHERE X=@Y
)

2) Bu bir fırtına başlatabilir, ancak select *tarih tablosu tetikleyicilerinde kullanmayı seviyorum . Bu select *sayede, ana tablonun ana tabloya eklendiğinde / güncellendiğinde / silindiğinde hemen hata yapmasıyla ana tablonun sütunu tarih tablosuna eklemeden yeni bir sütun almasını önler. Bu, geliştiricilerin sütun eklediği ve bunu tarih tablosuna eklemeyi unuttuğu sayıları engelledi.


3
Ben hala tercih ediyorum, SELECT 1çünkü en açık şekilde niyetinizle ilgili gelecekteki kod yöneticilerine haber veriyor. Bu bir zorunluluk değil , ama ... WHERE EXISTS (SELECT 1 ...)açıkça görürsem , kendini doğruluk testi olarak ilan ediyor.
swasheck

1
@zlatanMany insanlar SELECT 1performansın daha iyi olacağı bir efsaneye dayanarak kullanıyorlar SELECT *. Ancak, her iki seçenek de tamamen kabul edilebilir. Optimize edicinin EXISTS'i işleme biçimi nedeniyle performansta hiçbir fark yoktur. Ayrıca, doğruluk testini açıkça belirten “EXISTS” kelimesi nedeniyle okunabilirlik açısından herhangi bir fark yoktur.
Disillusioned

2. noktada, gerekçenizi anlıyorum ama yine de riskler var. 'Senin için bir senaryo çizeyim' ... Geliştirici Column8, tarih tablosunu unutarak ana tabloya ekler . Geliştirici, Sütun 8'de gerçekleşen bir sürü kod yazar. Sonra Column9ana tabloya ekler ; bu kez de tarihe eklemeyi hatırladım. Daha sonra test ederken Column9tarihe eklemeyi unuttuğunu fark eder (hata bulma tekniğinize teşekkürler) ve hemen ekler. Şimdi tetikleyici işe yarıyor gibi gözüküyor , ancak 8 ve 9 numaralı sütunlardaki veriler tarihe karışıyor. : S
Disillusioned

devamı ... Mesele şu ki, yukarıdaki “uyuşuk” senaryosu, hata tespitinizin sizi başarısızlığa uğratmasına ve aslında işleri daha da kötüleştirmesine neden olabilecek birçok şeyden sadece biri. Temel olarak daha iyi bir tekniğe ihtiyacınız var. Tetikleyicinize güvenmeyen bir tanesi, seçtiğiniz bir tablodaki sütunların sırası hakkında varsayımlarda bulunur. Öneriler: - Sık rastlanan hataların kontrol listeleriyle birlikte kişisel kod incelemeleri. - Akran kodu incelemeleri. - Geçmişi izlemek için alternatif teknik (şahsen proaktif değil, bu nedenle hatalara eğilimli tetikleyici mekanizmaların tetikleyici olduğunu düşünüyorum).
Hayal kırıklığına uğramış

@CraigYoung Bu bir olasılıktır. Ama bunu yaparlarsa birisini boğardım. Kolayca yapabileceğiniz bir hata değil
UnhandledExcepSean
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.