2009-04-11 18 views
12

Tengo una base de datos SQLite y tengo en ella cierta columna de tipo "doble". Quiero obtener una fila que tenga en este valor de columna el más cercano al especificado.SQLite - obteniendo el valor más cercano

Por ejemplo, en mi mesa tengo:

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

y quiero obtener una fila que tiene su valor más cercano a 50. Así que desea recibir ID: 3 (valor = 51).

¿Cómo puedo lograr este objetivo?

Gracias.

+0

Tenga en cuenta que el sistema de tipo sqlite es especial y si tiene un doble verdadero no tiene nada que ver con ninguna declaración de tipo. – unmounted

Respuesta

14

Esto debería funcionar:

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

Dónde ? representa el valor que se desea comparar.

+1

Obviamente funcionará, pero ¿está realmente optimizado para funcionar en el tiempo 'log N'? – ybungalobill

+1

@ybungalobill Dudo mucho que cualquier optimizador sea capaz de determinar cómo determinar de manera óptima qué teclas producirán la respuesta más pequeña para la expresión 'ABS (? - value)'. – Alnitak

7

Al hacer un pedido, SQLite escaneará toda la tabla y cargará todos los valores en un b-tree temporal para ordenarlos, haciendo que cualquier índice sea inútil. Esto será muy lento y utilizar una gran cantidad de memoria en grandes tablas:

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 

Puede obtener el siguiente valor inferior o superior utilizando el índice de la siguiente manera:

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

Y puede utilizar union a obtener ambos desde una sola consulta:

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) 

Esto será bastante rápido incluso en las tablas grandes. Usted simplemente puede cargar ambos valores y evaluarlos en su código, o utilizar aún más SQL para seleccionar una de varias maneras:

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 

Puesto que usted está interesado en los valores tanto de forma arbitraria mayores y menos que el objetivo, no puede evitar hacer dos búsquedas de índice. Si usted sabe que el objetivo está dentro de un rango pequeño, sin embargo, se puede usar "entre" sólo para golpear índice una vez:

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 

Este será de alrededor de 2 veces más rápido que la consulta de unión por encima de cuando se evalúa únicamente 1 -2 valores, pero si comienza a tener que cargar más datos, se volverá más lento.

+1

Tuve problemas de rendimiento al consultar grandes bases de datos (50GB +) y tu solución hizo que mi parte de consultas de mi aplicación x20 fuera más rápida que la solución de la respuesta aceptada * votado * – Westranger

Cuestiones relacionadas