2009-04-11 181 views
12

我有SQLite數據庫,並且在其中有一些類型爲「double」的特定列。 我想獲得最接近指定值的列值。SQLite - 獲得最接近的價值

例如,在我的表,我有:

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

而且我希望得到一個行中具有其值最接近50所以我想收到ID:3(值= 51)。

我該如何實現這個目標?

謝謝。

+0

請記住,sqlite類型系統是特殊的,是否有一個真正的double與任何類型的聲明無關。 – unmounted 2009-04-11 09:35:29

回答

14

這應該工作:

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

其中?代表要比較的值。

+1

它顯然會工作,但它實際上是優化工作在`log N`時間? – ybungalobill 2014-07-02 16:16:01

+1

@ybungalobill我非常懷疑任何優化器都能夠弄清楚如何最優地確定哪些鍵會爲ABS(? - 值)表達式產生最小的答案。 – Alnitak 2014-07-02 16:59:08

7

通過使用順序,SQLite將掃描整個表並將所有值加載到臨時B樹中以對它們進行排序,從而使任何索引都無用。這將是非常緩慢的,使用大量的內存大表:

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 

這將是圍繞2X比聯合查詢時,只計算1以上的速度更快-2值,但如果你開始不得不加載更多的數據,它會很快變慢。