【问题标题】:Return the first n records per group Oracle SQL [duplicate]返回每组Oracle SQL的前n条记录[重复]
【发布时间】:2016-09-19 20:41:00
【问题描述】:

我的问题

我想返回 Oracle 10g 中按日期排序的每个组的前 n 行

我的桌子

EMPLOYEE|START_DATE|DEPARTMENT
Amy     |01-02-1901|Sales
Edwina  |01-02-1902|Mergers
Tawnee  |01-02-1904|Legal
Trudy   |01-02-1998|Sales
Tanner  |01-02-1967|Sales
Kelly   |01-02-1954|Mergers
Jenny   |01-02-1991|Sales
Jacinta |01-02-1924|Legal
Suzanne |01-02-1976|Legal
Jacqui  |01-02-1989|Legal
Jill    |01-02-1989|Mergers
Kate    |01-02-1998|Mergers
Jane    |01-02-1900|Sales
Louise  |01-02-1912|Mergers
Kim     |01-02-1976|Sales
Cara    |01-02-1955|Sales
Kirsten |01-02-1933|Legal
Sarah   |01-02-1998|Legal

期望的结果

EMPLOYEE|START_DATE|DEPARTMENT
Jane    |01-02-1900|Sales
Amy     |01-02-1901|Sales
Tawnee  |01-02-1904|Legal
Jacinta |01-02-1924|Legal
Sarah   |01-02-1998|Legal
Edwina  |01-02-1902|Mergers
Louise  |01-02-1912|Mergers

我尝试过的

(select * from 
employees where 
DEPARTMENT = 'Sales' and
rownum <3;)
UNION
(select * from 
employees where 
DEPARTMENT = 'Legal' and
rownum <3;)
UNION
(select * from 
employees where 
DEPARTMENT = 'Mergers' and
rownum <3;)

真的很丑的查询

我在想是否有办法让你成为一个

OVER (PARTITION BY DEPARTMENT)

但是根据我的阅读,这需要在分析函数之前进行(计数,总和)。有没有更优雅、更便宜的解决方案?

【问题讨论】:

  • 你在正确的轨道上。看看 Nir ​​Levy 在上面的评论中指出的线程。看看您是否可以根据您的情况调整解决方案 - 这是正确的方法。如果您的数据中可能存在关联,则存在细微的变化 - 如果没有,您应该很高兴。如果那里的解决方案不起作用,请再次写信解释您发现困难的确切位置或与旧案例相比您的案例有什么不同。祝你好运!

标签: sql oracle syntax


【解决方案1】:

考虑这种使用计数相关聚合查询的非 Windows 函数方法。这个想法是运行一个部门等级子查询,然后在派生表中使用它,该表按该部门等级过滤外部查询。请注意,您想要的结果不会按顺序返回 START_DATE,而只是查询的行号。

SELECT main.EMPLOYEE, t.START_DATE, t.DEPARTMENT
FROM 
   (SELECT t.EMPLOYEE, t.START_DATE, t.DEPARTMENT, 
          (SELECT Count(*) FROM Employees sub 
           WHERE sub.START_DATE <= t.START_DATE 
           AND sub.Department = t.Department) AS DeptRank
FROM Employees t) main
WHERE main.DeptRank <= 3
ORDER BY main.DEPARTMENT, main.START_DATE;

-- EMPLOYEE START_DATE  DEPARTMENT
-- Tawnee   1/2/1904    Legal
-- Jacinta  1/2/1924    Legal
-- Kirsten  1/2/1933    Legal
-- Edwina   1/2/1902    Mergers
-- Louise   1/2/1912    Mergers
-- Kelly    1/2/1954    Mergers
-- Jane     1/2/1900    Sales
-- Amy      1/2/1901    Sales
-- Cara     1/2/1955    Sales

对于 Windows 函数对应项:

SELECT main.EMPLOYEE, t.START_DATE, t.DEPARTMENT
FROM 
   (SELECT t.EMPLOYEE, t.START_DATE, t.DEPARTMENT, 
           RANK() OVER (PARTITION BY Department
           ORDER BY START_DATE) AS DeptRank
FROM Employees t) main
WHERE main.DeptRank <= 3
ORDER BY main.DEPARTMENT, main.START_DATE;

作为@Matt cmets,您可能想要处理关系(即在同一天开始工作的员工)。上述两种解决方案都将根据排名过滤器输出所有此类员工。要在相关子查询中取得联系,请使用 Employee 名称作为 tiebreaker(或者如果可用,最好使用唯一 ID):

SELECT main.EMPLOYEE, t.START_DATE, t.DEPARTMENT
FROM 
   (SELECT t.EMPLOYEE, t.START_DATE, t.DEPARTMENT, 
           (SELECT Count(*) FROM Employees sub 
            WHERE sub.Department =  t.Department 
             AND (sub.START_DATE <= t.START_DATE
                  OR sub.START_DATE = t.START_DATE 
                  AND sub.EMPLOYEE < t.EMPLOYEE) AS DeptRank
FROM Employees t) main
WHERE main.DeptRank <= 3
ORDER BY main.DEPARTMENT, main.START_DATE;

对于窗口函数查询,使用ROW_NUMBER() 代替RANK()

【讨论】:

  • 因为 OP 明确表示 top n NOT top n WITH TIES 最好注意非窗口函数将包括 tie 并且窗口函数也可以,但 OP 可以更改 RANK()到 ROW_NUMBER() 以消除联系。
  • 很好,马特。我将这种情况扩展到您的信用!
猜你喜欢
  • 2017-05-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-15
相关资源
最近更新 更多