SQL Server'da bir bit alanını indekslemeli miyim?


99

Bir noktada, düşük kardinaliteye (düşük sayıda farklı değer) sahip bir alanı indekslemenin gerçekten yapmaya değmediğini okuduğumu hatırlıyorum. Bunun nedenini anlamak için indekslerin nasıl çalıştığını yeterince bilmediğimi itiraf ediyorum.

Peki ya içinde 100 milyon satır olan bir tablom varsa ve bit alanının 1 olduğu kayıtları seçiyorsam? Ve diyelim ki herhangi bir zamanda, bit alanının 1 olduğu (0'ın aksine) yalnızca bir avuç kayıt vardır. Bu bit alanını indekslemeye değer mi değil mi? Neden?

Tabii ki sadece test edip uygulama planını kontrol edebilirim ve bunu yapacağım ama arkasındaki teoriyi de merak ediyorum. Kardinalite ne zaman önemli ve ne zaman önemli değil?


Bu yaygın bir sorgu mu? "Bir avuç" kayıt ararken buna değer olabilir, ancak diğer satırlarda size pek yardımcı olmayacaktır. Verileri tanımlamanın başka yolları var mı?
jason saldo

4
SADECE bir sütunu kendi başına endeksleyeceğimi düşünmüyorum, ancak bileşik bir dizinin parçası olarak bit sütunlarını dahil etmek çok yaygındır. Basit bir örnek, uygulamanız neredeyse her zaman aktif müşteriler aradığında, yalnızca soyad yerine ACTIVE, LASTNAME üzerinde bir dizin olabilir.
BradC

"Bir noktada, düşük kardinaliteye (düşük sayıda farklı değer) sahip bir alanı indekslemenin gerçekten yapmaya değmediğini okuduğumu hatırlıyorum." Çünkü SQL Server, neredeyse her zaman bir tablo taraması yapmaktan daha verimli bulacaktır. indeks. Yani temelde dizininiz asla kullanılmayacak ve onu korumak israftır. Diğerlerinin söylediği gibi, bileşik bir endekste sorun olmayabilir.
DJ.

5
Katılmıyorum. Dağıtımınız 50/50 ise, o zaman bir tablo taraması yapmak daha hızlı olacağından indeksi asla kullanmazsınız. Ancak, yalnızca 5, 1 değeriniz ve 1 milyon 0 değeriniz varsa, 1'i ararken dizini kullanmanız çok olasıdır.
Kibbee

1
Verdiğiniz örnekte, LastName'i ilk sıraya koymaya daha meyilli olurdum. Spesifik sorgu iş yüküne bağlıdır, ancak genel olarak önce daha seçici sütuna sahip olmak, dizinin kullanılma olasılığının daha yüksek olduğu anlamına gelir.
Mitch Wheat

Yanıtlar:


72

SQL'de bir indeksin ne olduğunu düşünün - ve indeks aslında diğer bellek parçalarını (yani satırları gösteren işaretçiler) işaret eden bir bellek yığınıdır. Dizin, kullanıma bağlı olarak dizinin bazı bölümlerinin bellekten yüklenip kaldırılabilmesi için sayfalara bölünmüştür.

Bir dizi satır istediğinizde, SQL, satırları tablo taramasından (her satıra bakarak) daha hızlı bulmak için dizini kullanır.

SQL kümelenmiş ve kümelenmemiş dizinlere sahiptir. Kümelenmiş dizinler hakkındaki anlayışım, benzer dizin değerlerini aynı sayfada gruplandırmalarıdır. Bu şekilde, bir dizin değeriyle eşleşen tüm satırları sorduğunuzda, SQL bu satırları kümelenmiş bir bellek sayfasından döndürebilir. Bu nedenle, bir GUID sütununu dizine eklemeye çalışmak kötü bir fikirdir - rastgele değerleri kümelemeye çalışmazsınız.

Bir tamsayı sütununu indekslediğinizde, SQL'in indeksi her indeks değeri için bir dizi satır içerir. 1 ile 10 arasında bir aralığınız varsa, o zaman 10 dizin işaretleyiciniz olur. Kaç satır olduğuna bağlı olarak bu farklı şekilde sayfalanabilir. Sorgunuz "1" ile eşleşen dizini ararsa ve sonra Ad "Fred" i içeriyorsa (Ad sütununun dizinlenmediğini varsayarak), SQL "1" ile eşleşen satır kümesini çok hızlı bir şekilde alır, ardından geri kalanını bulmak için tablo tarar.

Öyleyse, SQL'in gerçekten yaptığı şey, yinelemesi gereken çalışma kümesini (satır sayısını) azaltmaya çalışmaktır.

Bir bit alanını (veya bazı dar aralıkları) dizine eklediğinizde, çalışma kümesini yalnızca bu değerle eşleşen satırların sayısı kadar azaltırsınız. Eşleşen az sayıda satırınız varsa, çalışma kümenizi çok azaltır. 50/50 dağılımlı çok sayıda satır için, endeksi güncel tutmaya kıyasla çok az performans kazancı sağlayabilir.

Herkesin test etmeyi söylemesinin nedeni, SQL'in tablo taramanın daha hızlı olduğuna karar verirse bir dizini göz ardı edebilecek çok akıllı ve karmaşık bir optimize edici içermesi veya bir sıralama kullanması veya bellek sayfalarını iyi istediği şekilde düzenleyebilmesidir.


Öyleyse, sanki bit alanının 1 olduğu bir avuç satırım varsa (örneğin, "IsProcessed" i takip etmek için), o zaman bir dizin iyi olur çünkü onları değere göre sıralar ve sonra küçük çalışma seti çok hızlı. Kabul edersen, ekle, ben de kabul edeceğim.
jeremcc

2
Önceki yorumumda kastettiğim şu: "Bir bit alanı (veya biraz dar aralığı) indekslediğinizde, çalışma kümesini yalnızca yarıya indirirsiniz", dağılım bir değere doğru ağır bir şekilde ağırlıklandırılmışsa doğru değildir. Ama cevabınızın geri kalanını beğendim, bu yüzden eğer bunu düzeltirseniz, kabul ederim.
jeremcc

1
Bitti. Bir milyon satır için, bir bit alanın% 50 dağılıma sahip olacağını düşünüyordum, ancak haklısınız, belirli bir sorun alanı için çalışma kümesini çok azaltabilir.
Geoff Cox

İndeksli ve indekssiz yürütme planlarına bakmak ve indeksin kullanılıp kullanılmadığını ve sorgularınızın maliyetini gerçekten düşürüp düşürmediğini görmek faydalı olacaktır. Kolay ve bilimsel!
onupdatecascade

Bir bit alanını + başka bir alanı indekslemeye ne dersiniz? Örneğin. bir web etkinliği günlüğünde, zaman damgası dizine eklenir, ancak tüm https eylemlerini hızlı bir şekilde görüntülemek için başka bir yararlı dizin, "IsHTTPS" + zaman damgası bit alanında olabilir. Bu da verimsiz olur mu?
ingredient_15939

19

Bu soruya başka bir yolla rastladım. İfadenizin yalnızca bir avuç kayıttan 1 değerini aldığını (ve ilgilendiklerinizi) varsayarsak, filtrelenmiş bir dizin iyi bir seçim olabilir. Gibi bir şey:

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

Bu, sorgunuzda bir yüklem olduğunda optimize edicinin yeterince akıllı olduğu, önemli ölçüde daha küçük bir dizin oluşturacaktır.


1
Sorgudaki yüklemin filtrelenmiş dizindeki değere sabit kodlanması gerektiğini belirtmek gerekir. Değeri bir parametrede yourBitColumn = @valueiletirseniz, optimize edici filtrelenmiş dizinin kullanılabilir olup olmadığını belirleyemez.
geofftnz

2
Bunun etrafından dolaşmanın yolları var, ama haklısın; optimizer, herhangi bir parametre kümesi için çalışacak genel bir plan oluşturmak optimize edicinin görevi olduğundan, filtrelenmiş indeks koşulu ile eşleşen herhangi bir yüklem için değerlerin statik / değişmez olduğu için derleme zamanında bir garantiye ihtiyaç duyar .
Ben Thul

9

Sadece birkaç bit alanı 1 olarak ayarlanmış 100 milyon kayıt? Evet, bit alanını indekslemenin kesinlikle bit = 1 kayıtlarını sorgulamayı hızlandıracağını düşünüyorum. Dizinden logaritmik arama süresi almalı ve sonra sadece bit = 1 kayıtları olan birkaç sayfaya dokunmalısınız. Aksi takdirde, 100 milyonluk kayıt tablosunun tüm sayfalarına dokunmanız gerekir.

Yine de, kesinlikle bir veritabanı uzmanı değilim ve önemli bir şeyi kaçırıyor olabilirim.


8

Dağıtımınız oldukça biliniyorsa ve dengesizse, örneğin satırların% 99'u bit = 1 ve% 1'i bit = 0 ise, bit = 1 ile bir WHERE cümlesi yaptığınızda, tam bir tablo taraması yaklaşık olarak aynı anda olacaktır. dizin taraması. Bit = 0 olan hızlı bir sorgu istiyorsanız, bildiğim en iyi yol, WHERE bit = 0 cümlesi ekleyerek filtrelenmiş bir dizin oluşturmaktır. Bu şekilde, bu dizin yalnızca% 1 satırını saklayacaktır. Sonra bir WHERE bit = 0 yapmak, sorgu iyileştiricisinin bu dizini seçmesine izin verir ve ondan gelen tüm satırlar bit = 0 olur .


2
Satırların% 99'u bit = 1 ise, optimize edicinin dizini yok sayması ve bir tablo taraması yapması gerekir. Dizinin kullanılması aslında bir tablo taramasından daha kötü olacaktır , en azından rotasyonel bir sürücüde, daha fazla G / Ç ve diskten ardışık olmayan okumalar. Filtrelenmiş dizin (Postgres eşdeğeri: kısmi dizin) gitmenin yoludur. Sanırım sorudan yıllar sonra olduğu için bu cevap hak ettiği oyları almadı.
Andrew Lazarus

7

SADECE bir sütunu kendi başına endeksleyeceğimi düşünmüyorum, ancak bileşik bir dizinin parçası olarak bit sütunlarını dahil etmek çok yaygındır.

Basit bir örnek, uygulamanız neredeyse her zaman aktif müşterileri aradığında, yalnızca soyad yerine ACTIVE, LASTNAME üzerinde bir dizin olabilir.


7
Verdiğiniz örnekte, LastName'i ilk sıraya koymaya daha meyilli olurdum. Spesifik sorgu iş yüküne bağlıdır, ancak genel olarak önce daha seçici sütuna sahip olmak, dizinin kullanılma olasılığının daha yüksek olduğu anlamına gelir.
Mitch Wheat

7

bu makale artık görünür değil
Homer6

@ Homer6 Bu makale için yeni ev gibi görünen bir bağlantı ekledim.
Jeff

Yeni bağlantı Toad World ana sayfasına gider.
N West

Wayback makinesini kullanarak makaleyi buldum ve yeni bir ilgili makale buldum. Bu yardımcı olur umarım.
Jeff

2

Elbette buna değer, özellikle verileri bu değere göre almanız gerekiyorsa. Normal bir matris kullanmak yerine seyrek bir matris kullanmaya benzer.

Artık SQL 2008 ile bölümleme işlevlerini kullanabilir ve bir dizine giren verileri filtreleyebilirsiniz. Önceki sürümlerin dezavantajı, dizinin tüm veriler için yapılmasıdır, ancak bu, ilginç değerleri ayrı bir dosya grubunda depolayarak optimize edilebilir.


2

Başkalarının da söylediği gibi, bunu ölçmek isteyeceksiniz. Bunu nerede okuduğumu hatırlamıyorum, ancak bir dizinin etkili olabilmesi için bir sütunun çok yüksek kardinaliteye (yaklaşık% 95) sahip olması gerekir. Bunun için en iyi testiniz, dizini oluşturmak ve BIT alanının 0 ve 1 değerleri için yürütme planlarını incelemektir. Yürütme planında bir dizin arama işlemi görürseniz, dizininizin kullanılacağını bilirsiniz.

Yapmanız gereken en iyi şey, temel bir SELECT * FROM tablosu WHERE BitField = 1 ile test etmektir; sorgulayın ve uygulamanız için gerçekçi bir sorgu elde edene kadar işlevselliği adım adım yavaşça oluşturun, dizin aramasının hala kullanıldığından emin olmak için her adımda yürütme planını inceleyin. Kuşkusuz, bu uygulama planının üretimde kullanılacağına dair bir garanti yok, ancak iyi bir şans var.

Bazı bilgiler sql-server-performance.com forumlarında ve atıfta bulunulan makalede bulunabilir.


Önemli olan bir bütün olarak sütunun önem derecesi değil. WHERE cümlesinin seçiciliğidir. Dolayısıyla, değeri 1 olan birkaç sütun varsa, dizine eklemek yine de iyi olabilir. 50/50 ise (örneğin erkek / kadın) o zaman buna değmez.
WW.

2

"Bir noktada, düşük kardinaliteye (düşük sayıda farklı değer) sahip bir alanı indekslemenin gerçekten yapmaya değmediğini okuduğumu hatırlıyorum"

Çünkü SQL Server, dizini okumaktan ziyade sadece bir tablo taraması yapmak neredeyse her zaman daha verimli olacaktır. Yani temelde dizininiz asla kullanılmayacak ve onu korumak israftır. Diğerlerinin söylediği gibi, bileşik bir endekste sorun olmayabilir.


2

Amacınız, bit alanı değerinin '1'e eşit olduğu kayıtları daha hızlı sorgulamaksa, temel tablonuzun yalnızca bit alanınızın' 1'e eşit olduğu kayıtları içeren dizine alınmış bir görünümünü deneyebilirsiniz. Kurumsal sürümde, bir sorgu, sorgu performansını artırmak için belirtilen bir tablo yerine dizine alınmış bir görünümden yararlanabiliyorsa, görünümü kullanacaktır. Teoride bu, yalnızca '1' bit alanı değerine sahip kayıtları arayan seçme sorgularının hızını artıracaktır.

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

Bütün bunlar, Microsoft SQL Server 2005 Enterprise olduğunuzu varsayar. Aynısı 2008 için de geçerli olabilir, bu sürüme aşina değilim.


2

Bir dizinin arzu ettiğiniz etkilere sahip olup olmadığını öğrenmek istiyorsanız: tekrar test edin ve test edin.

Genel olarak, bir dizini korumanın maliyeti nedeniyle tablonuzu yeterince daraltmayan bir dizin istemezsiniz. (maliyet> kar). Ama sizin durumunuzdaki indeks tabloyu ikiye bölecekse, bir şey kazanabilirsiniz ama onu masaya koymak. Her şey tablonuzun tam boyutuna / yapısına ve onu nasıl kullandığınıza (okuma / yazma sayısı) bağlıdır.


1

Kendi başına hayır, çünkü çok az seçicilikle sonuçlanıyor. Bileşik bir endeksin parçası olarak. büyük olasılıkla ancak diğer eşitlik sütunlarından sonra.


1

Sen olamaz endeksi SQL Server 2000'de bir bit alanını anda Books Online'da belirtildiği gibi:

bit

Tam sayı veri türü 1, 0 veya NULL.

Uyarılar

Bit türündeki sütunlarda dizin olamaz.

Evet, milyonlarca satırdan yalnızca bir avuç satırınız varsa, bir dizin yardımcı olacaktır. Ama bu durumda yapmak istiyorsanız, sütun a yapmalısınız tinyint.

Not : Enterprise Manager bit sütununda dizin oluşturmanıza izin vermez. Dilerseniz, bir bit sütununda manuel olarak da bir dizin oluşturabilirsiniz:

CREATE INDEX IX_Users_IsActiveUsername ON Users
(
   IsActive,
   Username
)

Ancak SQL Server 2000 aslında böyle bir indeksi kullanmaz - indeksin mükemmel bir aday olacağı bir sorgu çalıştırır, örneğin:

SELECT TOP 1 Username 
FROM Users
WHERE IsActive = 0

SQL Server 2000 bunun yerine dizin yokmuş gibi davranarak bir tablo taraması yapar. Sütunu bir tinyint olarak değiştirirseniz, SQL Server 2000 bir dizin araması yapacaktır . Ayrıca aşağıdaki kapsanmayan sorgu:

SELECT TOP 1 * 
FROM Users
WHERE IsActive = 0

Bir dizin araması ve ardından bir yer imi araması gerçekleştirecektir.


SQL Server 2005, bit sütunlarındaki dizinler için sınırlı desteğe sahiptir. Örneğin:

SELECT TOP 1 Username 
FROM Users
WHERE IsActive = 0

kaplama indeksi aracılığıyla bir indeks aramasına neden olur. Ancak kapsanmayan dava:

SELECT TOP 1 * 
FROM Users
WHERE IsActive = 0

bir indeks aramaya ve ardından bir yer imi aramasına neden olmaz, indeks aramanın ardından bir yer imi araması yapmak yerine bir tablo taraması (veya kümelenmiş indeks taraması) gerçekleştirir.

Deney ve doğrudan gözlemle doğrulanmıştır.


Bilginize - SQL Server 2005 Management Studio bunu yapmanıza izin veriyor.
jeremcc

SQL Server 2000 kopyam bir bit sütununda bir indeks belirlememe izin verdi.
Kibbee

SQL Server 2000 kopyam, bit sütununda bir dizin belirlememe izin vermiyor.
Ian Boyd

1

çok geç cevap ...

Evet, SQL CAT ekibine göre faydalı olabilir (güncellendi, konsolide edildi)


1
Bağlantı artık kesilmiş görünüyor. Ancak, bu gönderi bir e-kitapta diğer birkaç kişiyle birlikte konsolide edilmiş gibi görünüyor . Referans verilen bölüm 86. sayfada başlıyor. E-kitap, SQLCAT.com eBooks'tan "SQLCAT's Guide to Relational Engine" bağlantısı altında indirilebilir .
mwolfe02

0

Bu yaygın bir sorgu mu? "Bir avuç" kayıt ararken buna değer olabilir, ancak diğer satırlarda size pek yardımcı olmayacaktır. Verileri tanımlamanın başka yolları var mı?


0

Önem faktörlerinden biri, diğeri ise dizinin verilerinizi ne kadar iyi böldüğüdür. Yaklaşık yarım 1'leriniz ve yarım 0'larınız varsa, o zaman yardımcı olacaktır. (Bu dizinin başka bir dizinden daha iyi bir yol olduğunu varsayarsak). Ancak, ne sıklıkla ekleyip güncelliyorsunuz? SELECT performansı için dizin eklemek INSERT, UPDATE ve DELETE performansına da zarar verir, bu yüzden bunu aklınızda bulundurun.

Diyorum ki, 1'den 0'a (veya tersi)% 75 ila% 25'ten daha iyi değilse, zahmet etmeyin.


1
Katılmıyorum. Dağıtımınız 50/50 ise, o zaman bir tablo taraması yapmak daha hızlı olacağından indeksi asla kullanmazsınız. Ancak, yalnızca 5, 1 değeriniz ve 1 milyon 0 değeriniz varsa, 1'i ararken dizini kullanmanız çok olasıdır.
Kibbee

0

öncesi ve sonrası yanıt süresini ölçün ve buna değip değmediğini görün; teorik olarak dizine alınmış alanları kullanan sorgular için performansı iyileştirmelidir, ancak gerçekten doğru / yanlış değerlerin dağılımına ve ilgilendiğiniz sorgulara dahil olan diğer alanlara bağlıdır.


0

Ian Boyd, SQL 2000 için Enterprise Manager aracılığıyla bunu yapamayacağınızı söylediğinde haklıdır (T-SQL aracılığıyla oluşturmaya ilişkin notuna bakın.


0

Burada sorgulamak için akıllı olmalısınız, eğer sisteminizde true yükü daha fazlaysa ve tüm gerçek değerleri kontrol etmek istiyorsanız, sütununuzdaki yük değerini bilmelisiniz ve sorgunuzu yanlış olmadığını kontrol etmek için yazın .. çok yardımcı olacaktır. , bu sadece hile.

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.