Как получить верхнее значение N для каждого Division_id в postgres - PullRequest
0 голосов
/ 28 марта 2019

У меня есть таблица 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;

Table WORKER sample data

Для приведенных выше примеров данных мой запрос должен вернуть следующие строки: Query result for sample data

Примечание: у подразделения 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;

1 Ответ

1 голос
/ 28 марта 2019

Просто используйте запрос с row_number

WITH c AS
(
   SELECT *,
         ROW_NUMBER() OVER (PARTITION BY division_id ORDER BY Salary DESC) AS rn
   FROM worker
)
SELECT *
  FROM c
WHERE rn <= 3

В зависимости от того, хотите ли вы, чтобы равные зарплаты получали одинаковое звание и получить последовательный ранг или иным образом, вы можете использовать RANK или DENSE_RANK вместо ROW_NUMBER

...