【问题标题】:SQL Join only the last historical rowSQL 只加入最后一个历史行
【发布时间】:2015-05-12 10:28:25
【问题描述】:

我正面临以下问题:我正在编写一个视图,其中我将多个表连接到一个人员表。我现在尝试加入合作伙伴表,但我只需要历史上最后一个有效的合作伙伴行:

合作伙伴表:

id,
name,
married_at,
divorced_at,
died_at,
someone_id

如您所见,它与您已婚/已婚的伴侣有关。一个人一次只能有一个合伙人,但历史上可以有几个合伙人。所以某人的最后一个伙伴(someone_id)可能是:

  • 还活着,还结婚了
  • 在世但离婚
  • 死了“但仍然结婚”(所以有人是鳏夫)

我只需要找到某人的最后一个合作伙伴行。

到目前为止我得到了什么:

select *
from someone_table s
left join partners p on (p.someone_id = s.id and (p.divorced_at is null and p.died_at is null) )

但这 - 很明显 - 只给我那些还活着并且仍然结婚的伴侣。当然,这些伙伴是某人的最后一个伙伴,但最后一个伙伴离婚或死亡的所有其他“某人”不会出现在声明的结果中。我怎样才能得到其他的,每个人只有一排?

我还尝试了一个选择语句作为表格并使用 rownum

select *
from someone s,
(select * from partners p where p.someone_id = s.id and ROWNUM = 1 order by p.married_at)

但是这个语句总是失败并出现“invalied identifier s.id”错误

注意:表结构是固定的,不能更改。 DBMS 是 oracle。

提前致谢

编辑: 样本数据

partners_table

╔════╦═════════╦════════════╦═════════════╦════════════╦════════════╗
║ id ║  name   ║ married_at ║ divorced_at ║  died_at   ║ someone_id ║
╠════╬═════════╬════════════╬═════════════╬════════════╬════════════╣
║  1 ║ partner ║ 01.01.2000 ║             ║            ║         12 ║
║  2 ║ honey1  ║ 15.01.2000 ║ 15.01.2001  ║            ║         15 ║
║  3 ║ honey2  ║ 16.02.2001 ║             ║            ║         15 ║
║  4 ║ beauty  ║ 23.03.2005 ║             ║ 25.03.2005 ║         16 ║
║  5 ║ lady1   ║ 11.11.2000 ║ 11.12.2000  ║            ║         20 ║
║  6 ║ lady2   ║ 12.12.2000 ║ 01.01.2001  ║            ║         20 ║
║  7 ║ lady3   ║ 02.02.2001 ║             ║ 04.02.2004 ║         20 ║
║  8 ║ lady4   ║ 05.05.2005 ║             ║            ║         20 ║
║  9 ║ mate    ║ 23.06.2003 ║ 12.12.2009  ║            ║         25 ║
╚════╩═════════╩════════════╩═════════════╩════════════╩════════════╝

最后的历史行是:

╔════╦═════════╦════════════╦═════════════╦════════════╦════════════╗
║ id ║  name   ║ married_at ║ divorced_at ║  died_at   ║ someone_id ║
╠════╬═════════╬════════════╬═════════════╬════════════╬════════════╣
║  1 ║ partner ║ 01.01.2000 ║             ║            ║         12 ║
║  3 ║ honey2  ║ 16.02.2001 ║             ║            ║         15 ║
║  4 ║ beauty  ║ 23.03.2005 ║             ║ 25.03.2005 ║         16 ║
║  8 ║ lady4   ║ 05.05.2005 ║             ║            ║         20 ║
║  9 ║ mate    ║ 23.06.2003 ║ 12.12.2009  ║            ║         25 ║
╚════╩═════════╩════════════╩═════════════╩════════════╩════════════╝

【问题讨论】:

  • 您能否用一些示例数据以及预期的输出来更新您的问题,好吗?还有,一个人的最后一个伴侣就不能死去离婚吗?
  • 我添加了一些示例数据。如果有更多问题请评论

标签: sql oracle greatest-n-per-group


【解决方案1】:

这应该做你想做的:

with partners (id, name, married_at, divorced_at, died_at, someone_id) as (select 1, 'partner', to_date('01/01/2000', 'dd/mm/yyyy'), null, null, 12 from dual union all
                                                                           select 2, 'honey1', to_date('15/01/2000', 'dd/mm/yyyy'), to_date('15/01/2001', 'dd/mm/yyyy'), null, 15 from dual union all
                                                                           select 3, 'honey2', to_date('16/02/2001', 'dd/mm/yyyy'), null, null, 15 from dual union all
                                                                           select 4, 'beauty', to_date('23/03/2005', 'dd/mm/yyyy'), null, to_date('25/03/2005', 'dd/mm/yyyy'), 16 from dual union all
                                                                           select 5, 'lady1', to_date('11/11/2000', 'dd/mm/yyyy'), to_date('11/12/2000', 'dd/mm/yyyy'), null, 20 from dual union all
                                                                           select 6, 'lady2', to_date('12/12/2000', 'dd/mm/yyyy'), to_date('01/01/2001', 'dd/mm/yyyy'), null, 20 from dual union all
                                                                           select 7, 'lady3', to_date('02/02/2001', 'dd/mm/yyyy'), null, to_date('04/02/2004', 'dd/mm/yyyy'), 20 from dual union all
                                                                           select 8, 'lady4', to_date('05/05/2005', 'dd/mm/yyyy'), null, null, 20 from dual union all
                                                                           select 9, 'mate', to_date('23/06/2003', 'dd/mm/yyyy'), to_date('12/12/2009', 'dd/mm/yyyy'), null, 25 from dual)
