2009-04-11 42 views
12

私はSQLiteデータベースを持っており、その中に "double"型の特定の列があります。 この列に指定された値に最も近い値を持つ行を取得したいとします。SQLite - 最も近い値を得る

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

そして、私は50に最も近いだから私はIDを受信したい、その値を持つ行を取得したい:

は例えば、私のテーブルに私が持っている3(値= 51)。

この目標を達成するにはどうすればよいですか?

ありがとうございました。

+0

sqlite型システムは特殊で、真の倍精度を持つかどうかは型宣言とは関係ありません。 – unmounted

答えて

14

これは動作するはずです:?はあなたがと比較する値を表し

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

+1

明らかにうまくいくでしょうが、実際に 'log N'時に最適化されていますか? – ybungalobill

+1

@ybungalobill私は、どのオプティマイザが、どのキーが 'ABS(? - value)'式のための最小の答えを生み出すかを最適に決定する方法を理解することができるのではないかと疑います。 – Alnitak

7

order-byを使用すると、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) 

これは大きなテーブルさえも上で非常に高速になります。あなたは、単に両方の値をロードし、あなたのコードでそれらを評価し、様々な方法で1を選択するためにも、より多くの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 

を使用すると、両方の任意の大きな値に興味を持っているので、ターゲットよりも少ない場合は、2回の索引検索を避けることはできません。あなたは、ターゲットが小さい範囲内であることがわかっている場合は、しかし、あなたは一度だけインデックスをヒットする「間」を使用することができは:

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 

これは、1つだけを評価するとき上記のユニオンクエリよりも周りの2倍高速になります-2の値になりますが、より多くのデータをロードしなければならないと、すぐに遅くなります。

+1

大きなデータベース(50GB +)を照会しているときにパフォーマンス上の問題がありました。ソリューションでは、自分のアプリケーションx20の照会部分が、受け入れられた回答* – Westranger

関連する問題