Найти ближайшее числовое значение в базе данных - PullRequest
39 голосов
/ 26 февраля 2009

Мне нужно найти оператор выбора, который будет возвращать либо запись, которая точно соответствует моему вводу, либо самое близкое совпадение, если точное совпадение не найдено.

Вот мое утверждение select.

SELECT * FROM [myTable] 
WHERE Name = 'Test' AND Size = 2 AND PType = 'p' 
ORDER BY Area DESC

Что мне нужно сделать, так это найти ближайшее совпадение с полем «Площадь», поэтому, если мой ввод равен 1.125, а база данных содержит 2, 1.5, 1 и .5, запрос вернет запись, содержащую 1.

Мои навыки SQL очень ограничены, поэтому любая помощь будет оценена.

Ответы [ 8 ]

66 голосов
/ 26 февраля 2009

получите разницу между областью и вашим входом, возьмите абсолютное значение, всегда положительное, затем упорядочите по возрастанию и возьмите первое

SELECT TOP 1 * FROM [myTable] 
WHERE Name = 'Test' and Size = 2 and PType = 'p'
ORDER BY ABS( Area - @input ) 
9 голосов
/ 26 февраля 2009

что-то ужасное, в духе:

ORDER BY ABS( Area - 1.125 ) ASC LIMIT 1

Может быть?

4 голосов
/ 27 февраля 2009

Если у вас есть много строк, которые удовлетворяют предикатам равенства в столбцах Name, Size и PType, то вы можете включить предикаты диапазона в столбец Area в свой запрос. Если столбец Area проиндексирован, это может обеспечить эффективный доступ на основе индекса.

Следующий запрос (написанный с использованием синтаксиса Oracle) использует одну ветвь UNION ALL, чтобы найти запись с минимальной Area >= вашей целью, в то время как другая ветвь находит запись с максимальной Area < вашей целью. Одна из этих двух записей будет той записью, которую вы ищете. Затем вы можете ORDER BY ABS(Area - ?input) выбрать победителя из этих двух кандидатов. К сожалению, запрос сложный из-за вложенных SELECTS, которые необходимы для обеспечения желаемого приоритета ROWNUM / ORDER BY.

SELECT *
FROM
  (SELECT * FROM
    (SELECT * FROM
       (SELECT * FROM [myTable]
         WHERE Name = 'Test' AND Size = 2 AND PType = 'p' AND Area >= ?target
         ORDER BY Area)
       WHERE ROWNUM < 2
     UNION ALL
     SELECT * FROM 
       (SELECT * FROM [myTable]
         WHERE Name = 'Test' AND Size = 2 AND PType = 'p' AND Area < ?target
         ORDER BY Area DESC)
       WHERE ROWNUM < 2)
     ORDER BY ABS(Area - ?target))
WHERE rownum < 2

Хорошим индексом для этого запроса будет (Name, Size, PType, Area), и в этом случае ожидаемый план выполнения запроса будет основан на двух сканированиях диапазона индекса, каждый из которых вернул одну строку.

3 голосов
/ 26 февраля 2009
SELECT * 
  FROM [myTable] 
  WHERE Name = 'Test' AND Size = 2 AND PType = 'p' 
  ORDER BY ABS(Area - 1.125)
  LIMIT 1

- MarkusQ

3 голосов
/ 26 февраля 2009

Как насчет упорядочения по разнице между вашим вводом и [Площадь], например:

DECLARE @InputValue DECIMAL(7, 3)

SET @InputValue = 1.125

SELECT TOP 1 * FROM [myTable]
WHERE Name = 'Test' AND Size = 2 AND PType = 'p'
ORDER BY ABS(@InputValue - Area)
1 голос
/ 26 февраля 2009

Обратите внимание, что хотя ABS () поддерживается практически всем, это технически не стандартно (по крайней мере, в SQL99). Если вы по какой-то причине должны написать стандарт SQL ANSI, вам придется обойти проблему с оператором CASE:

SELECT * FROM myTable
WHERE Name='Test' AND Size=2 AND PType='p'
ORDER BY CASE Area>1.125 WHEN 1 THEN Area-1.125 ELSE 1.125-Area END
1 голос
/ 26 февраля 2009

При использовании MySQL

SELECT * FROM [myTable] ... ORDER BY ABS(Area - SuppliedValue) LIMIT 1
0 голосов
/ 28 декабря 2013

Выберите мин где [поле]> your_target_value Выберите максимум, где [поле]

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...