Apa alasan untuk tidak menggunakan Select *?

136

Saya telah melihat sejumlah orang mengklaim bahwa Anda harus secara spesifik memberi nama setiap kolom yang Anda inginkan dalam kueri pemilihan Anda.

Dengan asumsi saya akan menggunakan semua kolom, mengapa saya tidak menggunakan SELECT *?

Bahkan mempertimbangkan pertanyaan * SQL query - Select * from view atau Select col1, col2,… colN from view *, saya rasa ini bukan duplikat yang tepat karena saya mendekati masalah dari perspektif yang sedikit berbeda.

Salah satu prinsip kami adalah tidak mengoptimalkan sebelum waktunya. Dengan pemikiran tersebut, sepertinya menggunakan SELECT *harus menjadi metode yang disukai sampai terbukti menjadi masalah sumber daya atau skema cukup banyak ditetapkan. Yang, seperti yang kita ketahui, tidak akan terjadi sampai pengembangan selesai sepenuhnya.

Karena itu, apakah ada masalah utama untuk tidak digunakan SELECT *?

Bukan saya
sumber

Jawaban:

169

Inti dari kutipan dari tidak mengoptimalkan sebelum waktunya adalah menggunakan kode yang sederhana dan lugas, lalu menggunakan profiler untuk menunjukkan hot spot, yang kemudian dapat Anda optimalkan agar efisien.

Ketika Anda menggunakan pilih * Anda membuatnya tidak mungkin untuk membuat profil, oleh karena itu Anda tidak menulis kode yang jelas & lugas dan Anda bertentangan dengan semangat kutipan. select *adalah anti-pola.


Jadi memilih kolom bukanlah pengoptimalan yang prematur. Beberapa hal di luar kepala saya ....

  1. Jika Anda menentukan kolom dalam pernyataan SQL, mesin eksekusi SQL akan error jika kolom tersebut dihapus dari tabel dan kueri dijalankan.
  2. Anda dapat dengan mudah memindai kode di mana kolom itu digunakan.
  3. Anda harus selalu menulis pertanyaan untuk mengembalikan informasi yang paling sedikit.
  4. Seperti yang disebutkan orang lain jika Anda menggunakan akses kolom ordinal, Anda tidak boleh menggunakan pilih *
  5. Jika pernyataan SQL Anda menggabungkan tabel, pilih * memberi Anda semua kolom dari semua tabel yang digabungkan

Konsekuensinya adalah bahwa menggunakan select *...

  1. Kolom yang digunakan oleh aplikasi tidak tembus cahaya
  2. DBA dan profiler kuerinya tidak dapat membantu kinerja aplikasi Anda yang buruk
  3. Kode menjadi lebih rapuh saat terjadi perubahan
  4. Database dan jaringan Anda bermasalah karena membawa kembali terlalu banyak data (I / O)
  5. Pengoptimalan mesin database minimal saat Anda mengembalikan semua data apa pun (logis).

Menulis SQL yang benar semudah menulis Select *. Jadi, orang yang benar-benar malas menulis SQL yang benar karena mereka tidak ingin mengunjungi kembali kode tersebut dan mencoba mengingat apa yang mereka lakukan saat melakukannya. Mereka tidak ingin menjelaskan kepada DBA tentang setiap bit kode. Mereka tidak ingin menjelaskan kepada klien mereka mengapa aplikasi berjalan seperti anjing.

Robert Paulson
sumber
2
Di bagian pertama Anda, poin # 5 harus membaca "pilih * memberi Anda semua kolom dari semua tabel di gabungan". Di bagian kedua, poin # 2 dan # 5 belum tentu benar, dan tidak boleh dicantumkan sebagai alasan untuk tidak menggunakan "pilih *".
jimmyorr
1
@uglysmurf - terima kasih atas koreksinya, tetapi sehubungan dengan 2 & 5 - meskipun mungkin tidak selalu benar untuk semua database / dba dalam semua kasus, saya merasa itu penting dan valid untuk sebagian besar kasus dan akan membiarkannya masuk. Menggunakan 'pilih *' tidak pernah membuat pekerjaan dba lebih mudah.
Robert Paulson
11
Saya akan berpendapat bahwa # 3 (Kode rapuh) tidak benar-benar benar. Bergantung pada implementasinya, Select * mungkin membuatnya KURANG rapuh, tetapi saya tidak melihat bagaimana bisa lebih dari itu.
JohnFx
2
@ JohnFx, saya rasa Anda mendefinisikan rapuh secara berbeda. Rapuh biasanya diartikan sebagai 'mudah pecah'. Memiliki ketergantungan yang tidak diketahui atau sulit ditemukan karena setiap bagian kode akan menggunakan kolom yang berbeda berarti saya tidak dapat dengan mudah mengubah apa pun di tingkat data tanpa regresi penuh .. yang tampaknya rapuh.
Robert Paulson
9
@mavnn, wrt kerapuhan, saya khawatir ini beralih ke masalah semantik pada pilihan kata rapuh saya. Kata terakhir saya adalah mengatakan itu membuat sedikit perbedaan. Skenario satu-satunya adalah mengganti nama / menghapus kolom. Anda baru saja memindahkan jeda dari saat sql dijalankan (eksplisit) versus pemutusan saat hasil dikonsumsi. Cara di mana hasil kueri dikonsumsi dapat bervariasi, dan kode mungkin atau mungkin tidak diam-diam gagal, tetapi mesin eksekusi sql pasti akan gagal dengan sql yang tidak valid. Jadi, apakah pilih * membantu Anda? Kegagalan eksplisit IMO lebih dekat ke DB untuk masalah DB lebih baik. Thx
Robert Paulson
42

