2015-09-22 4 views
0

У меня есть таблица в базе данных с записями, как показано ниже:, как получить максимум две записи для каждого сотрудника

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, сложно составить запрос для следующего требования. Любой один помочь мне

+0

'ROW_NUMBER() OVER (PARTITION by EmpName ORDER BY LeaveDate DESC)', принять результат <= 2 – Eric

+2

Какая СУБД вы используете? Postgres? Oracle? –

+1

Просто быстро наблюдайте, пожалуйста, НЕ используйте EmpName .... это заставляет меня плакать .... используйте что-то вроде EmpID ...... – Dryadwoods

ответ

1

Вот запрос 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 
1

использование ниже запроса, который применяется для 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 
-4

Поскольку данные вы предоставили, как выглядит фиктивных данных я перестройте базу данных, чтобы иметь одну с первичными данными, которая включает в себя такие вещи, как: идентификатор сотрудника, имя сотрудника, фамилия сотрудника, пароль для входа, ...

Другая таблица будет идти на такие вещи, как: идентификатор сотрудника, STARTDATE, leavedate

Эти две таблицы будут присоединять друг к другу на идентификатор сотрудника

Вы собираетесь иметь два, пока петли, один внутри другого : Первый оператор SQL будет выглядеть примерно так:

SELECT * from TBL1 

от этого вы будете установить имя переменной для запроса таблицы 2

SQL, во второй время будет вот хорошо выглядеть следующим образом:

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

преимуществ этого решения является то, если вы делаете миллионы записей Он будет иметь данные сотрудника хранится отдельно от самого начала, и оставить время, чтобы сэкономить много места для хранения.

+0

, ваш запрос неверен. Плакат 'хочет отображать последние две записи всех сотрудников' – Imran

+0

Прочитайте весь ответ, а не только SQL. Я объяснил, что вы некоторое время работаете некоторое время. Как это спам, он отвечает на вопрос как можно чаще, потому что они не предоставляют такие вещи, как: имена таблиц и какой язык на сервере, с которым они работают в приложении? Webfarm.io - сайт разработки php, а также сайт дизайна. Все 3 правильные по-своему. –

+0

yes imran Я хочу отображать последние две записи каждого сотрудника – Creditto

2

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-запрос будет работать как mysql и sql-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; 
+0

Хорошее решение с подзапросом 'count()', хотя я думаю, что это немного дорого. Внутренний запрос относится к внешнему, поэтому внутренний запрос выполняется для каждой строки внешнего запроса (то есть для каждой строки в таблице). +1 в противном случае. – gaborsch

0

попробовать этот запрос в SQL сервере

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) 
0

На SQL Server Row_Number() function with Partition By пункт является лучшим вариантом для таких запросов

Здесь ВЫБРАТЬ образец

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 
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.

+1

Я думаю, что это должно быть «LEFT JOIN», чтобы также получить сотрудников только с одной записью. (Тогда 'WHERE'clause должен был бы перейти к разделу« ON », конечно, где бы я его поместил). Кроме того, это похоже на возможное решение, которое обходит ограничения, налагаемые JPA. –

+0

@ThorstenKettner 'LEFT JOIN' в данном случае не имеет значения, потому что это техническая совместимость (та же таблица с обеих сторон). Итак, если на левой стороне есть запись, на правой стороне тоже будет запись. 'WHERE' vs' ON': это вопрос вкуса, оба решения работают. – gaborsch

+1

Если для empname 'ABC' существует только одна запись, вы получаете эту запись один раз в e1 и один раз в e2. Вы присоединяетесь к ним и удаляете пару в WHERE, потому что условие 'e1.LeaveDate> e2.LeaveDate' не выполняется. Это оставляет вас без соответствия для empname 'ABC', поэтому «ABC» не находится в результатах. При внешнем соединении вы выбрали запись в e1, но не нашли бы запись с тем же именем и более старой датой для e2, поэтому нулевой регистр для e2 был бы объединен, и вы сохранили бы свою запись e1 и эту запись для «ABC» будет в результатах. –