select id,
       name,
       married_at,
       divorced_at,
       died_at,
       someone_id
from   (select id,
               name,
               married_at,
               divorced_at,
               died_at,
               someone_id,
               row_number() over (partition by someone_id order by married_at desc) rn
        from   partners)
where  rn = 1;

        ID NAME    MARRIED_AT DIVORCED_AT DIED_AT    SOMEONE_ID
---------- ------- ---------- ----------- ---------- ----------
         1 partner 01/01/2000                                12
         3 honey2  16/02/2001                                15
         4 beauty  23/03/2005             25/03/2005         16
         8 lady4   05/05/2005                                20
         9 mate    23/06/2003 12/12/2009                     25

【讨论】:

  • 我的 +1 是因为注意到 divorced_atdied_at 与问题无关。我应该自己考虑一下:-)
  • 托布西首先注意到了这一点,尽管很遗憾他们的回答没有回答问题。我,呃,“借用”了这个想法并付诸实践!
  • 从来没有听说过这个over (partition by...),但它可以工作,我可以很容易地用它来连​​接。谢谢!
  • 如果您以前从未接触过分析函数,那么您应该好好研究它们。在最基本的情况下,它们允许您跨行聚合数据(就像使用 group by 一样),但不会向下折叠行。对于运行总计、查找第一行等非常方便
【解决方案2】:

如果我明白你的问题(我相信我明白),你应该尝试这样的事情:

SELECT *
FROM someone_table s
left join (
    SELECT *
    FROM (
        SELECT *
        FROM partners p
        WHERE p.someone_id = s.id
        ORDER BY GREATEST(died_at, divorced_at, married_at)
    ) x
    WHERE ROWNUM = 1 
 ) y

注意:我不是oracle人,我的大部分工作都是用sql server,但according to this postgreatest应该在oracle数据库上工作。

【讨论】:

  • TOP 1不是有效的 Oracle 语法
  • 这里你使用GREATEST的问题是遇到空值就返回空。
  • 是的,好吧,如果我想回答 oracle 问题,我想我应该更加努力...... :-) 我可能很容易使用 sql server 来解决。
  • 我对 SQL Server 也有同样的看法,所以不用担心! *{:-)
【解决方案3】:

方法一

SELECT 
    * 
FROM 
    partners 
WHERE 
    someone_id = $someone_id 
AND 
    married_at = (SELECT MAX(married_at) FROM partners WHERE someone_id = $someone_id GROUP BY someone_id);

方法2

SELECT 
    p.*
FROM
    partners p
INNER JOIN
(
    SELECT 
        someone_id, MAX(married_at) as lastmarried_at
    FROM 
        partners
    GROUP BY
        someone_id
) m
ON m.someone_id = p.someone_id AND m.lastmarried_at = p.married_at
where p.someone_id in ($someone_id1, $someone_id2);

注意:将 $someone_id 替换为实际值

【讨论】:

  • 嗨,Rajesh,这在选择合作伙伴表时给出了预期的结果。但是当我在我的加入中使用它时,我也收到了与我的问题中描述的相同的错误消息,请参阅那里的第二个选择语句。 ://
  • 嗨,比什,见方法 2;两个查询都给出相同的结果。如果您仍然无法在您的视图/加入中使用它们......也许您可以打开新线程来更正您的视图/加入;将此答案标记为已接受。
【解决方案4】:
SELECT
    ID,
    Name,
    MAX(Married_At) LastMarriedAt,
    MAX(Divorced_At) KEEP (DENSE_RANK LAST ORDER BY Married_At) LastDivorcedAt,
    MAX(Died_At) KEEP (DENSE_RANK LAST ORDER BY Married_At) LastDiedAt,
    MAX(Someone_ID) KEEP (DENSE_RANK LAST ORDER BY Married_At) LastSomeoneID
FROM
    Partners
GROUP BY
    ID,
    Name

查看示例:http://sqlfiddle.com/#!4/3c073/1

编辑:根据示例数据,需要移动一些列

SELECT
    Someone_ID,
    MAX(Name) KEEP (DENSE_RANK LAST ORDER BY Married_At) Name,
    MAX(Married_At) LastMarriedAt,
    MAX(Divorced_At) KEEP (DENSE_RANK LAST ORDER BY Married_At) LastDivorcedAt,
    MAX(Died_At) KEEP (DENSE_RANK LAST ORDER BY Married_At) LastDiedAt,
    MAX(ID) KEEP (DENSE_RANK LAST ORDER BY Married_At) LastID
FROM
    Partners
GROUP BY
    Someone_ID

【讨论】:

  • 但即使没有看到你的小提琴。我不认为这是我需要的结果。请查看我的示例数据,这将使我的问题更清楚。
  • 没有给出预期的结果。数据也设置不正确。
  • @Rajesh 问题不清楚,当我给出答案时,someone_id 是关键。对我来说,最初形成的问题是 ID 指的是相关用户,而 someone_id 指的是他们的合作伙伴。我添加了一个更新的查询
猜你喜欢
  • 2013-09-16
  • 1970-01-01
  • 2019-03-11
  • 1970-01-01
  • 2014-12-14
  • 1970-01-01
  • 1970-01-01
  • 2013-02-09
  • 1970-01-01
相关资源
最近更新 更多