Haruskah saya mengindeks kolom bit di SQL Server?

99

Saya ingat pernah membaca pada satu titik bahwa mengindeks bidang dengan kardinalitas rendah (jumlah nilai berbeda yang rendah) sebenarnya tidak layak dilakukan. Saya akui, saya tidak cukup tahu tentang cara kerja indeks untuk memahami mengapa demikian.

Jadi bagaimana jika saya memiliki tabel dengan 100 juta baris di dalamnya, dan saya memilih catatan dengan bidang bit 1? Dan katakanlah pada titik waktu mana pun, hanya ada sedikit record yang bidang bitnya adalah 1 (sebagai lawan 0). Apakah layak mengindeks bidang bit itu atau tidak? Mengapa?

Tentu saja saya bisa mengujinya dan mengecek rencana pelaksanaannya, dan saya akan melakukannya, tapi saya juga penasaran dengan teori di baliknya. Kapan kardinalitas penting dan kapan tidak?

jeremcc
sumber
Apakah ini pertanyaan umum? Ini mungkin bermanfaat saat mencari "segelintir" rekaman tetapi tidak akan banyak membantu Anda di baris lain. Apakah ada cara lain untuk mengidentifikasi data?
jason saldo
4
Meskipun saya tidak berpikir saya akan mengindeks kolom bit HANYA dengan sendirinya, sangat umum untuk memasukkan kolom bit sebagai bagian dari indeks gabungan. Contoh sederhananya adalah indeks di ACTIVE, LASTNAME, bukan hanya nama belakang, saat aplikasi Anda hampir selalu mencari pelanggan aktif.
BradC
"Saya ingat pernah membaca pada satu titik bahwa mengindeks bidang dengan kardinalitas rendah (jumlah nilai berbeda yang rendah) tidak benar-benar layak dilakukan" Itu karena SQL Server akan hampir selalu menemukan lebih efisien untuk hanya melakukan pemindaian tabel daripada membaca indeks. Jadi pada dasarnya indeks Anda tidak akan pernah digunakan dan itu sia-sia untuk memeliharanya. Seperti yang dikatakan orang lain, mungkin baik-baik saja dalam indeks gabungan.
DJ.
5
Saya tidak setuju. Jika distribusi Anda 50/50, maka Anda tidak akan pernah menggunakan indeks, karena akan lebih cepat melakukan pemindaian tabel. Namun, jika Anda hanya memiliki nilai 5, 1, dan 1 juta 0, kemungkinan besar akan menggunakan indeks saat menelusuri 1.
Kibbee
1
Dalam contoh yang Anda berikan, saya akan lebih cenderung untuk menempatkan NamaBelakang terlebih dahulu. Itu tergantung pada beban kerja kueri tertentu, tetapi secara umum memiliki kolom yang lebih selektif terlebih dahulu, berarti indeks lebih mungkin digunakan.
Mitch Wheat

Jawaban:

72

Pertimbangkan apa itu indeks dalam SQL - dan indeks sebenarnya adalah potongan memori yang menunjuk ke potongan memori lainnya (yaitu, penunjuk ke baris). Indeks dipecah menjadi beberapa halaman sehingga bagian dari indeks dapat dimuat dan dikeluarkan dari memori tergantung pada penggunaan.

Saat Anda meminta satu set baris, SQL menggunakan indeks untuk menemukan baris lebih cepat daripada pemindaian tabel (melihat setiap baris).

SQL memiliki indeks berkerumun dan tidak berkerumun. Pemahaman saya tentang indeks berkerumun adalah bahwa mereka mengelompokkan nilai indeks yang serupa ke dalam halaman yang sama. Dengan cara ini ketika Anda meminta semua baris yang cocok dengan nilai indeks, SQL dapat mengembalikan baris tersebut dari halaman memori berkerumun. Inilah sebabnya mengapa mencoba untuk mengelompokkan indeks kolom GUID adalah ide yang buruk - Anda tidak mencoba untuk mengelompokkan nilai acak.

Saat Anda mengindeks kolom integer, indeks SQL berisi sekumpulan baris untuk setiap nilai indeks. Jika Anda memiliki rentang 1 hingga 10, maka Anda akan memiliki 10 penunjuk indeks. Bergantung pada berapa banyak barisnya, ini dapat dibuat halaman berbeda. Jika kueri Anda mencari indeks yang cocok dengan "1" dan kemudian di mana Nama berisi "Fred" (dengan asumsi kolom Nama tidak diindeks), SQL mendapatkan kumpulan baris yang cocok dengan "1" dengan sangat cepat, kemudian tabel memindai untuk menemukan sisanya.

