Select * kullanmamanın nedeni nedir?


136

Bazı kişilerin, seçtiğiniz sorgunuzda istediğiniz her sütuna özel olarak ad vermeniz gerektiğini iddia ettiklerini gördüm.

Yine de tüm sütunları kullanacağımı varsayarsak, neden kullanmayayım SELECT *?

* SQL sorgusu - Görünümden * Seç veya görünümden Seç col1, col2,… colN * sorusu göz önünde bulundurulduğunda bile , konuya biraz farklı bir perspektiften yaklaşırken bunun tam bir kopya olduğunu düşünmüyorum.

İlkelerimizden biri, zamanından önce optimize etmemektir. Bunu akılda tutarak, bir kaynak sorunu olduğu kanıtlanana veya şema hemen hemen taş haline getirilene kadar SELECT *, kullanım tercih edilen yöntem olmalı gibi görünüyor . Bu, bildiğimiz gibi, gelişme tamamen bitene kadar gerçekleşmeyecek.

Bununla birlikte, kullanılmaması gereken önemli bir sorun var SELECT *mı?

Yanıtlar:


168

Zamanından önce optimize etmeme teklifinin özü, basit ve anlaşılır bir kod bulmak ve daha sonra etkin noktaları optimize etmek için sıcak noktaları işaret etmek için bir profiler kullanmaktır.

Select * kullandığınızda, profil oluşturmayı imkansız hale getirirsiniz, bu nedenle açık ve anlaşılır bir kod yazmazsınız ve teklifin ruhuna aykırı hareket edersiniz. select *bir anti-kalıptır.


Dolayısıyla sütunları seçmek erken bir optimizasyon değildir. Kafamın üstünde birkaç şey ....

  1. Bir SQL ifadesinde sütunlar belirtirseniz, bu sütun tablodan kaldırılırsa ve sorgu yürütülürse SQL yürütme motoru hata verir.
  2. Bu sütunun kullanıldığı kodu daha kolay tarayabilirsiniz.
  3. En az miktarda bilgiyi geri getirmek için her zaman sorgu yazmalısınız.
  4. Diğerlerinin belirttiği gibi sıralı sütun erişimi kullanıyorsanız asla select * kullanmamalısınız
  5. SQL ifadeniz tablolara katılırsa, select * size birleştirme içindeki tüm tablolardaki tüm sütunları verir

Sonuç şu ki select *...

  1. Uygulama tarafından kullanılan sütunlar opaktır
  2. DBA'lar ve sorgu profil oluşturucuları uygulamanızın düşük performansına yardımcı olamaz
  3. Değişiklikler meydana geldiğinde kod daha kırılgandır
  4. Veritabanınız ve ağınız acı çekiyor çünkü çok fazla veri geri getiriyorlar (G / Ç)
  5. Veritabanı motoru optimizasyonları, tüm verileri (mantıksal) ne olursa olsun geri getirdiğiniz için minimum düzeydedir.

Doğru SQL yazmak yazmak kadar kolaydır Select *. Bu yüzden gerçek tembel kişi uygun SQL yazıyor çünkü kodu tekrar gözden geçirmek ve yaptıkları zaman ne yaptıklarını hatırlamak istemiyorlar. DBA'lara her kod parçasını açıklamak istemiyorlar. Müşterilerine uygulamanın neden bir köpek gibi çalıştığını açıklamak istemiyorlar.


2
İlk bölümünüzde # 5 numaralı noktanın "select * size birleştirme içindeki tüm tablolardaki tüm sütunları verir" yazması gerekir . İkinci bölümünüzde, # 2 ve # 5 noktaları mutlaka doğru değildir ve "select *" kullanmamanın nedenleri olarak listelenmemelidir.
jimmyorr

1
@uglysmurf - düzeltme için teşekkürler, ancak 2 & 5 ile ilgili olarak - her durumda tüm veritabanları / dba'lar için doğru olmayabilirken, çoğu durumda önemli ve geçerli olduklarını ve bunları bırakacaklarını hissediyorum. 'Select *' kullanarak bir dba'nın işi hiç bu kadar kolay olmamıştı.
Robert Paulson