Jika kode Anda bergantung pada kolom yang berada dalam urutan tertentu, kode Anda akan rusak ketika ada perubahan pada tabel. Selain itu, Anda mungkin mengambil terlalu banyak dari tabel saat memilih *, terutama jika ada bidang biner di tabel.

Hanya karena Anda menggunakan semua kolom sekarang, itu tidak berarti orang lain tidak akan menambahkan kolom tambahan ke tabel.

Ini juga menambahkan overhead ke cache eksekusi rencana karena harus mengambil meta data tentang tabel untuk mengetahui kolom apa yang ada di *.

Bob
sumber
4
Jawaban bagus, tapi saya akan mengubah "kode akan rusak" menjadi "kode MUNGKIN rusak". Itulah masalah sebenarnya di sini, penggunaan "pilih *" tidak SELALU menghasilkan perubahan yang merusak. Dan ketika kerusakan terjadi biasanya sangat dipisahkan dari penggunaan yang akhirnya rusak.
BQ.
4
Jika seseorang mereferensikan kolom secara ordinal dalam kode mereka, mereka dalam masalah terlepas dari apakah mereka menggunakan SELECT * atau tidak. Overhead eksekusi rencana itu sepele, dan tidak akan menjadi masalah setelah rencana di-cache.
MusiGenesis
1
Kemudian kesalahan programmer terletak pada penulisan kode yang bergantung pada urutan kolom. Anda tidak perlu melakukan itu.
dkretz
1
@ doofledorfer - jangan pernah bilang tidak. Lebih cepat untuk mengakses kolom ordinal, dan terkadang praktis. Ini adalah kesalahan yang lebih besar menggunakan select * daripada menggunakan akses ordinal.
Robert Paulson
23

Salah satu alasan utamanya adalah jika Anda pernah menambah / menghapus kolom dari tabel Anda, setiap kueri / prosedur yang membuat panggilan SELECT * sekarang akan mendapatkan lebih banyak atau lebih sedikit kolom data daripada yang diharapkan.

ahockley
sumber
3
Anda tidak boleh menulis kode yang bergantung pada jumlah kolom yang dikembalikan.
dkretz
4
Tetapi setiap orang menulis kode yang mengharuskan pemrogram mengetahui data mana yang akan kembali. Anda tidak bisa Ctrl + F nama kolom Anda jika tersembunyi di SELECT *.
Lotus Notes
17
  1. Secara tidak langsung, Anda melanggar aturan modularitas tentang penggunaan pengetikan yang ketat jika memungkinkan. Eksplisit hampir secara universal lebih baik.

  2. Bahkan jika Anda sekarang membutuhkan setiap kolom dalam tabel, lebih banyak lagi dapat ditambahkan nanti yang akan ditarik ke bawah setiap kali Anda menjalankan kueri dan dapat merusak kinerja. Itu merusak kinerja karena

    • Anda menarik lebih banyak data melalui kabel; dan
    • Karena Anda mungkin mengalahkan kemampuan pengoptimal untuk menarik data langsung dari indeks (untuk kueri pada kolom yang semuanya merupakan bagian dari indeks.) Daripada melakukan pencarian di tabel itu sendiri

Kapan harus menggunakan pilih *

Ketika Anda secara eksplisit MEMBUTUHKAN setiap kolom dalam tabel, bukan membutuhkan setiap kolom dalam tabel YANG ADA SAAT ANDA MENULIS KUERI. Misalnya, jika Anda menulis aplikasi manajemen DB yang perlu menampilkan seluruh konten tabel (apa pun yang terjadi), Anda mungkin menggunakan pendekatan itu.

