Apakah pilih * masih no-no besar di SQL Server 2012?

41

Kembali pada masa lampau, itu dianggap besar tidak boleh tidak dilakukan select * from tableatau select count(*) from tablekarena kinerja hit.

Apakah ini masih terjadi di versi SQL Server yang lebih baru (Saya menggunakan 2012, tapi saya kira pertanyaannya akan berlaku untuk 2008 - 2014)?

Sunting: Karena orang tampaknya sedikit membohongi saya di sini, saya melihat ini dari sudut pandang / akademis, bukan apakah itu hal yang "tepat" untuk dilakukan (yang tentu saja tidak)

Piers Karsenbarg
sumber

Jawaban:

50

Jika Anda SELECT COUNT(*) FROM TABLEyang hanya mengembalikan satu baris (hitungan), relatif ringan, dan merupakan cara untuk mendapatkan datum itu.

Dan SELECT *bukan fisik tidak-tidak, dalam hal itu legal dan diizinkan.

Namun, masalahnya SELECT *adalah Anda dapat menyebabkan lebih banyak perpindahan data. Anda beroperasi pada setiap kolom dalam tabel. Jika Anda SELECThanya menyertakan beberapa kolom, Anda mungkin bisa mendapatkan jawaban dari indeks atau indeks, yang mengurangi I / O dan juga dampak pada cache server.

Jadi, Ya direkomendasikan sebagai praktik umum karena Yaitu pemborosan sumber daya Anda.

Satu-satunya manfaat nyata dari SELECT *tidak mengetik semua nama kolom. Tapi dari SSMS Anda bisa menggunakan seret dan lepas untuk mendapatkan nama kolom dalam kueri Anda dan menghapus yang tidak Anda butuhkan.

Sebuah analogi: Jika seseorang menggunakan SELECT *ketika mereka tidak membutuhkan setiap kolom, apakah mereka juga akan menggunakan SELECTtanpa WHERE(atau klausa pembatas lainnya) ketika mereka tidak membutuhkan setiap baris?

RLF
sumber
24

Selain jawaban yang sudah penyedia, saya merasa perlu menunjukkan bahwa pengembang sering terlalu malas ketika bekerja dengan ORM modern seperti Entity Framework. Sementara DBA berusaha sekuat tenaga untuk menghindarinya SELECT *, pengembang sering menulis contoh yang semantik setara, di c # Linq:

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

Intinya, ini akan menghasilkan sebagai berikut:

SELECT * FROM MyTable WHERE FirstName = 'User'

Ada juga overhead tambahan yang belum tercakup. Itu adalah sumber daya yang diperlukan untuk memproses setiap kolom di setiap baris ke objek yang relevan. Selanjutnya, untuk setiap objek yang disimpan dalam memori, objek itu harus dibersihkan. Jika Anda hanya memilih kolom yang Anda butuhkan, Anda dapat dengan mudah menyimpan lebih dari 100mb ram. Meskipun bukan jumlah besar pada dirinya sendiri, itu efek kumulatif pengumpulan sampah dll itu adalah sisi klien biaya.

Jadi ya, setidaknya untuk saya, itu dan akan selalu menjadi tidak besar. Kita juga perlu mendidik tentang biaya "tersembunyi" untuk melakukan ini lebih banyak.

Tambahan

Berikut adalah contoh menarik hanya data yang Anda butuhkan seperti yang diminta dalam komentar:

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

Kinerja: Permintaan dengan SELECT * mungkin tidak akan pernah menjadi permintaan penutup ( Penjelasan bicara sederhana , penjelasan Stack Overflow ).

Pemeriksaan masa depan: Permintaan Anda mungkin mengembalikan ketujuh kolom hari ini tetapi jika seseorang menambahkan lima kolom pada tahun berikutnya maka dalam setahun permintaan Anda mengembalikan dua belas kolom, membuang-buang IO dan CPU.

Pengindeksan: Jika Anda ingin tampilan dan fungsi bernilai tabel berpartisipasi dalam pengindeksan di SQL Server, maka tampilan dan fungsi tersebut harus dibuat dengan schemabinding, yang melarang penggunaan SELECT *.

Praktik terbaik : jangan pernah gunakan SELECT *dalam kode produksi.