Jadi apa yang sebenarnya dilakukan SQL adalah mencoba mengurangi set kerja (jumlah baris) yang harus diiterasi.

Saat Anda mengindeks bidang bit (atau beberapa rentang sempit), Anda hanya mengurangi kumpulan kerja dengan jumlah baris yang cocok dengan nilai itu. Jika Anda memiliki sejumlah kecil baris yang cocok, itu akan banyak mengurangi set kerja Anda. Untuk sejumlah besar baris dengan distribusi 50/50, ini mungkin memberi Anda sedikit keuntungan kinerja vs. menjaga indeks tetap mutakhir.

Alasan semua orang mengatakan untuk menguji adalah karena SQL berisi pengoptimal yang sangat pintar dan kompleks yang dapat mengabaikan indeks jika memutuskan pemindaian tabel lebih cepat, atau mungkin menggunakan semacam, atau mungkin mengatur halaman memori sesuka hatinya.

Geoff Cox
sumber
Jadi kedengarannya seperti jika saya hanya memiliki beberapa baris dengan bidang bit 1 (misalnya melacak "IsProcessed"), maka indeks akan bagus karena akan mengurutkan berdasarkan nilai dan kemudian dapat memilih set kerja kecil dengan sangat cepat. Jika Anda setuju, tambahkan itu dan saya akan menerimanya.
jeremcc
2
Apa yang saya maksud dalam komentar saya sebelumnya adalah bahwa pernyataan ini: "Saat Anda mengindeks bidang bit (atau beberapa kisaran sempit), Anda hanya mengurangi set kerja menjadi setengah" tidak benar jika distribusi sangat berbobot ke satu nilai. Tapi saya suka jawaban Anda yang lain, jadi jika Anda memperbaikinya, saya akan menerimanya.
jeremcc
1
Selesai. Saya berpikir bahwa untuk satu juta baris, bidang bit akan memiliki distribusi 50%, tetapi Anda benar bahwa untuk ruang masalah tertentu hal itu dapat mengurangi banyak set kerja.
Geoff Cox
Ini bermanfaat untuk melihat rencana eksekusi dengan dan tanpa indeks, dan melihat apakah indeks sedang digunakan dan apakah itu benar-benar mengurangi biaya kueri Anda. Mudah dan ilmiah!
onupdatecascade
Bagaimana dengan mengindeks sedikit bidang + bidang lain? Misalnya. di log aktivitas web, seseorang akan mengindeks stempel waktu, tetapi indeks lain yang berguna mungkin ada di bidang bit "IsHTTPS" + stempel waktu, untuk melihat semua tindakan https dengan cepat. Apakah itu juga tidak efisien?
bahan_15939
19

Saya baru saja menemukan pertanyaan ini melalui pertanyaan lain. Dengan asumsi bahwa pernyataan Anda bahwa hanya segelintir catatan yang mengasumsikan nilai 1 (dan itu adalah yang Anda minati), maka indeks yang difilter bisa menjadi pilihan yang baik. Sesuatu seperti:

create index [IX_foobar] on dbo.Foobar (FooID) where yourBitColumn = 1

Ini akan membuat indeks yang jauh lebih kecil sehingga pengoptimal cukup pintar untuk digunakan jika itu adalah predikat dalam kueri Anda.

Ben Thul
sumber
1
Perlu dicatat bahwa predikat dalam kueri harus di-hardcode ke nilai dalam indeks yang difilter. Jika Anda meneruskan nilai dalam parameter yourBitColumn = @value, maka pengoptimal tidak dapat menentukan apakah indeks yang difilter dapat digunakan.
geofftnz
2
Ada cara untuk mengatasi ini, tetapi Anda benar; pengoptimal memerlukan jaminan pada waktu kompilasi bahwa nilai untuk predikat apa pun yang cocok dengan predikat indeks yang difilter bersifat statis / invarian karena tugas pengoptimal adalah membuat rencana umum yang akan berfungsi untuk kumpulan parameter apa pun .
Ben Thul
9

100 juta rekaman dengan hanya sedikit yang bidang bitnya disetel ke 1? Ya, menurut saya mengindeks bidang bit pasti akan mempercepat kueri bit = 1 catatan. Anda harus mendapatkan waktu pencarian logaritmik dari indeks dan kemudian hanya menyentuh beberapa halaman dengan catatan bit = 1. Jika tidak, Anda harus menyentuh semua halaman dari tabel catatan 100 juta.

Kemudian lagi, saya jelas bukan ahli database dan mungkin melewatkan sesuatu yang penting.

