【问题标题】:Segregate results based on order in Oracle SQL在 Oracle SQL 中根据顺序分离结果
【发布时间】:2021-12-27 03:45:54
【问题描述】:

我正在尝试从员工列表中获取工作编号。如果他们多年来没有调动部门,那么这应该被视为一份工作。一旦他们搬到一个新部门,那应该被视为一份新工作。我面临的问题是,如果他们搬回一个部门,我的要求也应该被视为一份新工作。所以流程会是这样的:

  • 销售 -> 工作 1
  • IT -> 工作 2
  • 销售 -> 工作 3
  • 营销 -> 工作 4。

我在下面做了一个超级简化的查询来展示我想要实现的目标:

SELECT RANK() OVER(ORDER BY last_year_in_role) JOB_NO, name, Role
FROM
    (
    SELECT MAX(years_at_company) last_year_in_role, Name, Role
    FROM (
        SELECT 'Bob' name, 1 years_at_company, 'Sales' role FROM DUAL
        UNION
        SELECT 'Bob', 2, 'Sales' FROM DUAL
        UNION
        SELECT 'Bob', 3, 'Sales' FROM DUAL
        UNION 
        SELECT 'Bob', 4, 'IT' FROM DUAL
        UNION
        SELECT 'Bob', 5, 'Sales' FROM DUAL
        UNION
        SELECT 'Bob', 6, 'Marketing' FROM DUAL
        )
    GROUP BY Name, Role
    )
;

这会产生以下结果:

这是错误的,因为它总是将 SALES 中的所有时间组合在一起,即使数据中存在中断。我觉得这个问题可能可以通过以某种方式在 ROLE 上进行分区的 WINDOW 函数来解决,但我没有成功。

【问题讨论】:

  • 这是一个差距和孤岛问题,每个部门的变化都应该被视为“海滩”。
  • @TheImpaler,谢谢!这让我可以开始谷歌搜索。
  • @stickybit 我写的查询是最小的可重现示例。跑步会产生我遇到的问题。
  • @LennyMeerwood:公平点,我的错。

标签: sql oracle gaps-and-islands


【解决方案1】:

由于这是一个差距和岛屿问题,其中“海滩”被定义为部门变更。您可以使用LAG() 分析功能检测它们。例如,您可以这样做:

with data as (
        SELECT 'Bob' name, 1 years_at_company, 'Sales' role FROM DUAL
        UNION
        SELECT 'Bob', 2, 'Sales' FROM DUAL
        UNION
        SELECT 'Bob', 3, 'Sales' FROM DUAL
        UNION 
        SELECT 'Bob', 4, 'IT' FROM DUAL
        UNION
        SELECT 'Bob', 5, 'Sales' FROM DUAL
        UNION
        SELECT 'Bob', 6, 'Marketing' FROM DUAL
)
select *
from (
  select d.*,
    case when role = lag(role) over(partition by name order by years_at_company)
         then 0 else 1 end as beach
  from data d
) x
order by name, years_at_company

结果:

 NAME  YEARS_AT_COMPANY  ROLE       BEACH 
 ----- ----------------- ---------- ----- 
 Bob   1                 Sales      1     
 Bob   2                 Sales      0     
 Bob   3                 Sales      0     
 Bob   4                 IT         1     
 Bob   5                 Sales      1     
 Bob   6                 Marketing  1     

请参阅db<>fiddle 的运行示例。

上面的查询对数据进行了预处理以生成beach 列。有了它,您可以清楚地区分哪些行实际代表新工作,哪些行不代表。

您可以继续处理数据以根据您的需要对其进行聚合或过滤,但这应该会为您提供所需的所有信息。

【讨论】:

  • 你摇滚!如果其他人好奇我是如何使用这个答案的,我会在 BEACH = 1 上进行过滤并执行 RANK() OVER (ORDER BY YEARS_AT_COMPANY)。可以看到我修改的db<>fiddle here
【解决方案2】:

在 12.1 及更高版本中,最佳(最简单、最高效和最优雅)的解决方案使用 match_recognize 子句:

select name, job_no, role
from   (
         SELECT 'Bob' name, 1 years_at_company, 'Sales' role
                                      FROM DUAL UNION ALL
         SELECT 'Bob', 2, 'Sales'     FROM DUAL UNION ALL
         SELECT 'Bob', 3, 'Sales'     FROM DUAL UNION ALL
         SELECT 'Bob', 4, 'IT'        FROM DUAL UNION ALL
         SELECT 'Bob', 5, 'Sales'     FROM DUAL UNION ALL
         SELECT 'Bob', 6, 'Marketing' FROM DUAL
       )
match_recognize(
  partition by name
  order     by years_at_company
  measures  match_number() as job_no, a.role as role
  pattern   (a b*)
  define    b as role = a.role
);

NAME JOB_NO ROLE     
---- ------ ---------
Bob       1 Sales    
Bob       2 IT       
Bob       3 Sales    
Bob       4 Marketing

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-03-14
    • 1970-01-01
    • 2013-02-19
    • 2020-07-15
    • 2021-10-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多