Sql 如何获取max列的行和第二高的值作为最后一列?

Sql 如何获取max列的行和第二高的值作为最后一列?,sql,postgresql,Sql,Postgresql,我有一个数据集,其中每一行都是薪资变动,我试图获取最高的最后一个薪资,包括所有详细信息,以及薪资变动的原因和最后一次变动的日期,以及我想要之前薪资值的最后一列。我怎样才能得到它 我已经用MIN处理了上一份工资和第一份工资,但我想要的是之前的工资,而不是第一份。我得到的结果是: company_id name last_salary_change reason updated_at salary_before company_id name last_salary_c

我有一个数据集,其中每一行都是薪资变动,我试图获取最高的最后一个薪资,包括所有详细信息,以及薪资变动的原因和最后一次变动的日期,以及我想要之前薪资值的最后一列。我怎样才能得到它

我已经用MIN处理了上一份工资和第一份工资,但我想要的是之前的工资,而不是第一份。我得到的结果是:

company_id   name   last_salary_change   reason   updated_at   salary_before
company_id   name   last_salary_change   reason   updated_at   salary_before
我使用的查询:

SELECT p.company_id,
       u.name AS name,
       MAX(psc.amount/100) AS last_salary,
       MAX(psc.reason) AS reason,
       MAX(psc.updated_at) AS updated,
       MIN(psc.amount/100) AS first_salary
FROM lukla.profiles AS p

INNER JOIN lukla.profile_salary_changes AS psc
  ON p.id = psc.profile_id
INNER JOIN lukla.users AS u
  ON p.id = u.profile_id

WHERE p.company_id = 29 (filtered by a specific company)

GROUP BY 1, 2
我要找的是:


看来你需要第二高的薪水。你可以试试下面的查询-

SELECT p.company_id,
       u.name AS name,
       MAX(psc.amount/100) AS last_salary,
       MAX(psc.reason) AS reason,
       MAX(psc.updated_at) AS updated,
       (SELECT MAX(psc.amount/100)
        FROM lukla.profile_salary_changes psc2
        WHERE psc2.profile_id = profile_salary_changes.profile_id
        AND (psc.amount/100) < (SELECT MAX(psc.amount/100)
                                FROM lukla.profile_salary_changes psc2
                                WHERE psc2.profile_id = profile_salary_changes.profile_id)) salary_before
FROM lukla.profiles AS p
INNER JOIN lukla.profile_salary_changes AS psc
      ON p.id = psc.profile_id
INNER JOIN lukla.users AS u
ON p.id = u.profile_id
WHERE p.company_id = 29 (filtered by a specific company)
GROUP BY 1, 2

虽然我没有测试它,但它应该可以工作。

您可以将窗口函数与条件聚合一起使用:

SELECT p.company_id,
       u.name AS name,
       MAX(psc.amount/100) AS last_salary,
       MAX(psc.reason) AS reason,
       MAX(psc.updated_at) AS updated,
       MIN(psc.amount/100) AS first_salary,
       MAX(amount / 100) FILTER (WHERE seqnum = 2) as prev_salary
FROM lukla.profiles p INNER JOIN
     (SELECT ps.*,
             ROW_NUMBER() OVER (PARTITION BY p.company_id, u.name ORDER BY psc.updated_at) as seqnum
      FROM lukla.profile_salary_changes psc
     ) psc
     ON p.id = psc.profile_id INNER JOIN
     lukla.users u
     ON p.id = u.profile_id
WHERE p.company_id = 29 (filtered by a specific company)
GROUP BY 1, 2;
两点意见:

你是在假设第一份工资是最低的。这并不总是正确的。 这假设salarychange表实际上只有salarychanges,而其他联接没有引入新行。
答案是基于@Gordon的答案,唯一的区别是我添加了

      JOIN lukla.profiles p
      ON p.id = psc.profile_id 
      JOIN lukla.users u
      ON p.id = u.profile_id
在第一个连接中

SELECT p.company_id,
       u.name AS name,
       MAX(psc.amount/100) AS last_salary,
       MAX(psc.reason) AS reason,
       MAX(psc.updated_at) AS updated,
       MIN(psc.amount/100) AS first_salary,
       MAX(amount / 100) FILTER (WHERE seqnum = 2) as prev_salary
FROM lukla.profiles p 

JOIN
    (SELECT psc.*,
             ROW_NUMBER() OVER (PARTITION BY p.company_id, u.name ORDER BY psc.updated_at) as seqnum
      FROM lukla.profile_salary_changes psc
      JOIN lukla.profiles p
      ON p.id = psc.profile_id 
      JOIN lukla.users u
      ON p.id = u.profile_id

     ) psc
  ON p.id = psc.profile_id 
