У меня есть таблица worker
со столбцами id, name, salary, division_id
, и мне нужно отобразить работников с тремя верхними зарплатами в каждом подразделении.Вот пример данных для работника таблицы:
insert into worker values (1, 'Joe', 70000, 1);
insert into worker values (2, 'Henry', 80000, 2);
insert into worker values (3, 'Sam', 60000, 2);
insert into worker values (4, 'Max', 90000, 1);
insert into worker values (5, 'Janet', 69000, 1);
insert into worker values (6, 'Randy', 85000, 1);
insert into worker values (7, 'Jordan', 70000, 4);
insert into worker values (8, 'Adam', 69000, 3);
insert into worker values (9, 'David', 76000, 4);
insert into worker values (10, 'Moses', 68000, 4);
insert into worker values (11, 'Solomon', 55000, 4);
insert into worker values (12, 'Cloe', 38000, 3);
insert into worker values (13, 'Sarah', 88000, 3);
insert into worker values (14, 'Deb', 92000, 3);
insert into worker values (15, 'Lea', 98000, 4);
commit;
Для приведенных выше примеров данных мой запрос должен вернуть следующие строки:
Примечание: у подразделения 2 есть только 2 строки, поскольку в выборочных данных у него только 2 строки.
Запрос должен быть в Oracle или PostgreSQL .
Я знаю, что могу ограничить количество строк с помощью ключевого слова Oracle ROWNUM
или Postgres LIMIT
, но здесь это не помогает, так как мне нужно получить 3 строки или меньше длякаждый division_id
.
Мой запрос должен быть максимально эффективным, поскольку число строк в workers
может быть огромным (я не знаю точного количества строк).
В моемРешение, я использую CURSOR и FOR my_rec IN my_cursor
для сканирования набора результатов и печати только первые 3 строки или меньше для каждого division_id
.Это решение дает мне сложность O (N) , и я надеюсь найти лучшее решение, возможно, решить его одним запросом без использования CURSOR
.
DECLARE
CREATE CURSOR my_cursor IS
SELECT division_id, name AS worker, salary
FROM worker
ORDER BY division_id, salary desc;
division_id NUMBER;
row_count_per_id NUMBER;
BEGIN
FOR my_rec IN my_cursor LOOP
-- If first iteration then initialize variables
IF (row_num = 0) THEN
division_id := myRec.division_id;
row_count_per_id := 1;
END IF;
-- row_num can't be 0 at this point
IF (division_id = myRec.division_id) THEN
IF (row_count_per_id < 3) THEN
-- Print first record of the new division_id
DBMS_OUTPUT.PUT_LINE('division_id = ' || myRec.division_id ||
', Worker = ' myRec.worker ||
', salary = ' myRec.salary;
row_count_per_id := row_count_per_id + 1;
END IF;
ELSE
-- division_id has changed from the previous row
-- Add the first row of new division_id
division_id = myRec.division_id;
row_count_per_id := 1;
DBMS_OUTPUT.PUT_LINE('division_id = ' || myRec.division_id ||
', Worker = ' myRec.worker ||
', salary = ' myRec.salary;
END IF;
END LOOP;
END;