Untuk subqueries, saya lebih suka WHERE EXISTS ( SELECT 1 FROM … ).

Sunting : Untuk mengatasi komentar Craig Young di bawah ini, menggunakan "SELECT 1" dalam subquery bukan merupakan "optimasi" - itu agar saya bisa berdiri di depan kelas saya dan mengatakan "jangan gunakan SELECT *, tanpa pengecualian! "

Tentang satu-satunya pengecualian yang bisa saya pikirkan adalah di mana klien melakukan semacam operasi tabel-pivot dan memang membutuhkan semua kolom sekarang dan masa depan.

Saya mungkin menerima pengecualian yang melibatkan CTE dan tabel turunan, meskipun saya ingin melihat rencana eksekusi.

Perhatikan bahwa saya menganggap COUNT(*)pengecualian untuk ini karena ini merupakan penggunaan sintaksis berbeda dari "*".

Greenstone Walker
sumber
10

Dalam SQL Server 2012, (atau versi apa pun dari 2005 ke atas), menggunakan SELECT *...hanya masalah kinerja yang mungkin dalam pernyataan SELECT tingkat atas dari kueri.

Jadi BUKAN masalah di Views (*), di subqueries, di klausa ADA, di CTE, atau di SELECT COUNT(*)..dll, dll. Perhatikan, bahwa ini mungkin juga berlaku untuk Oracle, dan DB2, dan mungkin PostGres (tidak yakin) , tetapi sangat mungkin bahwa itu masih menjadi masalah dalam banyak kasus untuk MySql.

Untuk memahami mengapa (dan mengapa itu masih bisa menjadi masalah dalam SELECT tingkat atas), akan sangat membantu untuk memahami mengapa itu pernah menjadi masalah, yang karena menggunakan SELECT *..cara " mengembalikan SEMUA kolom ". Secara umum ini akan mengembalikan lebih banyak data daripada yang Anda inginkan, yang jelas dapat menghasilkan lebih banyak IO, baik disk maupun jaringan.

Apa yang kurang jelas adalah bahwa ini juga membatasi indeks apa dan rencana kueri yang dapat digunakan oleh pengoptimal SQL, karena ia tahu bahwa pada akhirnya harus mengembalikan semua kolom data. Jika sebelumnya dapat mengetahui bahwa Anda hanya menginginkan kolom tertentu, maka seringkali dapat menggunakan rencana kueri yang lebih efisien dengan memanfaatkan indeks yang hanya memiliki kolom tersebut. Untungnya ada cara untuk mengetahui hal ini sebelumnya, yaitu bagi Anda untuk secara eksplisit menentukan kolom yang Anda inginkan dalam daftar kolom. Tetapi ketika Anda menggunakan "*", Anda menolak ini demi "berikan saja segalanya kepada saya, saya akan mencari tahu apa yang saya butuhkan."

Ya, ada juga CPU tambahan dan penggunaan memori untuk memproses setiap kolom, tetapi hampir selalu kecil dibandingkan dengan dua hal ini: disk tambahan yang signifikan dan bandwidth jaringan yang diperlukan untuk kolom yang tidak Anda butuhkan, dan harus menggunakan lebih sedikit rencana kueri yang dioptimalkan karena harus menyertakan setiap kolom.

Jadi apa yang berubah? Pada dasarnya, Pengoptimal SQL berhasil memasukkan fitur yang disebut "Optimasi Kolom" yang hanya berarti, bahwa mereka sekarang dapat mencari tahu di sub-kueri tingkat lebih rendah jika Anda akan benar-benar menggunakan kolom di tingkat atas permintaan.

Hasilnya adalah tidak masalah lagi jika Anda menggunakan 'SELECT * ..' di tingkat bawah / dalam dari sebuah kueri. Alih-alih, yang sebenarnya penting adalah apa yang ada dalam daftar kolom SELECT tingkat atas. Kecuali Anda menggunakan SELECT *..di bagian atas, maka sekali lagi, harus mengasumsikan bahwa Anda ingin SEMUA kolom, dan karenanya tidak dapat menggunakan optimasi kolom secara efektif.

