Sütunları endekslediğimde bu sqlite sorgusu neden daha yavaş?


14

(Sahte) isimleri içeren, her biri 50.000 satır ile iki tablo ile bir sqlite veritabanı var. Her iki tablo için ortak olan kaç ad (verilen ad, orta ad, soyadı) olduğunu bulmak için basit bir sorgu oluşturdum:

select count(*) from fakenames_uk inner join fakenames_usa on fakenames_uk.givenname=fakenames_usa.givenname and fakenames_uk.surname=fakenames_usa.surname and fakenames_uk.middleinitial=fakenames_usa.middleinitial;

Birincil anahtarlar (bu sorgu ile alakasız) dışında hiçbir dizin yoksa, hızlı bir şekilde çalışır:

[james@marlon Downloads] $ time sqlite3 generic_data_no_indexes.sqlite "select count(*) from fakenames_uk inner join fakenames_usa on fakenames_uk.givenname=fakenames_usa.givenname and fakenames_uk.surname=fakenames_usa.surname and fakenames_uk.middleinitial=fakenames_usa.middleinitial;"
131

real    0m0.115s
user    0m0.111s
sys     0m0.004s

Ancak, her bir tablodaki üç sütuna dizinler eklersem (toplamda altı dizin):

CREATE INDEX `idx_uk_givenname` ON `fakenames_uk` (`givenname` )
//etc.

sonra acı verici yavaş çalışır:

[james@marlon Downloads] $ time sqlite3 generic_data.sqlite "select count(*) from fakenames_uk inner join fakenames_usa on fakenames_uk.givenname=fakenames_usa.givenname and fakenames_uk.surname=fakenames_usa.surname and fakenames_uk.middleinitial=fakenames_usa.middleinitial;"
131

real    1m43.102s
user    0m52.397s
sys     0m50.696s

Bunun herhangi bir kafiye veya nedeni var mı?

Dizinsiz EXPLAIN QUERY PLANsürümün sonucu :

0|0|0|SCAN TABLE fakenames_uk
0|1|1|SEARCH TABLE fakenames_usa USING AUTOMATIC COVERING INDEX (middleinitial=? AND surname=? AND givenname=?)

Bu dizinlerle:

0|0|0|SCAN TABLE fakenames_uk
0|1|1|SEARCH TABLE fakenames_usa USING INDEX idx_us_middleinitial (middleinitial=?)

1
Dizinleriniz kapsam dahilinde değil. Her sütunu ayrı ayrı dizine eklediğiniz anlaşılıyor. Eğer bir dizin her üç sütun içeren bir kapak dizin oluşturmak (ne olur middleinitial, surnameve givenname)?
Randolph West

@Randoph West Ne demek istediğinizi anlıyorum, ama doğru terminolojiyi kullanmıyorsunuz: bir "örtme indeksi" de seçilen sütunları içeren bir dildir. Örneğin, sorgu SELECT c FROM t WHERE a=1 AND b=2için dizin t(a,b,c)örtüyor ancak kapsamıyor t(a,b). Kapsama endekslerinin yararı, tüm sorgu sonucunun doğrudan endeksten çekilebilmesidir, buna karşılık kapamayan endeksler hızlı bir şekilde ilgili satırları bulur, ancak yine de değerleri seçmek için ana tablo verilerine başvurması gerekir.
Arthur Tacca

Yanıtlar:


15

SQLite'de birleşimler iç içe döngü birleşimleri olarak yürütülür, yani veritabanı bir tablodan geçer ve her satır için diğer tablodan eşleşen satırları arar.

Bir dizin varsa, veritabanı dizindeki tüm eşleşmeleri hızlı bir şekilde arayabilir ve ardından gerekli diğer sütunların değerlerini almak için ilgili tablo satırına gidebilir.

Bu durumda, üç olası dizin vardır. Herhangi bir istatistiksel bilgi olmadan ( ANALYZE çalıştırılarak oluşturulacak ), veritabanı G / Ç'yi azaltmak için en küçük olanı seçer. Ancak, middleinitialdizin getirilmesi gereksizdir çünkü getirilmesi gereken tablo satırlarının sayısını büyük ölçüde azaltmaz; ve dizin boyunca ek adım aslında tablo satırları artık sırayla değil, rastgele okunduğu için gerekli G / Ç artırır.

Dizin yoksa, eşleşen satırların aranması için ilk tablonun her satırı için ikinci tablonun eksiksiz bir tablo taraması gerekir. Bu o kadar kötü olur ki, veritabanı sadece bu sorgu için geçici bir dizin oluşturup bırakmanın faydalı olduğunu tahmin eder. Bu geçici ("OTOMATİK") dizin, arama için kullanılan tüm sütunlarda oluşturulur. COUNT (*) işlemi başka herhangi bir sütundan değerlere ihtiyaç duymaz, bu nedenle bu dizin bir kaplama dizini olur , bu da aslında bir dizin girişine karşılık gelen tablo satırına bakmanın gerekli olmadığı anlamına gelir. /Ö.

Bu sorguyu hızlandırmak için, geçici bir dizin oluşturmak için artık gerekli olmayacak şekilde bu dizini kalıcı olarak oluşturun:

CREATE INDEX uk_all_names ON fakenames_uk(surname, givenname, middleinitial);

EXPLAIN QUERY PLAN
SELECT count(*)
FROM fakenames_uk
JOIN fakenames_usa USING (givenname, middleinitial, surname);

0|0|1|SCAN TABLE fakenames_usa
0|1|0|SEARCH TABLE fakenames_uk USING COVERING INDEX uk_all_names (surname=? AND givenname=? AND middleinitial=?)

Üzerinde endeksi surnameüç sütun dizini bu sütunda herhangi aramalarında kullanılacak, çünkü artık gerek yoktur. Yalnızca bu sütunda arama yapacaksanız
dizini givennameyararlı olabilir.
Açık dizin middleinitialher zaman değersizdir: tüm tabloyu tararsa 26 olası değerden birini arayan bir sorgu daha hızlıdır.

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.