JOIN lukla.users u
  ON p.id = u.profile_id

WHERE p.company_id = 29
GROUP BY 1, 2;

我想,整个概念存在重大问题。它们源自这样的假设:第一份工资总是最低的,最后一份是最高的,工资总是向上的。这些都不是真的。假设MAX可以用来获取最新的数据,这也导致了一种结构,这种结构充其量也是罕见的。该科:

MAX(psc.amount/100) AS last_salary,
MAX(psc.reason) AS reason,
MAX(psc.updated_at) AS updated,
即使承认MAX psc.amount会得到正确的值,我们也可以假设日期选择正确。然而,这并不适用于任何原因,必须始终注意使文本与另一列的组合达到最大值。不要求max text对应于max amount

这是一个有趣的问题,因此下面将解决这些问题。接受它或忽略它,因为你认为合适。不管怎样,我都觉得很有趣

--- create "tables" as cte
with profiles (id, company_id) as
     ( values (61,29)
            , (62,29)
            , (63,29)
            , (64,29)
     )
   , profile_salary_changes (profile_id, amount, reason, updated_at) as
     ( values (64, 640000, 'Initial Hire', '2018-02-15'::date)
            , (64, 611200, 'Salary cut 4.5% across the board Co wide.', '2018-07-05'::date)
            , (64, 625600, '50% July''s cut recovery', '2018-12-09'::date)
            , (64, 710000, 'Promotion', '2019-02-09'::date)            
            , (63, 630000, 'Initial  ', '2019-02-15'::date)
            , (63, 600000, 'Transfer.', '2019-07-05'::date)
            , (63, 627500, 'COL', '2019-12-09'::date)
            , (61, 100000, 'Initial Only', '2019-05-09'::date)  
            , (62, 620000, 'First', '2019-03-09'::date)  
            , (62, 625000, 'Bonus', '2019-08-09'::date)        
     )
   , users (profile_id, name) as
     ( values (61, 'Test1')
            , (62, 'Test2')
            , (63, 'Test3')
            , (64, 'Test4')
     )
-- final selection pick up designated columns 
select company_id
     , name 
     , reason
     , trunc(amount/100.0,2)        last_salary
     , updated_at  
     , trunc(prev_sal/100.0,2)      prev_salary
     , trunc(first_salary/100.0,2)  first_salary
  from ( -- pick up the appropriate previous and first salary
         select x.*
              , lag (amount)                  over( partition by company_id, profile_id order by updated_at ) prev_sal
              , lag (amount, (rn-1)::integer) over( partition by company_id, profile_id order by updated_at ) first_salary      
           from (
                 -- gather all columns, number each row for company and profile, and get number of total rows for each set 
                 select p.company_id
                      , u.name
                      , psc.*  
                      , row_number() over( partition by p.company_id, psc.profile_id order by updated_at) rn
                      , count(*)     over( partition by p.company_id, psc.profile_id) r
                   from profile_salary_changes psc
                   join profiles               p on (p.id = psc.profile_id)
                   join users                  u on (p.id = u.profile_id)
                ) x
         ) z
 -- select only the last row in each set. Additional salary values have been attached 
 where r = rn 
 order by name; 

假设您的数据库结构如下所示:

您可以运行此查询:

SELECT p.company_id,
    p.id,
    last_salary.amount/100 AS last_salary,
    last_salary.reason AS reason,
    last_salary.updated_at AS updated,
    prev_salary.updated_at as salary_before_updated_at,
    prev_salary.amount/100 AS salary_before
FROM profiles AS p
LEFT JOIN profile_salary_changes AS last_salary
    ON p.id = last_salary.profile_id 
        AND NOT EXISTS (
            SELECT * FROM profile_salary_changes psc2 
                WHERE psc2.profile_id = last_salary.profile_id 
                    AND psc2.updated_at > last_salary.updated_at)
LEFT JOIN profile_salary_changes AS prev_salary
    ON p.id = prev_salary.profile_id 
        AND prev_salary.updated_at != last_salary.updated_at 
        AND NOT EXISTS (
            SELECT * FROM profile_salary_changes psc3 
                WHERE psc3.profile_id = prev_salary.profile_id 
                    AND psc3.updated_at < last_salary.updated_at 
                    AND psc3.updated_at > prev_salary.updated_at)
WHERE p.company_id = 29;