(* - perhatikan bahwa ada masalah kecil yang mengikat di Tampilan dengan di *mana mereka tidak selalu mendaftarkan perubahan dalam daftar kolom ketika "*" digunakan. Ada cara lain untuk mengatasinya dan itu tidak mempengaruhi kinerja.)

RBarryYoung
sumber
5

Ada satu lagi alasan kecil untuk tidak digunakan SELECT *: jika urutan kolom dikembalikan berubah, aplikasi Anda akan rusak ... jika Anda beruntung. Jika tidak, Anda akan memiliki bug halus yang tidak terdeteksi untuk waktu yang lama. Urutan bidang dalam tabel adalah detail implementasi yang tidak boleh dipertimbangkan oleh aplikasi, karena satu-satunya waktu bahkan terlihat adalah jika Anda menggunakan a SELECT *.

Jon dari Semua Perdagangan
sumber
4
Ini tidak relevan. Jika Anda mengakses kolom dengan indeks kolom dalam kode aplikasi Anda, maka Anda layak memiliki aplikasi yang rusak. Mengakses kolom dengan nama selalu menghasilkan kode aplikasi yang lebih mudah dibaca dan hampir tidak pernah menjadi hambatan kinerja.
Lie Ryan
3

Secara fisik dan bermasalah diizinkan untuk menggunakan select * from table, namun, itu ide yang buruk. Mengapa?

Pertama-tama, Anda akan menemukan bahwa Anda mengembalikan kolom yang tidak Anda butuhkan (resource resource).

Kedua, ini akan memakan waktu lebih lama pada tabel besar daripada memberi nama kolom karena ketika Anda memilih *, Anda benar-benar memilih nama kolom dari database dan mengatakan "berikan saya data yang terkait dengan kolom yang memiliki nama dalam daftar lainnya ini . " Meskipun ini cepat untuk programmer, bayangkan melakukan ini mencari di komputer bank yang mungkin memiliki ratusan ribu pencarian dalam satu menit.

Ketiga, melakukan ini sebenarnya membuat lebih sulit bagi pengembang. Seberapa sering Anda perlu membalik-balik dari SSMS ke VS untuk mendapatkan semua nama kolom?

Keempat, itu pertanda program malas dan saya tidak berpikir bahwa pengembang mana pun menginginkan reputasi itu.

CharlieHorse
sumber
Argumen kedua Anda dalam formulir ini memiliki beberapa kesalahan kecil. Pertama, semua RDBMS cache skema tabel, terutama karena skema akan tetap dimuat pada tahap penguraian kueri untuk menentukan kolom mana yang ada atau tidak ada dalam tabel dari kueri. Jadi, parser kueri sudah menanyakan daftar nama kolom sendiri, dan langsung mengganti * dengan daftar kolom. Kemudian, sebagian besar mesin RDBMS mencoba untuk me-cache semua yang ia bisa, jadi jika Anda mengeluarkan SELECT * FROM table, maka query yang dikompilasi akan di-cache sehingga parsing tidak terjadi setiap waktu. Dan pengembang malas :-)
Gabor Garami
Mengenai argumen kedua Anda, ini adalah kesalahpahaman umum - masalah dengan SELECT * bukan pencarian metadata, karena jika Anda memberi nama kolom, SQL Server masih harus memvalidasi nama mereka, memeriksa tipe data, dll.
Aaron Bertrand
@Gabor Salah satu masalah dengan SELECT * terjadi ketika Anda melihatnya. Jika Anda mengubah skema yang mendasarinya, tampilan bisa menjadi bingung - sekarang memiliki konsep skema tabel (sendiri) yang berbeda dari tabel itu sendiri. Saya membicarakan hal ini di sini .
Aaron Bertrand
3

Ini bisa menjadi masalah jika Anda memasukkan Select * ...kode ke dalam sebuah program, karena, seperti yang ditunjukkan sebelumnya, database mungkin berubah seiring waktu dan memiliki lebih banyak kolom daripada yang Anda harapkan saat Anda menulis kueri. Ini dapat menyebabkan kegagalan program (kasus terbaik) atau program mungkin berjalan dengan cara yang meriah dan merusak beberapa data karena itu melihat nilai-nilai bidang yang tidak ditulis untuk ditangani. Singkatnya, kode produksi harus SELALU menentukan bidang yang akan dikembalikan dalam SELECT.