C.Naga 76
sumber
8

Jika distribusi Anda cukup dikenal dan tidak seimbang, seperti 99% baris adalah bit = 1 dan 1% adalah bit = 0, ketika Anda melakukan klausa WHERE dengan bit = 1, pemindaian tabel lengkap akan berlangsung sekitar waktu yang sama dengan pemindaian indeks. Jika Anda ingin mendapatkan kueri cepat di mana bit = 0, cara terbaik yang saya tahu adalah membuat indeks yang difilter, menambahkan klausa WHERE bit = 0. Dengan begitu, indeks tersebut hanya akan menyimpan baris 1%. Kemudian melakukan WHERE bit = 0 hanya akan membiarkan pengoptimal kueri memilih indeks itu, dan semua baris darinya akan menjadi bit = 0. Anda juga mendapat manfaat memiliki ruang disk yang sangat kecil yang diperlukan dibandingkan indeks penuh pada bit .

Philippe Boucher
sumber
2
Jika 99% baris adalah bit = 1, pengoptimal harus mengabaikan indeks dan melakukan pemindaian tabel. Menggunakan indeks sebenarnya akan lebih buruk daripada pemindaian tabel, setidaknya pada drive rotasi, lebih banyak I / O dan pembacaan tidak berurutan dari disk. Indeks yang difilter (setara dengan Postgres: indeks parsial) adalah caranya. Saya kira karena itu bertahun-tahun setelah pertanyaan, jawaban ini tidak mendapatkan suara yang layak.
Andrew Lazarus
7

Meskipun saya tidak berpikir saya akan mengindeks kolom bit HANYA dengan sendirinya, sangat umum untuk memasukkan kolom bit sebagai bagian dari indeks gabungan.

Contoh sederhananya adalah indeks di ACTIVE, LASTNAME, bukan hanya nama belakang, saat aplikasi Anda hampir selalu mencari pelanggan aktif.

BradC
sumber
7
Dalam contoh yang Anda berikan, saya akan lebih cenderung untuk menempatkan NamaBelakang terlebih dahulu. Itu tergantung pada beban kerja kueri tertentu, tetapi secara umum memiliki kolom yang lebih selektif terlebih dahulu, berarti indeks lebih mungkin digunakan.
Mitch Wheat
7

Jika Anda belum membacanya, Jason Massie menulis artikel baru-baru ini yang membahas topik ini.

http://statisticsio.com/Home/tabid/36/articleType/ArticleView/articleId/302/Never-Index-a-BIT.aspx

Sunting: Lokasi artikel baru - http://sqlserverpedia.com/blog/sql-server-bloggers/never-index-a-bit

Mesin wayback untuk lokasi artikel "Baru" sebelumnya: http://web.archive.org/web/20120201122503/http://sqlserverpedia.com/blog/sql-server-bloggers/never-index-a-bit/

Lokasi SQL Server Pedia baru adalah Toadworld, yang memiliki artikel baru dari Kenneth Fisher yang membahas topik ini:

http://www.toadworld.com/platforms/sql-server/b/weblog/archive/2014/02/17/dba-myths-an-index-on-a-bit-column-will-never-be- used.aspx

mesin wayback: http://web.archive.org/web/20150508115802/http://www.toadworld.com/platforms/sql-server/b/weblog/archive/2014/02/17/dba-myths-an -index-on-a-bit-column-will-never-be-used.aspx

Jeff
sumber
artikel ini tidak lagi terlihat
Homer6
@ Homer6 Saya menambahkan tautan ke tampilan seperti rumah baru untuk artikel ini.
Jeff
Tautan baru menuju ke beranda Toad World.
N West
Menemukan artikel menggunakan mesin Wayback, dan menemukan artikel terkait baru. Semoga ini membantu.
Jeff
2

Tentu saja itu berharga, terutama jika Anda perlu mengambil data dengan nilai itu. Ini akan mirip dengan menggunakan matriks renggang daripada menggunakan matriks normal.

Sekarang dengan SQL 2008 Anda dapat menggunakan fungsi partisi, dan Anda dapat memfilter data yang masuk ke dalam indeks. Kerugian untuk versi sebelumnya adalah bahwa indeks akan dibuat untuk semua data, tetapi ini dapat dioptimalkan dengan menyimpan nilai yang menarik dalam grup file terpisah.

Bogdan Maxim
sumber
2

