Нахождение наименьшего значения в таблице больше определенного значения - PullRequest
6 голосов
/ 20 апреля 2009

Скажите, у меня есть следующие данные

Name      Value
===============
Small        10
Medium      100
Large      1000

Представьте, что они представляют собой объемы коробок. У меня есть несколько предметов, которые я хочу положить в коробки, и я хочу наименьшую возможную коробку. Мне нужен запрос SQL, который будет:

  1. Вернуть строку с наименьшей строкой, большей, чем параметр моего запроса
  2. Если такой строки нет, вернуть самую большую строку.

Легко разделить это на два запроса (т. Е. Сначала указать точку запроса 1 и, если строки не возвращаются, выбрать наибольшее число из таблицы). Тем не менее, мне нравится делать вещи в одном запросе, если это возможно, чтобы исключить накладные расходы (как переключение кода, так и переключение контекста), и похоже, что это можно сделать. Это, наверное, очень очевидно, но солнце сияло на меня весь день, и я не могу думать!

Так, например, я хочу, чтобы запрос возвращал 10, если вы используете параметр 5, 100, если вы используете параметр 15 и 1000, если вы используете что-либо больше 100 (включая числа больше 1000).

Я на Oracle 11g, так что с любыми особыми благами Oracle все в порядке.

Ответы [ 6 ]

2 голосов
/ 20 апреля 2009
SELECT  *
FROM    (
        SELECT  *
        FROM    (
                SELECT  *
                FROM    mytable
                WHERE   value > 10000
                ORDER BY
                        value
                )
        UNION ALL
        SELECT  *
        FROM    (
                SELECT  *
                FROM    mytable
                ORDER BY
                        value DESC
                )
        )
WHERE   rownum = 1

Это будет эффективно использовать индекс для mytable(value) и COUNT(STOPKEY).

См. Эту статью в моем блоге для деталей производительности:

1 голос
/ 20 апреля 2009
WITH ranges_table AS
     (SELECT     LEVEL * 100 AS range_value
            FROM DUAL
      CONNECT BY LEVEL <= 20)
SELECT MIN (range_value)
  FROM ranges_table
 WHERE range_value >= 5 OR range_value = (SELECT MAX (range_value)
                                            FROM ranges_table)
1 голос
/ 20 апреля 2009

Ради интереса я сделал предположение, что целевые размеры взяты из таблицы пакетов, и вы хотите найти коробки для набора пакетов. COALESCE выбирает второе значение, если первое равно NULL.

SELECT  
    p.pkgid,  
    p.pkgsize,  
    COALESCE(MIN(b1.size), MAX(b2.size) AS boxsize    
FROM packages AS p  
LEFT JOIN boxes AS b1 ON p.pkgsize < b1.boxsize  
LEFT JOIN boxes AS b2  -- yes, a cartesian join 
GROUP BY p.pkgid, p.pkgsize

В качестве одного оператора для сравнения с другими решениями используйте

SELECT  
    COALESCE(MIN(b1.size), MAX(b2.size) AS boxsize    
FROM Table AS t1,  
     Table AS t2   
WHERE targetsize < t1.Value
1 голос
/ 20 апреля 2009
SELECT MAX(Value)
FROM Table
WHERE Value <= LEAST(@param,(SELECT MAX(Value) FROM Table))

Я не очень хорошо знаком с Oracle, но уверен, что у него есть ПОЛНАЯ функция или что-то подобное.

В любом случае подзапрос этого запроса будет быстрым с правильным индексом в столбце Value.

На полном серьезе вы действительно должны сделать это в двух запросах (или двух шагах в одной хранимой процедуре, если вы хотите сохранить их в одном месте), потому что второй запрос не нужен, если первый запрос работает. Объединение их в один запрос обязательно дает вам безусловный второй (или под) запрос. Вы должны запросить таблицу дважды, поэтому вопрос заключается в том, будете ли вы запрашивать ее дважды всегда или только когда это необходимо.

0 голосов
/ 20 апреля 2009

Это работает. Замените «5» на ваш параметр.

select min(basket_value) as basket_value
from baskets
where basket_value > 5 
   or basket_value = (select max(basket_value) from baskets)

Простой скрипт для генерации тестовых данных:

create table baskets(
  basket_name varchar2(20)
 ,basket_value number
);

insert into baskets(basket_name,basket_value) values('Small',10);
insert into baskets(basket_name,basket_value) values('Medium',100);
insert into baskets(basket_name,basket_value) values('Large',1000);
commit;

--drop table baskets;  --run when finished
0 голосов
/ 20 апреля 2009
select a.newvalue from (
select MIN(value) as newvalue, 1 as order  From table where value > @param
union select MAX(value) as newvalue, 2 as order from table) A
order by a.order
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...