Karena itu, saya memiliki sedikit masalah ketika Select *bagian dari EXISTSklausa, karena semua yang akan dikembalikan ke program adalah boolean yang menunjukkan keberhasilan atau kegagalan pemilihan. Orang lain mungkin tidak setuju dengan pendirian ini dan saya menghargai pendapat mereka tentang hal itu. MUNGKIN menjadi sedikit kurang efisien untuk kode Select *daripada kode 'Pilih 1' dalam EXISTSklausa, tapi saya tidak berpikir ada bahaya kerusakan data.

Mark Ross
sumber
Sebenarnya, ya, saya bermaksud merujuk klausa ADA. Kesalahanku.
Mark Ross
2

Banyak jawaban mengapa select *salah, jadi saya akan membahas ketika saya merasa itu benar atau setidaknya OK.

1) Dalam EXISTS, konten bagian SELECT dari kueri diabaikan, sehingga Anda bahkan dapat menulis SELECT 1/0dan itu tidak akan salah. EXISTShanya memverifikasi bahwa beberapa data akan kembali dan mengembalikan boolean berdasarkan itu.

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

2) Ini mungkin memulai badai api, tapi saya suka menggunakan select *di tabel histori saya pemicu. Oleh select *, itu mencegah tabel utama dari mendapatkan kolom baru tanpa menambahkan kolom ke tabel sejarah juga dengan kesalahan segera ketika dimasukkan / diperbarui / dihapus ke dalam tabel utama. Ini telah mencegah beberapa kali pengembang menambahkan kolom dan lupa menambahkannya ke tabel riwayat.

UnhandledExcepSean
sumber
3
Saya masih lebih suka SELECT 1karena itu paling jelas memberi tahu pengelola kode tentang niat Anda di masa depan. Itu bukan keharusan , tetapi jika saya melihatnya ... WHERE EXISTS (SELECT 1 ...)dengan jelas mengumumkan dirinya sebagai ujian kebenaran.
swasheck
1
@zlatan Banyak orang menggunakan SELECT 1berdasarkan mitos bahwa kinerja akan lebih baik daripada SELECT *. Namun, kedua opsi tersebut dapat diterima. Tidak ada perbedaan dalam kinerja karena cara pengoptimal menangani EXIS. Juga tidak ada perbedaan dalam keterbacaan karena kata "EXISTS" yang dengan jelas mengumumkan ujian kebenaran.
Disillusioned
Pada poin # 2, saya mengerti alasan Anda, tetapi masih ada risiko. Biarkan saya 'melukis skenario untuk Anda' ... Pengembang menambahkan Column8ke tabel utama melupakan tabel sejarah. Pengembang menulis banyak kode yang ditransfer ke Kolom 8. Kemudian ia menambahkan Column9ke tabel utama; ingat kali ini juga menambah sejarah. Kemudian ketika pengujian dia menyadari bahwa dia lupa untuk menambah Column9riwayat (terima kasih untuk teknik deteksi kesalahan Anda), dan segera menambahkannya. Sekarang pelatuk tampaknya berfungsi, tetapi data di kolom 8 & 9 tercampur dalam sejarah. : S
Disillusioned
cont ... Intinya adalah bahwa skenario 'mengarang' di atas hanyalah salah satu dari banyak yang dapat mengakibatkan trik deteksi kesalahan Anda gagal Anda, dan benar-benar membuat segalanya menjadi lebih buruk. Pada dasarnya Anda membutuhkan teknik yang lebih baik. Yang tidak bergantung pada pemicu Anda membuat asumsi tentang urutan kolom dalam tabel yang Anda pilih. Saran: - Ulasan kode pribadi dengan daftar periksa kesalahan umum Anda. - Ulasan kode rekan. - Teknik alternatif untuk melacak riwayat (secara pribadi saya menganggap mekanisme berbasis pemicu sebagai reaktif daripada proaktif, dan karena itu rentan terhadap kesalahan).
Disillusioned
@CraigYoung Itu kemungkinan. Tapi saya akan mencekik seseorang jika mereka melakukan itu. Itu bukan kesalahan yang bisa Anda lakukan dengan mudah
UnhandledExcepSean