使用 CTE 和 Window 函数获取预算内的招聘人数

Use CTE and Window Function to get the number of hiring under a budget

我有一个 table candidates,其中有三列 idpositionsalary

生成 table 的代码是

create table candidates (
id int primary key,
position varchar not null,
salary int not null
);
insert into candidates values (1, 'junior', 10500);
insert into candidates values (2, 'senior', 15000);
insert into candidates values (3, 'senior', 35000);
insert into candidates values (4, 'junior', 8000);
insert into candidates values (5, 'senior', 30000);
insert into candidates values (6, 'senior', 25000);
insert into candidates values (7, 'junior', 30000);
insert into candidates values (8, 'senior', 50000);
insert into candidates values (9, 'senior', 30000);
insert into candidates values (10, 'junior', 7000);
insert into candidates values (11, 'junior', 8000);
insert into candidates values (12, 'senior', 33000);
insert into candidates values (13, 'junior', 5000);
insert into candidates values (14, 'senior', 47000);
insert into candidates values (15, 'junior', 12000);

我的预算是150000,我需要尽可能多地聘请低成本的前辈,剩下的钱,我可以尽可能多地聘请后辈。我写这段代码是为了获取高年级和低年级的钱的累计金额。

SELECT id, position, salary, SUM(salary) OVER (PARTITION BY position ORDER BY salary) AS cum_salary
FROM candidates
ORDER BY position DESC, salary;

我得到的输出是

id position salary cum_salary
2 senior 15000 15000
6 senior 25000 40000
5 senior 30000 100000
9 senior 30000 100000
12 senior 33000 133000
3 senior 35000 168000
14 senior 47000 215000
8 senior 50000 265000
13 junior 5000 5000
10 junior 7000 12000
11 junior 8000 28000
4 junior 8000 28000
1 junior 10500 38500
15 junior 12000 50500
7 junior 30000 80500

我可以看到我可以用133000(不到150000)雇用5个前辈,用剩下的钱(150000 - 133000 = 17000)雇用两个后辈。所以,最终的输出应该是这样的

senior 5
junior 2

我如何在 POSTGRESQL 9.6 中使用 CTE 和 Window 函数编写此查询以获得我需要的大型数据集的输出类型,而手动操作并不总是可行?

PS: 我不是 Postgres 9.6 的专家用户。

这样就可以了:

WITH cte AS (
   SELECT position, salary
        , sum(salary)  OVER w AS cum_salary
        , row_number() OVER w AS count
   FROM   candidates
   WINDOW w AS (PARTITION BY position ORDER BY salary)
   )
, sen AS (
   SELECT position, cum_salary, count
   FROM   cte
   WHERE  position = 'senior'
   AND    cum_salary <= 150000
   ORDER  BY cum_salary DESC
   LIMIT  1
   )
SELECT position, count FROM sen
UNION ALL
(  -- parentheses required
SELECT position, count
FROM   cte
WHERE  position = 'junior'
AND    cum_salary <= (SELECT 150000 - cum_salary FROM sen)
ORDER  BY cum_salary DESC
LIMIT  1
);

db<>fiddle here

只要您可以硬编码 CTE 的数量,就会如下所示。 PS: 你开局不错。也可能有一种方法可以优化下面的代码。祝你好运 ;)

with seniors as (
  SELECT
        id
      , position
      , salary
      , SUM(salary) OVER (PARTITION BY position ORDER BY salary) AS cum_salary
  FROM candidates
  where position = 'senior'
)
,juniors as (
  SELECT
        id
      , position
      , salary
      , SUM(salary) OVER (PARTITION BY position ORDER BY salary) AS cum_salary
  FROM candidates
  where position = 'junior'
)
,seniors_canhire as (
  select
        id
      , position
      , salary
  from seniors
  where cum_salary <= 150000
)
,juniors_canhire as (
  SELECT
        id
      , position
      , salary
  FROM juniors
  where cum_salary <= (select 150000-sum(salary) from seniors_canhire)
)
select *
from seniors_canhire
union all
select *
from juniors_canhire