11
# 3'ün (Gevrek kod) gerçekten doğru olmadığını iddia ediyorum. Uygulamaya bağlı olarak, Select * daha az kırılgan yapabilir, ancak nasıl daha fazla olabileceğini göremiyorum.
JohnFx

2
@ JohnFx, sanırım kırılganlığı farklı tanımladınız. Kırılganlık normalde 'kolayca kırılır' olarak tanımlanır. Her kod parçası farklı sütunlar kullanacağı için bilinmeyen veya bulunması zor bağımlılıklara sahip olmak, tam regresyon olmadan veri düzeyinde hiçbir şeyi kolayca değiştiremediğim anlamına gelir.
Robert Paulson

9
@mavnn, wrt gevrekliği, bunun kırılgan kelimeyi seçmemde anlambilimsel bir meseleye dönüşmesinden korkuyorum. Son sözüm, bunun çok az fark yarattığını söylemek. Tek senaryo sütunları yeniden adlandırıldı / kaldırıldı. Sadece sql yürütüldüğünde (açık) mola sonuçları tüketildiğinde kırılma hareket ediyor. Sorgu sonucunun tüketilme şekli değişebilir ve kod sessizce başarısız olabilir veya olmayabilir, ancak sql yürütme motoru kesinlikle geçersiz sql ile başarısız olur. Peki * seçmek size yardımcı oldu mu? DB sorunu için DB yakın IMO açık hatası daha iyidir. Teşekkürler
Robert Paulson

42

Kodunuz belirli bir sıradaki sütunlara bağlıysa, tabloda değişiklik olduğunda kodunuz bozulur. Ayrıca, * seçeneğini belirlediğinizde, özellikle de tabloda ikili bir alan varsa, tablodan çok fazla bilgi alıyor olabilirsiniz.

Şu anda tüm sütunları kullandığınız için, başkasının tabloya fazladan sütun eklemeyeceği anlamına gelmez.

Ayrıca, hangi sütunların içinde olduğunu bilmek için tablo hakkındaki meta verileri getirmesi gerektiğinden plan yürütme önbelleğine ek yük ekler *.


4
İyi cevap, ama ben "kod kırılacak" değiştirmek için "kod olabilir MAYIS". Buradaki asıl sorun budur, "select *" kullanımı DAİMA bir değişiklik yaratmaz. Ve mola meydana geldiğinde, genellikle kırılan kullanımdan oldukça ayrılır.
BQ.

4
Birisi kodlarında sıralı olarak sütunlara başvuruyorsa, SELECT * kullanıp kullanmadıklarına bakılmaksızın başı dertte olur. Plan yürütme yükü önemsizdir ve plan önbelleğe alındıktan sonra hiçbir şekilde önemli olmaz.
MusiGenesis

1
Sonra programcı hatası sütunların sırasına bağlı kod yazma yatıyor. Bunu asla yapmanıza gerek yok.
dkretz

1
@doofledorfer - asla asla deme. Sıralı sütunlara erişmek daha hızlıdır ve zaman zaman pratiktir. Select * kullanmak sıradan erişim kullanmaktan daha büyük bir hatadır.
Robert Paulson

23

Bunun önemli bir nedeni, tablonuza hiç sütun ekleyip kaldırmanız durumunda, SELECT * çağrısı yapan herhangi bir sorgu / yordamın artık beklenenden daha fazla veya daha az veri sütununu almasıdır.


3
Yine de döndürülen sütunların sayısına bağlı olan kodu yazmamalısınız.
dkretz

4
Ancak herkes, programcıların hangi verilerin geri geleceğini bilmesini gerektiren bir kod yazıyor. SELECT * içinde gizlenmişse sütun adınızı Ctrl + F tuşlarına basamazsınız.
Lotus Notes