如果您的个人资料\u薪资\u变动表中有一个像id这样的主字段,最好在比较中使用它,而不是更新\u at

Hi,Ankit!谢谢,但它返回了一个错误:错误:子查询使用未分组的列psc.profile\u id来自外部查询请立即重试。错误:表profile\u salary\u changes的子句条目中缺少Hi,gordon!谢谢,但它返回了一个错误:错误:列psc.amount必须出现在GROUP BY子句中,或用于关于第一个点的聚合函数中,第一个工资几乎总是最低的,但我想要的不是最低的,而是最后一个之前的工资。。因此,如果员工获得了两次晋升,例如,我想要的是最后一次工资和第二次工资,而不是最后一次first@EduardoKaneko . . . 我本该凭直觉行事的。这是一个聚合查询,所以是条件聚合。是选择psc.*而不是选择ps.*?如果不是,则返回:`ERROR:missing FROM子句条目(表ps)`如果是,则返回错误:ERROR:对表p FROM子句条目的引用无效提示:表p有一个条目,但不能从查询的这一部分引用它。非常感谢你,戈登!我以你的答案为基础,找到了真正的答案!差别很小,再次感谢:D
--- create "tables" as cte
with profiles (id, company_id) as
     ( values (61,29)
            , (62,29)
            , (63,29)
            , (64,29)
     )
   , profile_salary_changes (profile_id, amount, reason, updated_at) as
     ( values (64, 640000, 'Initial Hire', '2018-02-15'::date)
            , (64, 611200, 'Salary cut 4.5% across the board Co wide.', '2018-07-05'::date)
            , (64, 625600, '50% July''s cut recovery', '2018-12-09'::date)
            , (64, 710000, 'Promotion', '2019-02-09'::date)            
            , (63, 630000, 'Initial  ', '2019-02-15'::date)
            , (63, 600000, 'Transfer.', '2019-07-05'::date)
            , (63, 627500, 'COL', '2019-12-09'::date)
            , (61, 100000, 'Initial Only', '2019-05-09'::date)  
            , (62, 620000, 'First', '2019-03-09'::date)  
            , (62, 625000, 'Bonus', '2019-08-09'::date)        
     )
   , users (profile_id, name) as
     ( values (61, 'Test1')
            , (62, 'Test2')
            , (63, 'Test3')
            , (64, 'Test4')
     )
-- final selection pick up designated columns 
select company_id
     , name 
     , reason
     , trunc(amount/100.0,2)        last_salary
     , updated_at  
     , trunc(prev_sal/100.0,2)      prev_salary
     , trunc(first_salary/100.0,2)  first_salary
  from ( -- pick up the appropriate previous and first salary
         select x.*
              , lag (amount)                  over( partition by company_id, profile_id order by updated_at ) prev_sal
              , lag (amount, (rn-1)::integer) over( partition by company_id, profile_id order by updated_at ) first_salary      
           from (
                 -- gather all columns, number each row for company and profile, and get number of total rows for each set 
                 select p.company_id
                      , u.name
                      , psc.*  
                      , row_number() over( partition by p.company_id, psc.profile_id order by updated_at) rn
                      , count(*)     over( partition by p.company_id, psc.profile_id) r
                   from profile_salary_changes psc
                   join profiles               p on (p.id = psc.profile_id)
                   join users                  u on (p.id = u.profile_id)
                ) x
         ) z
 -- select only the last row in each set. Additional salary values have been attached 
 where r = rn 
 order by name; 
SELECT p.company_id,
    p.id,
    last_salary.amount/100 AS last_salary,
    last_salary.reason AS reason,
    last_salary.updated_at AS updated,
    prev_salary.updated_at as salary_before_updated_at,
    prev_salary.amount/100 AS salary_before
FROM profiles AS p
LEFT JOIN profile_salary_changes AS last_salary
    ON p.id = last_salary.profile_id 
        AND NOT EXISTS (
            SELECT * FROM profile_salary_changes psc2 
                WHERE psc2.profile_id = last_salary.profile_id 
                    AND psc2.updated_at > last_salary.updated_at)
LEFT JOIN profile_salary_changes AS prev_salary
    ON p.id = prev_salary.profile_id 
        AND prev_salary.updated_at != last_salary.updated_at 
        AND NOT EXISTS (
            SELECT * FROM profile_salary_changes psc3 
                WHERE psc3.profile_id = prev_salary.profile_id 
                    AND psc3.updated_at < last_salary.updated_at 
                    AND psc3.updated_at > prev_salary.updated_at)
WHERE p.company_id = 29;