2009-04-11 21 views
12

SQLite veritabanına sahibim ve "double" türünde belirli bir sütuna sahibim. Belirtilene en yakın bu sütun değerine sahip bir satır almak istiyorum.SQLite - en yakın değeri alma

id: 1; value: 47 
id: 2; value: 56 
id: 3; value: 51 

Ve 50'ye yakın yüzden id almak istiyorum değerini aldığı bir satır almak istiyorum:

Örneğin benim tabloda ben 3 (değer = 51).

Bu hedefe nasıl ulaşabilirim?

Teşekkürler.

+0

sqlite tipi sistemin özel olduğunu ve gerçek bir çiftinizin herhangi bir tür bildirimi ile ilgisi olup olmadığını göz önünde bulundurun. – unmounted

cevap

14

Bu çalışması gerekir: ? Eğer karşı karşılaştırmak istediğiniz değeri temsil

SELECT * FROM table 
ORDER BY ABS(? - value) 
LIMIT 1 

.

+1

Açıkçası işe yarayacak, ama aslında 'log N' zamanında çalışmak için optimize edilmiş mi? – ybungalobill

+1

@ybungalobill Herhangi bir optimizatörün, hangi tuşların 'ABS (? - value)' ifadesi için en küçük cevabı üreteceğini en iyi nasıl belirleyeceğini anlayabileceğinden şüphe duyuyorum. – Alnitak

7

Bir sipariş kullanarak, SQLite tüm tabloyu tarar ve tüm değerleri sipariş vermek için geçici bir b-ağacına yükler ve herhangi bir dizini işe yaramaz hale getirir. Bu çok yavaş olabilir ve büyük tablolarda çok fazla bellek kullanır:

explain query plan select * from 'table' order by abs(10 - value) limit 1; 
0|0|0|SCAN TABLE table 
0|0|0|USE TEMP B-TREE FOR ORDER BY 
Böyle dizini kullanarak bir sonraki düşük veya daha yüksek bir değer elde edebilirsiniz

:

select min(value) from 'table' where x >= N; 
select max(value) from 'table' where x <= N; 

Ve üzere union kullanabilirsiniz her ikisi de tek bir sorgudan olsun: Bu büyük tablolarda bile oldukça hızlı olacaktır. Sadece iki değeri de yük ve kodunuzda bunları değerlendirmek veya çeşitli şekillerde birini seçmek için daha sql kullanabilirsiniz:

explain query plan select v from 
    (  select min(value) as v from 'table' where value >= 10 
    union select max(value) as v from 'table' where value <= 10) 
    order by abs(10-v) limit 1; 
2|0|0|SEARCH TABLE table USING COVERING INDEX value_index (value>?) 
3|0|0|SEARCH TABLE table USING COVERING INDEX value_index (value<?) 
1|0|0|COMPOUND SUBQUERIES 2 AND 3 USING TEMP B-TREE (UNION) 
0|0|0|SCAN SUBQUERY 1 
0|0|0|USE TEMP B-TREE FOR ORDER BY 

veya değerler hem keyfi daha ilgilendiğiniz yana

explain query plan select 10+v from 
    (  select min(value)-10 as v from 'table' where value >= 10 
    union select max(value)-10 as v from 'table' where value <= 10) 
    group by v having max(abs(v)) limit 1; 
2|0|0|SEARCH TABLE table USING COVERING INDEX value_index (value>?) 
3|0|0|SEARCH TABLE table USING COVERING INDEX value_index (value<?) 
1|0|0|COMPOUND SUBQUERIES 2 AND 3 USING TEMP B-TREE (UNION) 
0|0|0|SCAN SUBQUERY 1 
0|0|0|USE TEMP B-TREE FOR GROUP BY 

ve hedeften daha az, iki dizinli arama yapmaktan kaçınamazsınız. Hedef küçük bir aralık içinde olduğunu biliyorsanız, olsa da, sadece bir kez indeksi vurmak "arasında" kullanabilirsiniz:

explain query plan select * from 'table' where value between 9 and 11 order by abs(10-value) limit 1; 
0|0|0|SEARCH TABLE table USING COVERING INDEX value_index (value>? AND value<?) 
0|0|0|USE TEMP B-TREE FOR ORDER BY 

Bu sadece 1 değerlendirir yukarıdaki sendika sorgusu daha etrafında 2x daha hızlı olacaktır -2 değer, ancak daha fazla veri yüklemeye başladığınızda hızlıca yavaşlar.

+1

Büyük veritabanlarını (50GB +) sorgularken performans sorunları yaşadım ve çözümüm, x20 uygulamasının sorgulama bölümünün kabul edilen yanıtın çözümünden daha hızlı bir şekilde yapılmasını sağladı * – Westranger