17
  1. Göbekli bir şekilde, mümkün olan her yerde katı yazım kullanma hakkındaki modülerlik kuralını ihlal ediyorsunuz. Açık neredeyse evrensel olarak daha iyidir.

  2. Şimdi tablodaki her sütuna ihtiyacınız olsa bile, sorguyu her çalıştırdığınızda aşağı çekilecek ve performansa zarar verebilecek daha sonra daha fazlası eklenebilir. Performansı acıtıyor çünkü

    • Kablo üzerinden daha fazla veri çekiyorsunuz; ve
    • Çünkü optimize edicinin, tablonun kendisinde bir arama yapmak yerine verileri doğrudan dizinden dışarı çekebilmesini (dizinin bir parçası olan sütunlardaki sorgular için) yenebilirsiniz.

Ne zaman KULLANILIR *

Tablodaki her bir sütuna ihtiyaç duymak yerine, tablodaki her sütuna ihtiyaç duyduğunuzda, QUERY'I DOĞRUDAN ZAMAN VAR. Örneğin, tablonun tüm içeriğini (ne olursa olsun) görüntülemek için gereken bir DB yönetim uygulaması yazıyorsanız, bu yaklaşımı kullanabilirsiniz.


1
SELECT *Kullanmak için başka bir zaman db istemcisi kullanarak test sorguları yaparken olacaktır.
cdmckay

Sorunun bağlamı göz önüne alındığında garip bir istisna gibi görünüyor. Bazı yazımları kaydetmekten başka, bunu test sorguları için yapmanın avantajı nedir?
JohnFx

Ayrıca SELECT * FROM (SELECT a, b, c FROM tablosu) TAMAM.
kmkaplan

12

Bir kaç neden var:

  1. Veritabanındaki sütun sayısı değişirse ve uygulamanız belirli bir sayı olmasını beklerse ...
  2. Veritabanındaki sütunların sırası değişirse ve uygulamanız bunların belirli bir sırada olmasını beklerse ...
  3. Bellek yükü. 8 gereksiz INTEGER sütunu 32 bayt boşa bellek ekleyecektir. Bu çok fazla görünmüyor, ancak bu her sorgu için ve INTEGER küçük sütun türlerinden biridir ... ekstra sütunların daha hızlı ekleyen VARCHAR veya TEXT sütunları olma olasılığı daha yüksektir.
  4. Ağ yükü. Bellek yükü ile ilgili: 30.000 sorgu gönderir ve 8 gereksiz INTEGER sütunum varsa, 960kB bant genişliği harcadım. VARCHAR ve TEXT sütunlarının büyük olasılıkla daha büyük olması muhtemeldir.

Not: Yukarıdaki örnekte INTEGER'ı seçtim çünkü sabit boyutu 4 bayt.


1 ve 2 kod kokusu ve 3 ve 4 erken optimizasyon gibi geliyor
NikkyD

7

Uygulamanız SELECT * ile veri alırsa ve veritabanındaki tablo yapısı değiştirilirse (bir sütunun kaldırıldığını varsayalım), uygulamanız eksik alana başvurduğunuz her yerde başarısız olur. Bunun yerine sorgunuza tüm sütunları eklerseniz, başvurunuz (umarım) başlangıçta verileri aldığınız bir yerde kırılır ve düzeltmeyi kolaylaştırır.

Bununla birlikte, SELECT * 'in istendiği birkaç durum vardır. Bir tüm tablo başka bir veritabanına (örneğin SQL Server DB2 gibi) çoğaltmak gerekir her zaman karşılaştığım bir durumdur. Başka bir tablo genel olarak (yani herhangi bir tablo hakkında herhangi bir bilgi olmadan) görüntülemek için yazılmış bir uygulamadır.


Soru 'seçili * hiç arzu edilmiyor' değildir, bu nedenle cevabınızın 2. kısmı ilgisizdir. Soru, 'select *' kullanmanın tercihli olması gerektiğini belirtir, ki bu elbette tam kilitlemelerdir.
Robert Paulson

Evet, 2. bölümüm alakasız. OQ soruyu SELECT * tercih edilir olarak değiştirdi ve evet bu bir çeşit bollocksy.
MusiGenesis

