代码之家  ›  专栏  ›  技术社区  ›  Greg Reynolds

查找表中大于某个值的最小值

  •  6
  • Greg Reynolds  · 技术社区  · 16 年前

    假设我有以下数据

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

    假设这些代表盒子的体积。我有一些东西要放在盒子里,我想要尽可能小的盒子。我需要一个SQL查询,它将:

    1. 返回最小行大于我的查询参数的行
    2. 如果没有这样的行,则返回最大的行。

    很容易将其拆分为两个查询(即首先查询点1,如果没有返回行,则从表中选择最大的数字)。然而,如果可能的话,我喜欢在一个查询中做一些事情来消除开销(代码和上下文切换),看起来应该可以做到。这可能是很明显的,但太阳整天都在照耀着我,我无法思考!

    我在Oracle 11g上,所以任何特殊的Oracle Goods都可以。

    6 回复  |  直到 16 年前
        1
  •  2
  •   Quassnoi    16 年前
    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)

    有关性能详细信息,请参阅我博客中的这篇文章:

        2
  •  1
  •   Welbog    16 年前
    SELECT MAX(Value)
    FROM Table
    WHERE Value <= LEAST(@param,(SELECT MAX(Value) FROM Table))
    

    在任何情况下,此查询的子查询都将是swift,并在 Value

    非常严肃地说,您确实应该在两个查询中完成这项工作(如果希望将它们保持在同一位置,则在一个存储过程中执行两个步骤),因为如果第一个查询有效,则第二个查询是不必要的。将它们组合在一个查询中必然会得到无条件的第二个(或子)查询。您必须查询该表两次,因此问题是您是否查询了两次 总是 或者在必要的时候。

        3
  •  1
  •   dkretz    16 年前

    只是为了好玩,我假设目标尺寸来自一个包裹表,你想找到一堆包裹的盒子。如果第一个值为空,则COALESCE选择第二个值。

    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
    
        4
  •  1
  •   Brian    16 年前
    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)
    
        5
  •  0
  •   JosephStyons    16 年前

    这很有效。用参数替换“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
    
        6
  •  -1
  •   James Curran    16 年前
    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