2009-04-11 20 views
12

Ho un database SQLite e in esso sono presenti alcune colonne di tipo "double". Voglio ottenere una riga che ha il valore di questa colonna più vicino a uno specificato.SQLite - ottenere il valore più vicino

Ad esempio, nel mio tavolo ho:

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

e voglio ottenere una fila che ha il suo valore più vicino a 50. Quindi voglio ricevere id: 3 (valore = 51).

Come posso raggiungere questo obiettivo?

Grazie.

+0

Ricordare che il sistema di tipo sqlite è speciale e se si dispone di un vero doppio non ha nulla a che fare con dichiarazioni di tipo. – unmounted

risposta

14

Questo dovrebbe funzionare:

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

Dove ? rappresenta il valore che si desidera confrontare con.

+1

Ovviamente funzionerà, ma in realtà è ottimizzato per funzionare in tempo 'log N'? – ybungalobill

+1

@ybungalobill Dubito fortemente che qualsiasi ottimizzatore sarebbe in grado di capire come determinare in modo ottimale quali chiavi produrrebbero la risposta più piccola per l'espressione 'ABS (? - value)'. – Alnitak

7

Utilizzando un ordine, SQLite esegue la scansione dell'intera tabella e carica tutti i valori in un b-tree temporaneo per ordinarli, rendendo inutile qualsiasi indice. Questo sarà molto lento e utilizzare un sacco di memoria su grandi tavoli:

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 

È possibile ottenere il valore successivo inferiore o superiore utilizzando l'indice in questo modo:

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

Ed è possibile utilizzare union per ottenere entrambi da una singola query:

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) 

Questo sarà abbastanza veloce anche su tavoli di grandi dimensioni. Si potrebbe semplicemente caricare entrambi i valori e valutarli nel codice, o utilizzare anche più di SQL per selezionare uno in vari modi:

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 

o

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 

Dal momento che siete interessati a entrambi i valori arbitrariamente grandi e meno del target, non puoi evitare di fare due ricerche su indici. Se si sa che il bersaglio è all'interno di un piccolo intervallo, però, è possibile utilizzare "tra" a colpire solo l'indice una volta:

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 

Questo si aggirano intorno a 2 volte più veloce rispetto alla query di unione di cui sopra quando si valuta solo 1 -2 valori, ma se si inizia a dover caricare più dati diventerà rapidamente più lento.

+1

Ho riscontrato problemi di prestazioni durante l'interrogazione di database di grandi dimensioni (50 GB +) e la tua soluzione ha reso la mia parte di query della mia applicazione x20 più veloce della soluzione della risposta accettata * con l'upvoted * – Westranger

Problemi correlati