【问题标题】:how to get maximum of two records for each employee如何为每个员工获取最多两条记录
【发布时间】:2015-12-18 23:43:56
【问题描述】:

我在数据库中有一张表,记录如下:

Id | EmpName  | LeaveDate               | createdDate

1  | Govind   | 2014-04-02 00:00:00.000 | 2014-04-02
2  | Aravind  | 2014-04-03 00:00:00.000 | 2014-04-05
3  | Govind   | 2014-04-04 00:00:00.000 | 2014-04-10
4  | Amar     | 2014-04-05 00:00:00.000 | 2014-04-11
6  | Aravind  | 2014-04-06 00:00:00.000 | 2014-04-16
7  | Govind   | 2014-04-07 00:00:00.000 | 2014-04-16
8  | Aravind  | 2014-04-08 00:00:00.000 | 2014-04-16
9  | Amar     | 2014-04-09 00:00:00.000 | 2014-04-16
10 | Aravind  | 2014-04-10 00:00:00.000 | 2014-04-16
11 | Govind   | 2014-04-11 00:00:00.000 | 2014-04-16
12 | Aravind  | 2014-04-12 00:00:00.000 | 2014-04-16
13 | Amar     | 2014-04-13 00:00:00.000 | 2014-04-16
14 | Aravind  | 2014-04-14 00:00:00.000 | 2014-04-16

现在,我想显示所有员工的最后两条记录

示例输出:

Id | EmpName  | LeaveDate                | createdDate


11 | Govind   | 2014-04-11 00:00:00.000 | 2014-04-16
7  | Govind   | 2014-04-07 00:00:00.000 | 2014-04-16
14 | Aravind  | 2014-04-14 00:00:00.000 | 2014-04-16
12 | Aravind  | 2014-04-12 00:00:00.000 | 2014-04-16
13 | Amar     | 2014-04-13 00:00:00.000 | 2014-04-16
9  | Amar     | 2014-04-09 00:00:00.000 | 2014-04-16

我正在使用 JPA,很难为以下要求编写查询。谁来帮帮我

【问题讨论】:

  • ROW_NUMBER() OVER (PARTITION BY EmpName ORDER BY LeaveDate DESC),取结果
  • 您使用的是哪个 DBMS?后格雷斯?甲骨文?
  • 只是快速观察,请不要使用 EmpName.... 这让我哭了.... 使用 EmpID 之类的东西......
  • 我正在使用 mysql、sqlserver、oracle,我已经实现了这 3 个 DBMS
  • 决定您希望这个问题与哪个 RDBMS 相关

标签: mysql sql sql-server oracle top-n


【解决方案1】:

由于您提供的数据看起来像虚拟数据,因此我将重新设计数据库以拥有一个包含主要数据的数据库,其中包括以下内容: 员工ID、员工名字、员工姓氏、登录密码……

另一张桌子会像这样: 员工id、开始日期、离开日期

这两个表将在员工 ID 上相互关联

您将有两个 while 循环,一个在另一个内部: 第一个 sql 语句类似于:

SELECT * from TBL1

您将为表 2 的查询设置一个变量名

第二个的 SQL 看起来类似于:

SELECT * FROM tbl where employee_id (from table 1)= employee_id (from table 1) LIMIT 2

此解决方案的优点是,如果您正在处理数百万条记录,它将从一开始就将员工数据分开保存,并留出时间以节省大量存储空间。

【讨论】:

  • 您的查询不正确。海报want display the last two records of the all employees
  • 阅读整个答案,而不仅仅是 SQL。我解释说你在一段时间内跑了一段时间。它是如何产生垃圾邮件的,它尽可能笼统地回答问题,因为它们不提供以下内容:表名,以及它们在应用程序中使用的服务器语言? Webfarm.io 是一个 php 开发站点,也是一个设计站点。所有 3 都以他们自己的方式是正确的。
  • 是的,我想显示每个员工的最后两条记录
  • “Webfarm.io 是一个 php 开发站点,也是一个设计站点。” 这与什么有什么关系?
  • @Cerbrus:对此答案有评论称此答案为垃圾邮件(当然,这也与答案无关)。他们正在回应该评论,该评论已被删除。 (这就是为什么评论标志处理如此混乱 - 它们永远不会被正确清理。)
【解决方案2】:

在 JPA (JPQL) 中,无法以通用且有效的方式解决此问题。 Rownum 概念在数据库中是如此不同,以至于在 JPQL 中无法使用不同的方言对其进行建模。

Here is one answer 为整个结果集提供了解决方法,但不适用于子查询。 This answer 的故事大致相同,但他们没有提出解决方案。

这是一个 JPQL 解决方案,它在大型表上可能效率低下(除非您正确索引您的表):