Seperti yang dikatakan orang lain, Anda pasti ingin mengukur ini. Saya tidak ingat di mana saya membaca ini, tetapi kolom harus memiliki kardinalitas yang sangat tinggi (sekitar 95%) agar indeks menjadi efektif. Tes terbaik Anda untuk ini adalah membangun indeks dan memeriksa rencana eksekusi untuk nilai 0 dan 1 dari bidang BIT. Jika Anda melihat operasi pencarian indeks dalam rencana eksekusi, maka Anda tahu bahwa indeks Anda akan digunakan.

Tindakan terbaik Anda adalah menguji dengan tabel SELECT * FROM dasar WHERE BitField = 1; membuat kueri dan perlahan-lahan membangun fungsionalitas dari sana selangkah demi selangkah hingga Anda memiliki kueri yang realistis untuk aplikasi Anda, memeriksa rencana eksekusi dengan setiap langkah untuk memastikan bahwa pencarian indeks masih digunakan. Diakui, tidak ada jaminan bahwa rencana pelaksanaan ini akan digunakan dalam produksi, tetapi kemungkinan besar akan digunakan.

Beberapa informasi dapat ditemukan di forum sql-server-performance.com dan di artikel referensi

Jeremiah Peschka
sumber
Yang penting bukanlah kardinalitas kolom secara keseluruhan. Ini adalah selektivitas klausa WHERE. Jadi jika ada sedikit kolom dengan nilai 1, masih bagus untuk diindeks. Jika 50/50 (misalnya pria / wanita) maka tidak begitu berharga.
WW.
2

"Saya ingat pernah membaca di satu titik bahwa mengindeks bidang dengan kardinalitas rendah (jumlah nilai berbeda yang rendah) tidak benar-benar bermanfaat"

Itu karena SQL Server akan selalu merasa lebih efisien dengan hanya melakukan pemindaian tabel daripada membaca indeks. Jadi pada dasarnya indeks Anda tidak akan pernah digunakan dan itu sia-sia untuk memeliharanya. Seperti yang dikatakan orang lain, mungkin baik-baik saja dalam indeks gabungan.

DJ.
sumber
2

Jika tujuan Anda adalah membuat kueri untuk rekaman di mana nilai bidang bit sama dengan '1' lebih cepat, Anda dapat mencoba tampilan terindeks dari tabel dasar Anda yang hanya berisi rekaman di mana bidang bit Anda sama dengan '1'. Dalam edisi perusahaan, jika kueri dapat menggunakan tampilan yang diindeks daripada tabel yang ditentukan untuk meningkatkan kinerja kueri, itu akan menggunakan tampilan. Secara teori, ini akan meningkatkan kecepatan kueri pemilihan yang hanya mencari rekaman dengan nilai bidang bit '1'.

http://www.microsoft.com/technet/prodtechnol/sql/2005/impprfiv.mspx

Semua ini mengasumsikan Anda adalah Microsoft SQL Server 2005 Enterprise. Hal yang sama mungkin berlaku untuk 2008, saya tidak terbiasa dengan versi itu.


sumber
2

Jika Anda ingin mengetahui apakah indeks memiliki efek yang Anda inginkan: uji dan uji lagi.

Secara umum Anda tidak menginginkan indeks yang tidak cukup mempersempit tabel Anda, karena biaya untuk mempertahankan indeks. (biaya> keuntungan). Tetapi jika indeks dalam kasus Anda akan memotong tabel menjadi dua, Anda mungkin mendapatkan sesuatu selain meletakkannya di atas meja. Itu semua tergantung pada ukuran / struktur tabel Anda dan bagaimana Anda menggunakannya (jumlah baca / tulis).

thijs
sumber
1

Dengan sendirinya, tidak karena menghasilkan selektivitas yang sangat sedikit. Sebagai bagian dari indeks majemuk. sangat mungkin tetapi hanya setelah kolom kesetaraan lainnya.

Craig Nicholson
sumber
1

Anda tidak dapat mengindeks bidang bit di SQL Server 2000, seperti yang ditunjukkan di Buku Daring pada saat itu:

sedikit

Tipe data integer 1, 0, atau NULL.

Catatan

Kolom jenis bit tidak boleh memiliki indeks di atasnya.

Ya, jika Anda hanya memiliki sedikit baris, dari jutaan, indeks akan membantu. Tetapi jika Anda ingin melakukannya dalam hal ini Anda perlu membuat kolom a tinyint.

Catatan : Manajer Perusahaan tidak akan membiarkan Anda membuat indeks pada kolom bit. Jika mau, Anda masih dapat membuat indeks secara manual pada kolom bit:

CREATE INDEX IX_Users_IsActiveUsername ON Users
(
   IsActive,
   Username
)