JohnFx
sumber
1
Waktu lain untuk digunakan SELECT *adalah saat Anda melakukan kueri pengujian menggunakan klien db.
cdmckay
Itu tampak seperti pengecualian yang aneh mengingat konteks pertanyaannya. Selain menghemat beberapa pengetikan, apa keuntungan melakukan ini untuk kueri pengujian?
JohnFx
1
Juga SELECT * FROM (SELECT a, b, c FROM table) OK.
kmkaplan
12

Ada beberapa alasan:

  1. Jika jumlah kolom dalam database berubah dan aplikasi Anda mengharapkan ada sejumlah ...
  2. Jika urutan kolom dalam database berubah dan aplikasi Anda mengharapkannya dalam urutan tertentu ...
  3. Overhead memori. 8 kolom INTEGER yang tidak perlu akan menambah 32 byte memori yang terbuang. Kedengarannya tidak banyak, tetapi ini untuk setiap kueri dan INTEGER adalah salah satu jenis kolom kecil ... kolom tambahan lebih cenderung berupa kolom VARCHAR atau TEXT, yang bertambah lebih cepat.
  4. Overhead jaringan. Terkait dengan overhead memori: jika saya mengeluarkan 30.000 query dan memiliki 8 kolom INTEGER yang tidak perlu, saya telah membuang 960kB bandwidth. Kolom VARCHAR dan TEXT kemungkinan besar lebih besar.

Catatan: Saya memilih INTEGER pada contoh di atas karena mereka memiliki ukuran tetap 4 byte.

Powerlord
sumber
1 dan 2 akan menjadi bau kode dan 3 dan 4 terdengar seperti pengoptimalan prematur
NikkyD
7

Jika aplikasi Anda mendapatkan data dengan SELECT * dan struktur tabel dalam database diubah (misalnya kolom dihapus), aplikasi Anda akan gagal di setiap tempat yang Anda rujuk ke bidang yang hilang. Jika Anda malah menyertakan semua kolom dalam kueri, aplikasi Anda akan rusak di (semoga) satu tempat di mana Anda awalnya mendapatkan data, membuat perbaikan lebih mudah.

Meskipun demikian, ada sejumlah situasi di mana SELECT * diinginkan. Salah satunya adalah situasi yang saya hadapi sepanjang waktu, di mana saya perlu mereplikasi seluruh tabel ke database lain (seperti SQL Server ke DB2, misalnya). Lain adalah aplikasi yang ditulis untuk menampilkan tabel secara umum (yaitu tanpa pengetahuan tentang tabel tertentu).

MusiGenesis
sumber
Pertanyaannya bukanlah 'adalah pilih * pernah diinginkan', jadi bagian kedua dari jawaban Anda tidak relevan. Pertanyaan tersebut menyatakan bahwa menggunakan 'pilih *' harus lebih disukai, yang tentu saja omong kosong.
Robert Paulson
Ya, bagian kedua saya tidak relevan. OQ mengubah pertanyaan menjadi menyatakan SELECT * lebih disukai, dan ya itu semacam omong kosong.
MusiGenesis
Ah ya maaf - pertanyaan berubah arah setelah jawaban Anda.
Robert Paulson
Itu benar. Bahkan Mozart adalah editor ( stackoverflow.com/questions/292682/… ). Posting asli saya menyarankan bahwa penggunaan SELECT * menyebabkan kanibalisme. :)
MusiGenesis
3

Saya benar-benar melihat perilaku aneh ketika saya menggunakan select *tampilan di SQL Server 2005.

Jalankan kueri berikut dan Anda akan melihat apa yang saya maksud.

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

Bandingkan hasil dari 2 pernyataan pemilihan terakhir. Saya yakin apa yang akan Anda lihat adalah hasil dari Pilih * kolom referensi berdasarkan indeks, bukan nama.

Jika Anda membangun kembali tampilan itu akan berfungsi dengan baik lagi.

EDIT

Saya telah menambahkan pertanyaan terpisah, * "pilih * dari tabel" vs "pilih colA, colB, dll. Dari tabel" perilaku menarik di SQL Server 2005 * untuk melihat perilaku tersebut secara lebih rinci.

kristof
sumber
2