Ah evet üzgünüm - soru cevabından sonra yön değiştirdi.
Robert Paulson

Sorun değil. Mozart bile bir editördü ( stackoverflow.com/questions/292682/… ). Orijinal yazım SELECT * kullanımının yamyamlığa yol açtığını öne sürdü. :)
MusiGenesis

3

select *SQL Server 2005'te görünümlerde kullandığımda aslında garip bir davranış fark ettim .

Aşağıdaki sorguyu çalıştırın ve ne demek istediğimi göreceksiniz.

IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[starTest]') AND type in (N'U'))
DROP TABLE [dbo].[starTest]
CREATE TABLE [dbo].[starTest](
    [id] [int] IDENTITY(1,1) NOT NULL,
    [A] [varchar](50) NULL,
    [B] [varchar](50) NULL,
    [C] [varchar](50) NULL
) ON [PRIMARY]

GO

insert into dbo.starTest
select 'a1','b1','c1'
union all select 'a2','b2','c2'
union all select 'a3','b3','c3'

go
IF  EXISTS (SELECT * FROM sys.views WHERE object_id = OBJECT_ID(N'[dbo].[vStartest]'))
DROP VIEW [dbo].[vStartest]
go
create view dbo.vStartest as
select * from dbo.starTest
go

go
IF  EXISTS (SELECT * FROM sys.views WHERE object_id = OBJECT_ID(N'[dbo].[vExplicittest]'))
DROP VIEW [dbo].[vExplicittest]
go
create view dbo.[vExplicittest] as
select a,b,c from dbo.starTest
go


select a,b,c from dbo.vStartest
select a,b,c from dbo.vExplicitTest

IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[starTest]') AND type in (N'U'))
DROP TABLE [dbo].[starTest]
CREATE TABLE [dbo].[starTest](
    [id] [int] IDENTITY(1,1) NOT NULL,
    [A] [varchar](50) NULL,
    [B] [varchar](50) NULL,
    [D] [varchar](50) NULL,
    [C] [varchar](50) NULL
) ON [PRIMARY]

GO

insert into dbo.starTest
select 'a1','b1','d1','c1'
union all select 'a2','b2','d2','c2'
union all select 'a3','b3','d3','c3'

select a,b,c from dbo.vStartest
select a,b,c from dbo.vExplicittest

Son 2 seçme ifadesinin sonuçlarını karşılaştırın. Göreceğiniz şeyin Select * referans sütunlarını isim yerine dizine göre seçtiğine inanıyorum .

Görünümü yeniden oluşturursanız, tekrar iyi çalışır.

DÜZENLE

Ayrı bir soru ekledim, * "seçin * tablodan" vs "seçin colA, colB, vb." SQL Server 2005 * ilginç davranış bu davranış daha ayrıntılı olarak bakmak için.


2

İki tabloyu birleştirebilir ve ikinci tablodan A sütununu kullanabilirsiniz. Daha sonra ilk tabloya (aynı ada ancak muhtemelen farklı bir anlama sahip) A sütununu eklerseniz, büyük olasılıkla değerleri ilk tablodan alırsınız, ikinci tabloyu daha önceki gibi almazsınız. Seçmek istediğiniz sütunları açıkça belirtirseniz bu gerçekleşmez.

Tabii ki sütunları belirtmek, bazen her seçme maddesine yeni sütunlar eklemeyi unutursanız bazen hatalara neden olur. Sorgu her yürütüldüğünde yeni sütuna gerek duyulmazsa, hatanın fark edilmesi biraz zaman alabilir.


2

Erken optimizasyonla ilgili nereye gittiğinizi anlıyorum, ancak bu gerçekten sadece bir noktaya gidiyor. Amaç gereksizlerden kaçınmaktır başlangıçta optimizasyondan . Masalarınız dizine eklenmemiş mi? Bir posta kodu saklamak için nvarchar (4000) kullanır mısınız?

Diğerlerinin de belirttiği gibi, sorguda kullanmak istediğiniz her sütunu belirtmek için başka pozitifler de vardır (sürdürülebilirlik gibi).