Tetapi SQL Server 2000 tidak akan benar-benar menggunakan indeks seperti itu - menjalankan kueri di mana indeks akan menjadi kandidat yang sempurna, misalnya:

SELECT TOP 1 Username 
FROM Users
WHERE IsActive = 0

SQL Server 2000 akan melakukan pemindaian tabel, bertindak seolah-olah indeks tersebut bahkan tidak ada. Jika Anda mengubah kolom menjadi tinyint SQL Server 2000 akan melakukan pencarian indeks. Juga, kueri tidak tercakup berikut:

SELECT TOP 1 * 
FROM Users
WHERE IsActive = 0

Ini akan melakukan pencarian indeks, diikuti oleh pencarian bookmark.


SQL Server 2005 memiliki dukungan terbatas untuk indeks pada kolom bit. Sebagai contoh:

SELECT TOP 1 Username 
FROM Users
WHERE IsActive = 0

akan menyebabkan pencarian indeks melalui indeks penutup. Tapi kasus yang tidak tertutup:

SELECT TOP 1 * 
FROM Users
WHERE IsActive = 0

tidak akan menyebabkan pencarian indeks diikuti oleh pencarian bookmark, ia akan melakukan pemindaian tabel (atau pemindaian indeks berkerumun), daripada melakukan pencarian indeks yang diikuti dengan pencarian bookmark.

Diverifikasi dengan eksperimen dan observasi langsung.

Ian Boyd
sumber
FYI - SQL Server 2005 Management Studio memungkinkan Anda melakukannya.
jeremcc
Salinan SQL Server 2000 saya memungkinkan saya menetapkan indeks pada kolom bit.
Kibbee
Salinan SQL Server 2000 saya tidak mengizinkan saya menetapkan indeks pada kolom bit.
Ian Boyd
1

jawaban yang sangat terlambat ...

Ya, ini dapat berguna menurut tim SQL CAT (diperbarui, telah dikonsolidasikan)

gbn
sumber
1
Tautan tampaknya sudah mati sekarang. Namun, postingan tersebut tampaknya telah digabungkan dengan beberapa postingan lainnya dalam sebuah e-book . Bagian referensi dimulai pada halaman 86. e-book dapat diunduh dari eBook SQLCAT.com di bawah link "Panduan SQLCAT untuk Mesin Relasional".
mwolfe02
0

Apakah ini pertanyaan umum? Ini mungkin bermanfaat saat mencari "segelintir" rekaman tetapi tidak akan banyak membantu Anda di baris lain. Apakah ada cara lain untuk mengidentifikasi data?

jason saldo
sumber
0

Kardinalitas adalah satu faktor, yang lainnya adalah seberapa baik indeks membagi data Anda. Jika Anda memiliki sekitar setengah 1 dan setengah 0, maka itu akan membantu. (Dengan asumsi bahwa indeks adalah jalur yang lebih baik untuk dipilih daripada indeks lainnya). Namun, seberapa sering Anda memasukkan dan memperbarui? Menambahkan indeks untuk kinerja SELECT juga merusak kinerja INSERT, UPDATE dan DELETE, jadi ingatlah itu.

Saya akan mengatakan, jika 1 hingga 0 (atau sebaliknya) tidak lebih baik dari 75% hingga 25%, jangan repot-repot.

Anthony Potts
sumber
1
Saya tidak setuju. Jika distribusi Anda 50/50, maka Anda tidak akan pernah menggunakan indeks, karena akan lebih cepat melakukan pemindaian tabel. Namun, jika Anda hanya memiliki nilai 5, 1, dan 1 juta 0, kemungkinan besar akan menggunakan indeks saat menelusuri 1.
Kibbee
0

ukur waktu respons sebelum dan sesudah dan lihat apakah itu bermanfaat; secara teoritis itu harus meningkatkan kinerja untuk kueri yang menggunakan bidang yang diindeks tetapi itu sangat tergantung pada distribusi nilai benar / salah dan bidang lain yang terlibat dalam kueri yang Anda khawatirkan

Steven A. Lowe
sumber
0

Ian Boyd benar ketika dia mengatakan bahwa Anda tidak dapat melakukannya melalui Manajer Perusahaan untuk SQL 2000 (lihat catatannya tentang pembuatannya melalui T-SQL.

John B
sumber
0

Anda harus pintar di sini untuk membuat kueri, Anda harus mengetahui nilai beban pada kolom Anda jika beban benar lebih banyak di sistem Anda dan Anda ingin memeriksa semua nilai yang benar tulis kueri Anda untuk memeriksa bukan salah .. itu akan banyak membantu , itu hanya tipuan.

Chetan Verma
sumber