2009-04-11 3 views
12

У меня есть база данных SQLite, и у меня есть в ней определенный столбец типа «double». Я хочу получить строку, которая имеет в этом столбце значение, ближайшее к указанному.SQLite - получение ближайшего значения

Например, в моей таблице у меня есть:

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

И я хочу, чтобы получить строку, которая имеет свое значение ближе к 50. Так что я хочу получить идентификатор: 3 (значение = 51).

Как достичь этой цели?

Спасибо.

+0

Имейте в виду, что система типа sqlite является специальной и имеет ли у вас истинный двойник ничего общего с объявлениями любого типа. – unmounted

ответ

14

Это должно работать:

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

Где ? представляет значение, которое вы хотите сравнить против.

+1

Он, очевидно, будет работать, но действительно ли он оптимизирован для работы в 'log N' time? – ybungalobill

+1

@ybungalobill Я очень сомневаюсь, что любой оптимизатор сможет определить, как оптимально определить, какие клавиши будут давать наименьший ответ для выражения 'ABS (? - value). – Alnitak

7

Используя SQL-запрос, SQLite сканирует всю таблицу и загружает все значения во временное дерево b-tree для их упорядочения, делая любой индекс бесполезным. Это будет очень медленно и использовать много памяти на больших таблицах:

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 

Вы можете получить следующее ниже или выше значение, используя индекс, как это:

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

И вы можете использовать union для получить как от одного запроса:

explain query plan 
     select min(value) from 'table' where value >= 10 
    union select max(value) from 'table' where value <= 10; 
1|0|0|SEARCH TABLE table USING COVERING INDEX value_index (value>?) 
2|0|0|SEARCH TABLE table USING COVERING INDEX value_index (value<?) 
0|0|0|COMPOUND SUBQUERIES 1 AND 2 USING TEMP B-TREE (UNION) 

Это будет довольно быстро даже на больших столах. Вы можете просто загрузить оба значения и оценивать их в коде, или использовать еще больше SQL, чтобы выбрать один различными способами:

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 

или

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 

Поскольку вы заинтересованы в значениях и произвольно больше и меньше, чем цель, вы не можете избежать выполнения двух запросов индекса. Если вы знаете, что цель находится в небольшом диапазоне, однако, вы можете использовать «между» только ударил индекс один раз:

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 

Это будет примерно 2 раза быстрее, чем запрос накидной выше, когда он оценивает только 1 -2, но если вы начнете загружать больше данных, он быстро станет медленнее.

+1

У меня были проблемы с производительностью при запросе больших баз данных (50 ГБ +), и ваше решение сделало мою часть запроса моего приложения x20 быстрее, чем решение принятого ответа * upvoted * – Westranger

Смежные вопросы