【问题标题】:Oracle Analytic function for min value in groupingOracle 分析函数用于分组中的最小值
【发布时间】:2010-12-04 17:32:12
【问题描述】:

我是使用分析函数的新手。

部门雇员工资 ---- ----- ------ 100000 年 3 月 10 日 10 约翰 200000 10 斯科特 300000 20 鲍勃 100000 20 贝蒂 200000 30艾伦100000 30 汤姆 200000 30 杰夫 300000

我想要最低工资的部门和员工。

结果应如下所示:

部门雇员工资 ---- ----- ------ 100000 年 3 月 10 日 20 鲍勃 100000 30艾伦100000

编辑:这是我拥有的 SQL(但当然,它不起作用,因为它也需要 group by 子句中的人员):

选择部门, 雇员, MIN(salary) KEEP (DENSE_RANK FIRST ORDER BY Salary) 来自我的表 按部门分组

【问题讨论】:

    标签: sql oracle analytic-functions top-n


    【解决方案1】:

    您可以使用RANK() 语法。例如,此查询将告诉您员工在其部门内的薪水有多大排名:

    SELECT
      dept,
      emp,
      salary,
      (RANK() OVER (PARTITION BY dept ORDER BY salary)) salary_rank_within_dept
    FROM EMPLOYEES
    

    然后你可以从这里查询salary_rank_within_dept = 1:

    SELECT * FROM
      (
        SELECT
          dept,
          emp,
          salary,
          (RANK() OVER (PARTITION BY dept ORDER BY salary)) salary_rank_within_dept
        FROM EMPLOYEES
      )
    WHERE salary_rank_within_dept = 1
    

    【讨论】:

    • 完美!我还不知道 RANK() 。谢谢。
    • 直到昨天我才知道 RANK()! :)
    • 由于我在自己的回答中概述的原因,我对此表示反对:我认为这可能效率低下,并且我认为该查询与所提出的确切问题不太匹配。我不是说它不会给出正确的答案,只是它不能很好地表达问题的逻辑。
    • @David:从答案时间戳中可以明显看出,您在投票否决我的同时提升了我的答案。
    • 您的答案 rexem 不使用分析函数。我是如何“举起”它的?
    【解决方案2】:
    select e2.dept, e2.emp, e2.salary
    from employee e2
    where e2.salary = (select min(e1.salary) from employee e1)
    

    【讨论】:

    • 这将为您提供一条记录 - 整个表格的最小值。您需要在子选择中按部门分组。
    【解决方案3】:

    我认为 Rank() 函数不是解决这个问题的方法,原因有两个。

    首先,它可能比基于 Min() 的方法效率低。

    这样做的原因是,查询必须在扫描数据时维护每个部门所有薪水的有序列表,然后稍后通过重新读取此列表来分配排名。显然,在没有可以利用的索引的情况下,在读取最后一个数据项之前,您无法分配排名,并且列表的维护成本很高。

    因此 Rank() 函数的性能取决于要扫描的元素总数,如果数量足以使排序溢出到磁盘,那么性能就会崩溃。

    这可能更有效:

    select dept,
           emp,
           salary
    from
           (
           SELECT dept, 
                  emp,
                  salary,
                  Min(salary) Over (Partition By dept) min_salary
           FROM   mytable
           )
    where salary = min_salary
    /
    

    此方法只要求查询为每个部门维护一个迄今为止遇到的最小值的单个值。如果遇到新的最小值,则修改现有值,否则丢弃新值。必须保存在内存中的元素总数与部门数有关,而不是与扫描的行数有关。

    可能是 Oracle 有一个代码路径来识别在这种情况下实际上不需要计算排名,但我不会打赌。

    不喜欢 Rank() 的第二个原因是它只是回答了错误的问题。问题不是“当每个部门的薪水升序排列时,哪些记录的薪水排名第一”,而是“哪些记录的每个部门的薪水最低”。至少,这对我有很大影响。

    【讨论】:

    • 谢谢大卫。在考虑了它的好处之后,我重构了你的解决方案。
    【解决方案4】:

    我认为您与原始查询非常接近。以下将运行并匹配您的测试用例:

    SELECT dept, 
      MIN(emp) KEEP(DENSE_RANK FIRST ORDER BY salary, ROWID) AS emp,
      MIN(salary) KEEP (DENSE_RANK FIRST ORDER BY salary, ROWID) AS salary
    FROM mytable
    GROUP BY dept
    

    与 RANK() 解决方案相比,这一解决方案保证每个部门最多一行。但这暗示了一个问题:在一个有两名员工最低工资的部门会发生什么? RANK() 解决方案将返回两个员工——该部门不止一行。此答案将任意选择一个,并确保该部门只有一个。

    【讨论】:

    • 是的,这是多条记录的一个好点。 Min() 方法将检索所有重复项……如果需要,要为那些返回一条记录会更棘手。
    • 精益求精——尤其是当分析更关注最小值的时。如果需要识别属性最小值,保留重复项似乎是可取的。
    猜你喜欢
    • 1970-01-01
    • 2017-10-21
    • 2015-02-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-02-25
    • 2015-10-04
    • 2012-05-02
    相关资源
    最近更新 更多