2

Sütunları belirtirken, kendinizi belirli bir sütun kümesine bağlarsınız ve kendinizi daha az esnek hale getirirsiniz, Feuerstein'ı nerede olursa olsun devreye sokarsınız. Sadece bir düşünce.


1
Feuerstein'ın kim olduğu hakkında hiçbir fikrim yok. Google'ı denedi ve bir psikolog, bir televizyon karakteri ve bir blogcu buldu, bu yüzden gelebileceğim en iyi şey bir şakaydı.
NotMe

PL / SQL üzerine O'Reilly kitaplarının yazarı. Sadece "feuerstein" yerine "feuerstein sql" kelimesini deneyin.
orbfish

2

SELECT * her zaman kötü değildir. Bence en azından. Bir tabloyu ve bazı hesaplanmış alanları döndüren dinamik sorgular için oldukça sık kullanıyorum.

Örneğin, "normal" bir tablodan, yani herhangi bir geometri alanı olmayan, ancak koordinat içeren alanlardan oluşan bir tabloyu coğrafi geometrileri hesaplamak istiyorum. Postgresql ve uzamsal uzantı postgis kullanıyorum. Ancak ilke diğer birçok durum için de geçerlidir.

Bir örnek:

  • x, y, z etiketli alanlarda koordinatların bulunduğu bir yer tablosu:

    CREATE TABLE yerleri (place_id tamsayı, x sayısal (10, 3), y sayısal (10, 3), z sayısal (10, 3), açıklama varchar);

  • birkaç örnek değerle besleyelim:

    Yerlere yerleştirin (yer_kimliği, x, y, z, açıklama) DEĞERLER
    (1, 2.295, 48.863, 64, 'Paris, Place de l \' Étoile '),
    (2, 2.945, 48.858, 40,' Paris, Tour Eiffel '),
    (3, 0.373, 43.958, 90,' Prezervatif, Cathédrale St-Pierre ');

  • Bazı CBS istemcilerini kullanarak bu tablonun içeriğini eşlemek istiyorum. Normal yol, tabloya bir geometri alanı eklemek ve geometriyi koordinatlara göre oluşturmaktır. Ancak dinamik bir sorgu almayı tercih ederim: bu şekilde, koordinatları değiştirdiğimde (düzeltmeler, daha fazla doğruluk, vb.), Eşlenen nesneler aslında dinamik olarak hareket eder. İşte SELECT * ile sorgu :

    Oluşturmasını veya GÖRÜNTÜSÜ places_points DEĞİŞTİRME
    SELECT *,
    GeomFromewkt ( 'SRID = 4326; POINT (' || x || '|| y ||' || z || ')')
    yerlerden;

    GeomFromewkt () işlevinin kullanımı için postgis'e bakın.

  • İşte sonuç:

    SELECT * FROM places_points;

place_id | x | y | z | açıklama | geomfromewkt                            
---------- + ------- + -------- + -------- + ------------- ----------------- + -------------------------------- ------------------------------------  
        1 | 2.295 | 48.863 | 64.000 | Paris, Place de l'Étoile | 01010000A0E61000005C8FC2F5285C02405839B4C8766E48400000000000005040  
        2 | 2.945 | 48.858 | 40.000 | Paris, Eyfel Turu | 01010000A0E61000008FC2F5285C8F0740E7FBA9F1D26D48400000000000004440
        3 | 0.373 | 43.958 | 90.000 | Prezervatif, Cathédrale St-Pierre | 01010000A0E6100000AC1C5A643BDFD73FB4C876BE9FFA45400000000000805640
(3 lign)

En sağdaki sütun artık noktaları doğru şekilde eşleştirmek için herhangi bir CBS programı tarafından kullanılabilir.

  • Gelecekte, bazı alanlar tabloya eklenirse: endişelenmeyin, aynı VIEW tanımını tekrar çalıştırmam gerekiyor.