Anda bisa menggabungkan dua tabel dan menggunakan kolom A dari tabel kedua. Jika nanti Anda menambahkan kolom A ke tabel pertama (dengan nama yang sama tetapi mungkin artinya berbeda), kemungkinan besar Anda akan mendapatkan nilai dari tabel pertama dan bukan yang kedua seperti sebelumnya. Itu tidak akan terjadi jika Anda secara eksplisit menentukan kolom yang ingin Anda pilih.

Tentu saja menentukan kolom juga terkadang menyebabkan bug jika Anda lupa menambahkan kolom baru ke setiap klausa pilih. Jika kolom baru tidak diperlukan setiap kali kueri dijalankan, mungkin perlu beberapa saat sebelum bug diketahui.

Kaniu
sumber
2

Saya mengerti ke mana Anda akan pergi mengenai pengoptimalan prematur, tetapi itu benar-benar hanya sampai pada satu poin. Maksudnya adalah untuk menghindari yang tidak perlu pengoptimalan yang di awal. Apakah tabel Anda tidak terindeks? Apakah Anda akan menggunakan nvarchar (4000) untuk menyimpan kode pos?

Seperti yang ditunjukkan orang lain, ada hal positif lain untuk menentukan setiap kolom yang ingin Anda gunakan dalam kueri (seperti pemeliharaan).

Jim BG
sumber
2

Saat Anda menentukan kolom, Anda juga mengikat diri Anda ke dalam kumpulan kolom tertentu dan membuat diri Anda kurang fleksibel, membuat Feuerstein berguling, di mana pun dia berada. Hanya pemikiran saja.

orbfish
sumber
1
Saya sama sekali tidak tahu siapa Feuerstein. Mencoba googling dan menemukan psikolog, tokoh televisi dan blogger jadi yang terbaik yang bisa saya buat adalah lelucon.
NotMe
Penulis buku O'Reilly tentang PL / SQL. Coba googling "feuerstein sql" bukan hanya "feuerstein".
orbfish
2

PILIH * tidak selalu jahat. Setidaknya menurut pendapat saya. Saya cukup sering menggunakannya untuk kueri dinamis yang mengembalikan seluruh tabel, ditambah beberapa bidang yang dihitung.

Misalnya, saya ingin menghitung geometri geografis dari tabel "normal", yaitu tabel tanpa bidang geometri, tetapi dengan bidang yang berisi koordinat. Saya menggunakan postgresql, dan ekstensi spasialnya postgis. Tetapi prinsip tersebut berlaku untuk banyak kasus lain.

Sebuah contoh:

  • tabel tempat, dengan koordinat disimpan dalam bidang berlabel x, y, z:

    BUAT tempat TABEL (place_id integer, x numeric (10, 3), y numeric (10, 3), z numeric (10, 3), description varchar);

  • mari kita beri makan dengan beberapa nilai contoh:

    INSERT INTO places (place_id, x, y, z, description) VALUES
    (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,' Kondom, Cathédrale St-Pierre ');

  • Saya ingin dapat memetakan isi tabel ini, menggunakan beberapa klien GIS. Cara normal adalah dengan menambahkan bidang geometri ke tabel, dan membangun geometri, berdasarkan koordinat. Tetapi saya lebih suka mendapatkan kueri dinamis: dengan cara ini, ketika saya mengubah koordinat (koreksi, akurasi lebih, dll.), Objek yang dipetakan benar-benar bergerak, secara dinamis. Jadi inilah kueri dengan SELECT * :

    BUAT ATAU GANTI TAMPILAN tempat_point SEBAGAI
    PILIH *,
    GeomFromewkt ('SRID = 4326; POINT (' || x || '' || y || '' || z || ')')
    DARI tempat;

    Lihat postgis, untuk penggunaan fungsi GeomFromewkt ().

  • Inilah hasilnya:

    PILIH * DARI places_points;

place_id | x | y | z | deskripsi | geomfromewkt                            
---------- + ------- + -------- + -------- + ------------- ----------------- + -------------------------------- ------------------------------------  
        1 | 2.295 | 48.863 | 64.000 | Paris, Place de l'Étoile | 01010000A0E61000005C8FC2F5285C02405839B4C8766E48400000000000005040  
        2 | 2.945 | 48.858 | 40.000 | Paris, Menara Eiffel | 01010000A0E61000008FC2F5285C8F0740E7FBA9F1D26D48400000000000004440
        3 | 0.373 | 43.958 | 90.000 | Kondom, Cathédrale St-Pierre | 01010000A0E6100000AC1C5A643BDFD73FB4C876BE9FFA45400000000000805640
(3 ligne)

