SQL 只加入最后一个历史行
SQL Join only the last historical row
我正面临以下问题:我正在编写一个视图,其中我将几个 table 加入一个人 table。我现在尝试加入合作伙伴 table 但我只需要历史上最后一个有效合作伙伴行:
合作伙伴table:
id,
name,
married_at,
divorced_at,
died_at,
someone_id
如您所见,它是关于您 are/were 结婚的伴侣的。一个人一次只能有一个伙伴,但历史上可以有几个伙伴。所以某人(someone_id)的最后一个搭档可能是:
- 还活着并且仍然结婚
- 活着但离婚了
- 死了"but still married"(所以有人是鳏夫)
我只需要找到某人的最后一个合作伙伴行。
到目前为止我得到了什么:
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) )
但这很明显 - 只给我还活着并且仍然结婚的伴侣。当然,这些伴侣是某人的最后伴侣,但所有其他 "someones" 最后一个伴侣离婚或死亡的人都不会出现在声明的结果中。我如何获得其他人并且每个人只有一行?
我还尝试了 select 语句作为 table 并使用 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" 错误
注意:table 结构是固定的,无法更改。数据库管理系统是甲骨文。
提前致谢
编辑:
样本数据
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 ║
╚════╩═════════╩════════════╩═════════════╩════════════╩════════════╝
如果我理解你的问题(我相信我理解),你应该尝试这样的事情:
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 服务器,但是 according to this post greatest
应该在 Oracle 数据库上工作。
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
这应该可以满足您的要求:
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 :
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
我正面临以下问题:我正在编写一个视图,其中我将几个 table 加入一个人 table。我现在尝试加入合作伙伴 table 但我只需要历史上最后一个有效合作伙伴行:
合作伙伴table:
id,
name,
married_at,
divorced_at,
died_at,
someone_id
如您所见,它是关于您 are/were 结婚的伴侣的。一个人一次只能有一个伙伴,但历史上可以有几个伙伴。所以某人(someone_id)的最后一个搭档可能是:
- 还活着并且仍然结婚
- 活着但离婚了
- 死了"but still married"(所以有人是鳏夫)
我只需要找到某人的最后一个合作伙伴行。
到目前为止我得到了什么:
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) )
但这很明显 - 只给我还活着并且仍然结婚的伴侣。当然,这些伴侣是某人的最后伴侣,但所有其他 "someones" 最后一个伴侣离婚或死亡的人都不会出现在声明的结果中。我如何获得其他人并且每个人只有一行?
我还尝试了 select 语句作为 table 并使用 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" 错误
注意:table 结构是固定的,无法更改。数据库管理系统是甲骨文。
提前致谢
编辑: 样本数据
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 ║
╚════╩═════════╩════════════╩═════════════╩════════════╩════════════╝
如果我理解你的问题(我相信我理解),你应该尝试这样的事情:
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 服务器,但是 according to this post greatest
应该在 Oracle 数据库上工作。
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
这应该可以满足您的要求:
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 :
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