2009-04-11 14 views
12

Ich habe SQLite-Datenbank und ich habe darin bestimmte Spalte des Typs "double". Ich möchte eine Zeile erhalten, die in diesem Spaltenwert einem angegebenen Wert am nächsten kommt.SQLite - den nächsten Wert erhalten

Zum Beispiel in meinem Tisch habe ich:

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

Und ich möchte eine Reihe bekommen, was dessen Wert So am nächsten 50. hat Ich mag id erhalten: 3 (Wert = 51).

Wie kann ich dieses Ziel erreichen?

Danke.

+0

Beachten Sie, dass das SQLite-System speziell ist und ob Sie ein echtes Double haben, hat nichts mit Typdeklarationen zu tun. – unmounted

Antwort

14

Dies sollte funktionieren:

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

Wo ? den Wert, den Sie gegen vergleichen wollen darstellt.

+1

Es wird offensichtlich funktionieren, aber ist es tatsächlich optimiert, um in 'log N' Zeit zu arbeiten? – ybungalobill

+1

@ybungalobill Ich bezweifle sehr, dass jeder Optimierer in der Lage wäre, herauszufinden, wie man optimal bestimmen kann, welche Schlüssel die kleinste Antwort für den Ausdruck 'ABS (? - value)' liefern würden. – Alnitak

7

Mithilfe einer order-by wird SQLite die gesamte Tabelle scannen und alle Werte in eine temporäre b-Struktur laden, um sie zu ordnen, wodurch jeder Index unbrauchbar wird. Dies wird sehr langsam sein und viel Speicher für große Tabellen verwenden:

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 

Sie können die nächst niedrigeren oder höheren Wert zu bekommen, den Index wie folgt aus:

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

Und können Sie union verwenden bekomme beide von einer einzigen Abfrage:

Dies wird auch auf großen Tabellen ziemlich schnell sein. Sie könnten einfach beide Werte laden und sie in Ihrem Code bewerten, oder verwenden sogar mehr SQL ein auf verschiedene Weise zu wählen:

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 

oder

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 

Da Sie in Werte interessiert sind beide willkürlich größer und weniger als das Ziel, können Sie nicht zwei Indexsuchen vermeiden. Wenn Sie wissen, dass das Ziel in einem kleinen Bereich ist aber, könnte man „zwischen“ verwenden, nur den Index einmal getroffen:

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 

Dies wird rund 2x schneller als die Union-Abfrage oben, wenn es wertet nur 1 -2 Werte, aber wenn Sie beginnen, mehr Daten zu laden, wird es schnell langsamer.

+1

Ich hatte Leistungsprobleme beim Abfragen großer Datenbanken (50 GB +) und Ihre Lösung hat meinen Teil der Anwendung x20 schneller abgefragt als die Lösung der angenommenen Antwort * upvoted * – Westranger

Verwandte Themen