Mengapa SELECT *
praktik buruk? Bukankah itu berarti lebih sedikit kode untuk diubah jika Anda menambahkan kolom baru yang Anda inginkan?
Saya mengerti itu SELECT COUNT(*)
adalah masalah kinerja pada beberapa DB, tetapi bagaimana jika Anda benar-benar menginginkan setiap kolom?
SELECT COUNT(*)
menjadi buruk itu sangat tua dan ketinggalan jaman . Untuk info tentangSELECT *
- lihat: stackoverflow.com/questions/1960036/…SELECT COUNT(*)
memberikan jawaban yang berbeda dariSELECT COUNT(SomeColumn)
kecuali kolomnya adalah kolom NOT NULL. Dan optimizer dapat memberikanSELECT COUNT(*)
perlakuan khusus - dan biasanya demikian. Perhatikan juga bahwaWHERE EXISTS(SELECT * FROM SomeTable WHERE ...)
diberikan perawatan kasus khusus.Jawaban:
Sebenarnya ada tiga alasan utama:
Ketidakefisienan dalam memindahkan data ke konsumen. Ketika Anda SELECT *, Anda sering mengambil lebih banyak kolom dari database daripada aplikasi Anda benar-benar perlu berfungsi. Ini menyebabkan lebih banyak data berpindah dari server basis data ke klien, memperlambat akses dan menambah beban pada mesin Anda, serta mengambil lebih banyak waktu untuk melakukan perjalanan melintasi jaringan. Ini terutama benar ketika seseorang menambahkan kolom baru ke tabel yang mendasari yang tidak ada dan tidak diperlukan ketika konsumen asli mengkodekan akses data mereka.
Masalah pengindeksan. Pertimbangkan skenario di mana Anda ingin menyetel kueri ke tingkat kinerja yang tinggi. Jika Anda menggunakan *, dan itu menghasilkan lebih banyak kolom daripada yang sebenarnya Anda butuhkan, server sering harus melakukan metode yang lebih mahal untuk mengambil data Anda daripada yang seharusnya. Misalnya, Anda tidak akan dapat membuat indeks yang hanya menutupi kolom dalam daftar SELECT Anda, dan bahkan jika Anda melakukannya (termasuk semua kolom [ gemetar ]), orang berikutnya yang datang dan menambahkan kolom ke dasar tabel akan menyebabkan pengoptimal untuk mengabaikan indeks cakupan yang dioptimalkan, dan Anda mungkin akan menemukan bahwa kinerja permintaan Anda akan turun secara substansial tanpa alasan yang jelas.
Mengikat Masalah. Saat Anda PILIH *, dimungkinkan untuk mengambil dua kolom dengan nama yang sama dari dua tabel yang berbeda. Ini sering dapat membuat crash konsumen data Anda. Bayangkan sebuah kueri yang menggabungkan dua tabel, yang keduanya berisi kolom yang disebut "ID". Bagaimana konsumen tahu mana yang mana? SELECT * juga dapat membingungkan pandangan (setidaknya dalam beberapa versi SQL Server) ketika struktur tabel yang mendasarinya berubah - tampilan tidak dibangun kembali, dan data yang kembali dapat menjadi omong kosong . Dan bagian terburuknya adalah Anda dapat dengan hati-hati memberi nama kolom apa pun yang Anda inginkan, tetapi orang berikutnya yang datang mungkin tidak memiliki cara untuk mengetahui bahwa ia harus khawatir tentang menambahkan kolom yang akan bertabrakan dengan kolom yang sudah Anda kembangkan. nama.
Tapi tidak semuanya buruk untuk SELECT *. Saya menggunakannya secara bebas untuk kasus penggunaan ini:
Kueri ad-hoc. Ketika mencoba men-debug sesuatu, terutama dari tabel sempit yang mungkin tidak saya kenal, SELECT * sering kali adalah sahabat saya. Ini membantu saya melihat apa yang terjadi tanpa harus melakukan banyak penelitian tentang apa nama kolom yang mendasarinya. Ini akan menjadi lebih besar "plus" semakin lama nama kolom didapat.
Ketika * berarti "satu baris". Dalam kasus penggunaan berikut, SELECT * baik-baik saja, dan desas-desus bahwa itu adalah pembunuh kinerja hanyalah legenda perkotaan yang mungkin telah memiliki validitas beberapa tahun yang lalu, tetapi jangan sekarang:
dalam hal ini, * berarti "menghitung baris". Jika Anda menggunakan nama kolom alih-alih *, itu akan menghitung baris di mana nilai kolom itu tidak nol . COUNT (*), bagi saya, benar-benar membawa pulang konsep bahwa Anda menghitung baris , dan Anda menghindari kasus tepi aneh yang disebabkan oleh NULL yang dihilangkan dari agregat Anda.
Sama halnya dengan jenis kueri ini:
dalam basis data apa pun yang berharga, * hanya berarti "satu baris". Tidak masalah apa yang Anda masukkan ke dalam subquery. Beberapa orang menggunakan ID b dalam daftar SELECT, atau mereka akan menggunakan nomor 1, tetapi IMO konvensi itu cukup banyak tidak masuk akal. Yang Anda maksud adalah "hitung baris", dan itulah yang * menandakan. Kebanyakan pengoptimal permintaan di luar sana cukup pintar untuk mengetahui hal ini. (Meskipun jujur, saya hanya tahu ini benar dengan SQL Server dan Oracle.)
sumber
*
adalah bahwa dalam beberapa situasi dapat mengambil keuntungan lebih baik dari sistem cache MySQL. Jika Anda menjalankan sejumlah besarselect
kueri serupa yang meminta nama kolom yang berbeda (select A where X
,,select B where X
...) menggunakan aselect * where X
akan memungkinkan cache untuk menangani sejumlah besar kueri yang dapat menghasilkan peningkatan kinerja yang substansial. Ini adalah skenario khusus aplikasi, tetapi perlu diingat.SELECT *
memaksa pengembang untuk melihat skema tabel yang terlibat, untuk menentukan kolom yang terpengaruh / tersedia, seperti di dalamforeach
atauserialize
. Tugas berulang kali melihat skema untuk melacak apa yang terjadi, pasti akan meningkatkan waktu total yang terlibat baik dalam debugging dan mengembangkan kode terkait.Karakter tanda bintang, "*", dalam pernyataan SELECT adalah singkatan untuk semua kolom dalam tabel yang terlibat dalam kueri.
Performa
The
*
singkat dapat lebih lambat karena:SELECT *
melalui kawat berisiko pemindaian tabel penuhPemeliharaan
Saat menggunakan
SELECT *
:SELECT *
akan menyembunyikan kesalahan yang menunggu terjadi jika tabel memiliki urutan kolomnya berubah.Rancangan
SELECT *
adalah anti-pola :Kapan Seharusnya "SELECT *" Digunakan?
Dapat diterima untuk digunakan
SELECT *
ketika ada kebutuhan eksplisit untuk setiap kolom dalam tabel yang terlibat, berbeda dengan setiap kolom yang ada saat kueri ditulis. Basis data secara internal akan memperluas * ke daftar kolom lengkap - tidak ada perbedaan kinerja.Jika tidak, tuliskan secara eksplisit setiap kolom yang akan digunakan dalam kueri - lebih disukai saat menggunakan alias tabel.
sumber
Bahkan jika Anda ingin memilih setiap kolom sekarang, Anda mungkin tidak ingin memilih setiap kolom setelah seseorang menambahkan satu atau lebih kolom baru. Jika Anda menulis kueri dengan
SELECT *
Anda mengambil risiko bahwa pada titik tertentu seseorang mungkin menambahkan kolom teks yang membuat kueri Anda berjalan lebih lambat meskipun Anda tidak benar-benar membutuhkan kolom itu.Kemungkinannya adalah jika Anda benar-benar ingin menggunakan kolom baru maka Anda harus membuat cukup banyak perubahan lain pada kode Anda. Anda hanya menyimpan
, new_column
- hanya beberapa karakter mengetik.sumber
*
dapat tiba-tiba berubah dan ini dapat mendatangkan malapetaka dalam aplikasi itu sendiri: kolom direferensikan oleh ordinal (mis. Sqldatareader.getstring (2)) tiba-tiba mengambil a berbeda kolom, setiapINSERT ... SELECT *
akan istirahat dan sebagainya dan sebagainya.SELECT *
bukan masalah menghemat beberapa karakter. Ini masalah menghemat jam waktu debug karena mudah lupa menentukan kolom baru yang ditambahkan.Jika Anda memberi nama kolom dalam pernyataan SELECT, mereka akan dikembalikan dalam urutan yang ditentukan, dan dengan demikian dapat dengan aman dirujuk oleh indeks numerik. Jika Anda menggunakan "SELECT *", Anda mungkin akhirnya menerima kolom dalam urutan acak, dan dengan demikian hanya dapat menggunakan kolom dengan aman dengan nama. Kecuali Anda tahu sebelumnya apa yang ingin Anda lakukan dengan kolom baru yang ditambahkan ke database, tindakan yang paling tepat adalah mengabaikannya. Jika Anda akan mengabaikan kolom baru yang ditambahkan ke dalam basis data, tidak ada untungnya untuk mengambilnya.
sumber
select *
dan kemudian menggunakan kolom dengan indeks akan mengerikan, tetapi menggunakanselect X, Y, Z
atauselect A,B,C
dan kemudian meneruskan pembaca data yang dihasilkan ke kode yang mengharapkan untuk melakukan sesuatu dengan data dalam kolom 0, 1, dan 2 akan tampak cara yang masuk akal untuk memungkinkan kode yang sama untuk bertindak atas X, Y, Z atau A, B, C. Perhatikan bahwa indeks kolom akan tergantung pada lokasi mereka dalam pernyataan SELECT, bukan urutannya dalam database.Dalam banyak situasi, SELECT * akan menyebabkan kesalahan pada saat dijalankan dalam aplikasi Anda, bukan pada waktu desain. Itu menyembunyikan pengetahuan tentang perubahan kolom, atau referensi buruk di aplikasi Anda.
sumber
Jika Anda benar-benar menginginkan setiap kolom, saya belum melihat perbedaan kinerja antara select (*) dan penamaan kolom. Pengemudi untuk memberi nama kolom mungkin hanya untuk menjadi eksplisit tentang kolom apa yang Anda harapkan dalam kode Anda.
Namun, seringkali Anda tidak ingin setiap kolom dan pilih (*) dapat mengakibatkan pekerjaan yang tidak perlu untuk server database dan informasi yang tidak perlu harus diteruskan melalui jaringan. Ini tidak mungkin menyebabkan masalah yang nyata kecuali sistemnya banyak digunakan atau konektivitas jaringan lambat.
sumber
Anggap saja sebagai mengurangi sambungan antara aplikasi dan database.
Untuk merangkum aspek 'bau kode':
SELECT *
menciptakan ketergantungan dinamis antara aplikasi dan skema. Membatasi penggunaannya adalah salah satu cara untuk membuat ketergantungan lebih terdefinisi, jika tidak, perubahan ke database memiliki kemungkinan lebih besar untuk membuat aplikasi Anda mogok.sumber
Jika Anda menambahkan bidang ke tabel, mereka akan secara otomatis disertakan dalam semua kueri tempat Anda menggunakan
select *
. Ini mungkin terlihat nyaman, tetapi itu akan membuat aplikasi Anda lebih lambat karena Anda mengambil lebih banyak data daripada yang Anda butuhkan, dan itu sebenarnya akan merusak aplikasi Anda di beberapa titik.Ada batas untuk berapa banyak data yang dapat Anda ambil di setiap baris hasil. Jika Anda menambahkan bidang ke tabel Anda sehingga hasil akhirnya melebihi batas itu, Anda mendapatkan pesan kesalahan saat Anda mencoba menjalankan kueri.
Ini adalah jenis kesalahan yang sulit ditemukan. Anda membuat perubahan di satu tempat, dan itu meledak di tempat lain yang sebenarnya tidak menggunakan data baru sama sekali. Bahkan mungkin ini adalah kueri yang lebih jarang digunakan sehingga dibutuhkan beberapa saat sebelum seseorang menggunakannya, yang membuatnya lebih sulit untuk menghubungkan kesalahan dengan perubahan.
Jika Anda menentukan bidang mana yang Anda inginkan dalam hasil, Anda aman dari jenis overhead overflow ini.
sumber
Referensi diambil dari artikel ini.
Jangan pernah menggunakan "SELECT *",
Saya hanya menemukan satu alasan untuk menggunakan "SELECT *"
Jika Anda memiliki persyaratan khusus dan menciptakan lingkungan yang dinamis ketika menambah atau menghapus kolom secara otomatis menangani kode aplikasi. Dalam kasus khusus ini Anda tidak perlu mengubah kode aplikasi dan database dan ini akan secara otomatis mempengaruhi lingkungan produksi. Dalam hal ini Anda dapat menggunakan "SELECT *".
sumber
Umumnya Anda harus sesuai dengan hasil
SELECT * ...
ke dalam struktur data dari berbagai jenis. Tanpa menentukan urutan hasil yang diterima, mungkin sulit untuk mengatur semuanya dengan benar (dan lebih banyak bidang yang tidak jelas akan lebih mudah untuk dilewatkan).Dengan cara ini Anda dapat menambahkan bidang ke tabel Anda (bahkan di tengahnya) karena berbagai alasan tanpa melanggar kode akses sql di seluruh aplikasi.
sumber
Menggunakan
SELECT *
saat Anda hanya membutuhkan beberapa kolom berarti lebih banyak data yang ditransfer daripada yang Anda butuhkan. Ini menambahkan pemrosesan pada database, dan meningkatkan latensi dalam mendapatkan data ke klien. Tambahkan ke ini bahwa itu akan menggunakan lebih banyak memori ketika dimuat, dalam beberapa kasus secara signifikan lebih banyak, seperti file BLOB besar, sebagian besar tentang efisiensi.Selain itu, lebih mudah untuk melihat ketika melihat kueri kolom apa yang sedang dimuat, tanpa harus mencari apa yang ada di tabel.
Ya, jika Anda menambahkan kolom tambahan, itu akan lebih cepat, tetapi dalam kebanyakan kasus, Anda ingin / perlu mengubah kode Anda menggunakan kueri untuk menerima kolom baru, dan ada potensi untuk mendapatkan kolom yang tidak Anda miliki ' t ingin / harapkan dapat menyebabkan masalah. Misalnya, jika Anda mengambil semua kolom, lalu mengandalkan urutan dalam satu lingkaran untuk menetapkan variabel, lalu menambahkan satu, atau jika pesanan kolom berubah (terlihat itu terjadi ketika memulihkan dari cadangan) itu dapat membuang semuanya.
Ini juga merupakan alasan yang sama mengapa jika Anda melakukan
INSERT
Anda harus selalu menentukan kolom.sumber
Saya tidak berpikir bahwa mungkin ada aturan selimut untuk ini. Dalam banyak kasus, saya menghindari SELECT *, tetapi saya juga bekerja dengan kerangka kerja data di mana SELECT * sangat bermanfaat.
Seperti halnya semua hal, ada manfaat dan biaya. Saya pikir bagian dari persamaan manfaat vs biaya adalah seberapa besar kendali yang Anda miliki atas struktur data. Dalam kasus di mana SELECT * bekerja dengan baik, struktur data dikontrol dengan ketat (itu adalah perangkat lunak ritel), jadi tidak ada banyak risiko bahwa seseorang akan menyelipkan bidang Gumpalan besar ke dalam tabel.
sumber
Memilih dengan nama kolom meningkatkan kemungkinan bahwa mesin basis data dapat mengakses data dari indeks daripada menanyakan data tabel.
SELECT * memaparkan sistem Anda pada perubahan kinerja dan fungsi yang tidak terduga jika skema database Anda berubah karena Anda akan mendapatkan kolom baru yang ditambahkan ke tabel, meskipun, kode Anda tidak siap untuk menggunakan atau menyajikan data baru itu.
sumber
Ada juga alasan yang lebih pragmatis: uang. Ketika Anda menggunakan basis data cloud dan Anda harus membayar untuk data yang diproses, tidak ada penjelasan untuk membaca data yang akan segera Anda buang.
Misalnya: BigQuery :
dan Kontrol proyeksi - Hindari SELECT * :
sumber
Pahami persyaratan Anda sebelum merancang skema (jika mungkin).
Pelajari tentang data, 1) pengindeksan 2) jenis penyimpanan yang digunakan, 3) mesin atau fitur vendor; yaitu ... caching, kemampuan dalam memori 4) tipe data 5) ukuran tabel 6) frekuensi kueri 7) beban kerja terkait jika sumber daya dibagi 8) Uji
A) Persyaratan akan bervariasi. Jika perangkat keras tidak dapat mendukung beban kerja yang diharapkan, Anda harus mengevaluasi kembali cara menyediakan persyaratan dalam beban kerja. Mengenai kolom tambahan ke tabel. Jika database mendukung tampilan, Anda dapat membuat tampilan indeks (?) Yang diindeks dari data spesifik dengan kolom bernama tertentu (vs. pilih '*'). Tinjau data dan skema Anda secara berkala untuk memastikan Anda tidak pernah mengalami sindrom "Sampah" -> "Sampah".
Dengan asumsi tidak ada solusi lain; Anda dapat mempertimbangkan yang berikut ini. Selalu ada beberapa solusi untuk suatu masalah.
1) Pengindeksan: Select * akan menjalankan tablescan. Bergantung pada berbagai faktor, ini mungkin melibatkan pencarian disk dan / atau pertikaian dengan pertanyaan lain. Jika tabelnya multi-guna, pastikan semua kueri berkinerja dan jalankan di bawah target waktu Anda. Jika ada sejumlah besar data, dan jaringan Anda atau sumber daya lainnya tidak dicari; Anda perlu mempertimbangkan ini. Basis data adalah lingkungan bersama.
2) jenis penyimpanan. Yaitu: jika Anda menggunakan SSD, disk, atau memori. Waktu I / O dan beban pada sistem / cpu akan bervariasi.
3) Dapatkah DBA menyesuaikan database / tabel untuk kinerja yang lebih tinggi? Dengan asumsi untuk alasan apa pun, tim telah memutuskan pilih '*' adalah solusi terbaik untuk masalah tersebut; dapatkah DB atau tabel dimuat ke dalam memori. (Atau metode lain ... mungkin respons dirancang untuk merespons dengan jeda 2-3 detik? --- saat iklan diputar untuk mendapatkan pendapatan perusahaan ...)
4) Mulai dari garis dasar. Pahami tipe data Anda, dan bagaimana hasilnya akan disajikan. Tipe data yang lebih kecil, jumlah bidang mengurangi jumlah data yang dikembalikan dalam set hasil. Ini membuat sumber daya tersedia untuk kebutuhan sistem lain. Sumber daya sistem biasanya memiliki batas; 'selalu' bekerja di bawah batas ini untuk memastikan stabilitas, dan perilaku yang dapat diprediksi.
5) ukuran tabel / data. pilih '*' adalah umum dengan tabel kecil. Biasanya sesuai dengan memori, dan waktu responsnya cepat. Sekali lagi .... tinjau kebutuhan Anda. Paket untuk fitur creep; selalu rencanakan untuk kebutuhan saat ini dan kemungkinan masa depan.
6) Frekuensi kueri / kueri. Waspadai beban kerja lain pada sistem. Jika kueri ini menyala setiap detik, dan tabelnya kecil. Set hasil dapat dirancang untuk tetap dalam cache / memori. Namun, jika kueri adalah proses batch yang sering dengan data Gigabytes / Terabyte ... Anda mungkin lebih baik mendedikasikan sumber daya tambahan untuk memastikan beban kerja lainnya tidak terpengaruh.
7) beban kerja terkait. Memahami bagaimana sumber daya digunakan. Apakah jaringan / sistem / database / tabel / aplikasi dikhususkan, atau dibagikan? Siapa saja para pemangku kepentingan? Apakah ini untuk produksi, pengembangan, atau QA? Apakah ini "perbaikan cepat" sementara. Sudahkah Anda menguji skenario? Anda akan terkejut betapa banyak masalah yang bisa ada pada perangkat keras saat ini. (Ya, kinerjanya cepat ... tetapi desain / kinerjanya masih menurun.) Apakah sistem perlu menjalankan 10K kueri per detik vs 5-10 kueri per detik. Apakah server basis data didedikasikan, atau melakukan aplikasi lain, pemantauan dijalankan pada sumber daya bersama. Beberapa aplikasi / bahasa; O / S akan mengkonsumsi 100% dari memori menyebabkan berbagai gejala / masalah.
8) Uji: Uji teori Anda, dan pahami sebanyak mungkin tentang Anda. Masalah '*' pilihan Anda mungkin merupakan masalah besar, atau mungkin sesuatu yang bahkan tidak perlu Anda khawatirkan.
sumber