SELECT e1.EmpName, e1.LeaveDate, e1.createdDate
FROM EmployeeLeaves e1
JOIN EmployeeLeaves e2 on (e1.EmpName = e2.EmpName)
WHERE e1.LeaveDate >= e2.LeaveDate
GROUP BY e1.EmpName, e1.LeaveDate, e1.createdDate
HAVING count(*) <= 2

另一个(低效)可能的解决方案是逐个查询每个员工并用 Java 处理结果。

【讨论】:

  • 我认为应该是LEFT JOIN,以便让员工也只有一条记录。 (然后WHEREclause 当然必须移到ON 子句,无论如何我都会把它放在哪里。)除此之外,这似乎是一个可行的解决方案,可以绕过 JPA 造成的限制。
  • @ThorstenKettner LEFT JOIN 在这种情况下无关紧要,因为从技术上讲,它是一个自联接(两边同一张表)。所以,如果左边有记录,右边也会有记录。 WHERE vs ON 子句:这是风味问题,两种解决方案都有效。
  • 如果 empname 'ABC' 只存在一条记录,那么您在 e1 和 e2 中分别获得该记录。您加入这些并在 WHERE 中删除这对,因为不满足条件 e1.LeaveDate &gt; e2.LeaveDate。这使您没有任何匹配 empname 'ABC',因此 'ABC' 不在结果中。使用外部连接,您将选择 e1 中的记录,但找不到 e2 的相同名称和较早日期的记录,因此将连接 e2 的空记录,因此您将保留您的 e1 记录和这条记录'ABC' 将出现在结果中。
  • @ThorstenKettner 你是对的,但同样适用于 2 条记录的情况。解决方案是允许两个比较相等。修复了查询。
【解决方案3】:

对于sql-server

使用CTE

;with cte as
(
    select rn = row_number() over
    (
      partition by EmpName
      order by LeaveDate desc
    ),*
from employees
)
select * from cte
where rn <= 2;

SQL Fiddle


对于mysql

通过添加行号。

select t2.Id,
t2.EmpName,
t2.LeaveDate,
t2.createdDate
from
(
   select Id,
   EmpName,
   LeaveDate,
   createdDate,
   ( 
       case EmpName 
       when @curA
       then @curRow := @curRow + 1 
       else @curRow := 1 and @curA := EmpName end
    ) + 1 as rn
    from employees t,
    (select @curRow := 0, @curA := '') r
    order by EmpName,LeaveDate desc
)t2
where t2.rn<3;

SQL Fiddle


下面的 sql 查询适用于 mysqlsql-server

查询

select *
from employees t1
where 
(
  select count(*) from employees t2
  where t2.EmpName = t1.EmpName
  and t2.LeaveDate > t1.LeaveDate
) <= 1
order by t1.EmpName;

【讨论】:

  • count() 子查询的好解决方案,虽然我认为这有点贵。内查询是指外查询,所以内查询对外查询的每一行(即表中的每一行)执行。否则 +1。
【解决方案4】:

在 SQL Server 上,Row_Number() function with Partition By 子句是此类查询的最佳选择

这是一个示例 SELECT

with cte as (
    select
    rn = ROW_NUMBER() over (partition by EmpName order by LeaveDate desc),*
    from EmployeeLeaves
)
select
    Id,
    EmpName,
    LeaveDate,
    createdDate
from cte
where rn <= 2

【讨论】:

    【解决方案5】:

    在 SQL Server 中尝试此查询

    WITH tmp
    AS
    (
        SELECT id
            ,Empname
            ,Leavedate
            ,CreatedDate
            ,DENSE_RANK() OVER (PARTITION BY Empname ORDER BY Leavedate) AS [rank_leavedate]
        FROM tbl
    )
    
    SELECT *
    FROM tmp
    WHERE tmp.rank_leavedate IN (1,2)
    

    【讨论】:

      【解决方案6】:

      这是一个 SQL Server 查询:

      SELECT t.Id, t.EmpName, t.LeaveDate, t.createdDate
      FROM
      (
          SELECT Id, EmpName, LeaveDate, createdDate, RANK() OVER
              (PARTITION BY LeaveDate ORDER BY LeaveDate) num
          FROM Table
      ) t
      WHERE t.num <= 2
      

      【讨论】:

        【解决方案7】:

        使用以下适用于 Sql Server 的查询

        ;WITH CTE AS 
        ( SELECT Id , EmpName  , LeaveDate , createdDate, ROW_NUMBER() OVER (PARTITION BY EmpName   ORDER BY ID DESC) RECORD_NUMBER FROM TABLENAME
        ) SELECT Id , EmpName  , LeaveDate , createdDate FROM CTE WHERE RECORD_NUMBER <=2
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-06-02
          • 2017-09-18
          • 1970-01-01
          • 2016-11-26
          • 2020-10-01
          相关资源
          最近更新 更多