Keşke * ile tanımı "olduğu gibi" muhafaza edilebilir diliyorum, ama hélas durum böyle değil: postgresql tarafından dahili olarak bu şekilde saklanır:

Places.place_id, places.x, places.y, places.z, places.description, geomfromewkt (((((('SRID = 4326; POINT (' :: metin || places.x) || '': : text) || places.y) || '' :: text) || places.z) || ')' :: text) Yerlerden AS geomfromewkt;


1

Her sütunu kullansanız da satır dizisini sayısal dizine göre ele alsanız bile, daha sonra başka bir satır eklerseniz sorun yaşarsınız.

Yani temelde bu bir sürdürülebilirlik meselesidir! * Seçicisini kullanmazsanız, sorgularınız için endişelenmenize gerek kalmaz.


1

Yalnızca ihtiyacınız olan sütunları seçmek bellekteki veri kümesini daha küçük tutar ve bunun için uygulamanızı daha hızlı tutar.

Ayrıca, birçok araç (örneğin saklı yordamlar) sorgu yürütme planlarını da önbelleğe alır. Daha sonra bir sütun ekler veya kaldırırsanız (özellikle bir görünümü seçerseniz kolay olur), araç genellikle beklediği sonuçları geri almadığında hata verir.


1

Kodunuzu daha belirsiz ve bakımı daha zor hale getirir; çünkü etki alanına fazladan kullanılmayan veriler ekliyorsunuz ve hangisini istediğinizi hangisini istemediğiniz net değil. (Ayrıca bilmediğiniz veya önemsemeyeceğinizi de gösterir.)


1

Sorunuzu doğrudan yanıtlamak için: Kodunuzu alttaki tablolarda yapılan değişiklikler için daha güzel hale getirdiğinde "SELECT *" ifadesini kullanmayın. Kodunuz yalnızca programınızın gereksinimlerini doğrudan etkileyen tabloda bir değişiklik yapıldığında kesilmelidir.

Başvurunuz, İlişkisel erişimin sağladığı soyutlama katmanından yararlanmalıdır.


1

SELECT * kullanmıyorum çünkü hangi alanları aldığımı görmek güzel.


1

Genelde görünümlerin içinde 'select *' kullanmak kötü çünkü tablo sütunu değişikliği durumunda görünümü yeniden derlemek zorunda kalacaksınız. Bir görünümün altında yatan tablo sütunlarını değiştirdiğinizde, geri dönüp yeniden derleyene kadar var olmayan sütunlar için bir hata alırsınız.


1

exists(select * ...)Asla genişletilmediği için yaptığınız zaman sorun değil . Aksi takdirde, yalnızca geçici seçim ifadelerine sahip tabloları keşfederken veya yukarıda tanımlanmış bir CTE'niz varsa ve her sütunu tekrar yazmadan istiyorsanız gerçekten yararlıdır.


1

Sadece kimsenin bahsetmediği bir şey eklemek için. Select *tüm sütunları döndürürse, birileri daha sonra kullanıcıların verileri en son güncelleyen veya bir zaman damgası veya yalnızca yöneticilerin tüm kullanıcıları görmemesi gereken notlar vb. gibi kullanıcıların görmesini istemediğiniz bir sütun ekleyebilir.

Ayrıca, bir sütun eklerken, mevcut kod üzerindeki etki gözden geçirilmeli ve sütunda hangi bilgilerin depolandığına bağlı olarak değişikliklerin gerekli olup olmadığını görmek için değerlendirilmelidir. Bunu kullanarak select *, geliştirici hiçbir şeyin kırılmayacağını varsayacağından, bu inceleme genellikle atlanır. Ve aslında hiçbir şey açıkça kırılmaz gibi görünebilir, ancak sorgular artık yanlış şeyi döndürmeye başlayabilir. Hiçbir şeyin açıkça kırılmaması, sorgularda değişiklik olmaması gerektiği anlamına gelmez.


0

çünkü "select *" tüm alanlara ihtiyacınız olmadığında bellek israfına neden olur. Ancak sql sunucusu için performansları aynıdır.

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.