Kolom paling kanan sekarang dapat digunakan oleh program GIS untuk memetakan titik dengan benar.

  • Jika, di masa mendatang, beberapa bidang ditambahkan ke tabel: jangan khawatir, saya hanya perlu menjalankan lagi definisi VIEW yang sama.

Saya berharap definisi VIEW dapat disimpan "sebagaimana adanya", dengan *, tetapi ini bukan masalahnya: ini adalah cara penyimpanan internal oleh postgresql:

PILIH places.place_id, places.x, places.y, places.z, places.description, geomfromewkt ((((('SRID = 4326; POINT (' :: text || places.x) || '': : teks) || tempat.y) || '' :: teks) || tempat.z) || ')' :: teks) AS geomfromewkt DARI tempat;

Pierre
sumber
1

Bahkan jika Anda menggunakan setiap kolom tetapi mengatasi array baris dengan indeks numerik, Anda akan mengalami masalah jika Anda menambahkan baris lain nanti.

Jadi pada dasarnya ini adalah pertanyaan tentang pemeliharaan! Jika Anda tidak menggunakan * selektor Anda tidak perlu khawatir dengan pertanyaan Anda.

markus
sumber
1

Memilih hanya kolom yang Anda butuhkan membuat kumpulan data dalam memori lebih kecil dan karenanya membuat aplikasi Anda lebih cepat.

Juga, banyak alat (misalnya prosedur tersimpan) rencana eksekusi permintaan cache juga. Jika Anda nanti menambah atau menghapus kolom (terutama mudah jika Anda memilih dari tampilan), alat akan sering error saat tidak mendapatkan kembali hasil yang diharapkan.

Soldarnal
sumber
1

Itu membuat kode Anda lebih ambigu dan lebih sulit untuk dipertahankan; karena Anda menambahkan data ekstra yang tidak terpakai ke domain, dan tidak jelas mana yang Anda inginkan dan mana yang tidak. (Ini juga menunjukkan bahwa Anda mungkin tidak tahu, atau peduli.)

dkretz.dll
sumber
1

Untuk menjawab pertanyaan Anda secara langsung: Jangan gunakan "PILIH *" saat itu membuat kode Anda lebih mudah berubah ke tabel yang mendasarinya. Kode Anda harus rusak hanya jika ada perubahan pada tabel yang secara langsung mempengaruhi persyaratan program Anda.

Aplikasi Anda harus memanfaatkan lapisan abstraksi yang disediakan oleh akses Relasional.

Metro
sumber
1

Saya tidak menggunakan SELECT * hanya karena bagus untuk melihat dan mengetahui kolom apa yang saya ambil.

lkessler.dll
sumber
1

Umumnya buruk untuk menggunakan 'pilih *' di dalam tampilan karena Anda akan dipaksa untuk mengkompilasi ulang tampilan jika kolom tabel berubah. Mengubah kolom tabel yang mendasari tampilan Anda akan mendapatkan kesalahan untuk kolom yang tidak ada sampai Anda kembali dan mengkompilasi ulang.

Christopher Klein
sumber
1

Tidak apa-apa jika Anda melakukannya exists(select * ...)karena tidak pernah diperluas. Jika tidak, itu benar-benar hanya berguna saat menjelajahi tabel dengan pernyataan pilih sementara atau jika Anda memiliki CTE yang ditentukan di atas dan Anda ingin setiap kolom tanpa mengetik semuanya lagi.

dotjoe
sumber
1

Hanya untuk menambahkan satu hal yang tidak disebutkan orang lain. Select *mengembalikan semua kolom, seseorang mungkin menambahkan kolom nanti yang Anda tidak ingin pengguna dapat melihatnya seperti yang terakhir memperbarui data atau stempel waktu atau catatan bahwa hanya manajer yang boleh melihat tidak semua pengguna, dll.

Selanjutnya, ketika menambahkan kolom, dampak pada kode yang ada harus ditinjau dan dipertimbangkan untuk melihat apakah diperlukan perubahan berdasarkan informasi apa yang disimpan di kolom tersebut. Dengan menggunakan select *, tinjauan tersebut akan sering dilewati karena pengembang akan berasumsi bahwa tidak ada yang akan rusak. Dan sebenarnya tidak ada yang secara eksplisit tampak rusak tetapi kueri sekarang mungkin mulai mengembalikan hal yang salah. Hanya karena tidak ada yang secara eksplisit rusak, tidak berarti seharusnya tidak ada perubahan pada kueri.

HLGEM
sumber
0

karena "pilih *" akan membuang memori ketika Anda tidak membutuhkan semua kolom. Tetapi untuk sql server, kinerjanya sama.

FloatFish
sumber