You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PostgreSQL多表Join后按时间排序并去重的实现问题

问题:获取关联表中按时间排序后的唯一最新记录

表结构与测试数据

CREATE TABLE t1(myid int, myyear int, mycol int, mdate timestamp);
INSERT INTO t1 VALUES
(11833,2022,1059,'2022-11-03 22:02:00'),(11834,2022,1059,'2022-11-17 19:56:41'),(11832,2021,1058,'2021-11-16 16:38:21'),(11839,2021,1057,'2021-11-10 18:08:09'),(11847,2021,1055,'2022-05-31 12:13:11'),(11847,2021,1055,'2022-05-31 12:13:11'),(11850,2021,1049,'2021-09-29 16:11:31'),(11853,2021,1046,'2022-01-24 11:44:41'),(11855,2021,1045,'2022-01-24 11:38:05'),(11865,2021,1044,'2022-01-24 11:23:51'),(11856,2021,1043,'2022-01-24 11:00:24'),(11840,2021,1042,'2021-11-30 12:28:13'),(11831,2021,1042,'2021-11-30 12:22:30'),(11846,2022,1042,'2022-11-02 15:06:00'),(11829,2022,1036,'2022-11-02 02:37:00'),(11826,2021,1035,'2021-09-24 13:07:48'),(11825,2021,1034,'2021-10-06 08:22:23'),(11830,2022,1033,'2022-11-03 21:18:00'),(11827,2022,1033,'2022-11-15 21:46:04'),(11828,2022,1032,'2022-11-08 16:44:08'),(11824,2022,1031,'2022-10-25 18:09:03'),(11823,2022,1031,'2022-11-02 03:10:00'),(11822,2022,1030,'2022-10-24 14:59:25')
;

CREATE TABLE t2(myid int, name varchar,idate timestamp);
INSERT INTO t2 VALUES
(11833,'Name1684','2023-01-10 15:52:55'),(11834,'Name1727','2023-01-10 15:52:55'),(11832,'Name609','2023-01-10 15:52:54'),(11839,'Name608','2023-01-10 15:52:59'),(11847,'Name606','2023-01-10 15:53:03'),(11847,'Name607','2023-01-10 15:53:03'),(11850,'Name605','2023-01-10 15:53:04'),(11853,'Name604','2023-01-10 15:53:05'),(11855,'Name603','2023-01-10 15:53:06'),(11865,'Name602','2023-01-10 15:53:10'),(11856,'Name601','2023-01-10 15:53:07'),(11840,'Name600','2023-01-10 15:52:59'),(11831,'Name1726','2023-01-10 15:52:53'),(11846,'Name1683','2023-01-10 15:53:03'),(11829,'Name1682','2023-01-10 15:52:52'),(11826,'Name599','2023-01-10 15:52:50'),(11825,'Name598','2023-01-10 15:52:49'),(11830,'Name1681','2023-01-10 15:52:52'),(11827,'Name1725','2023-01-10 15:52:51'),(11828,'Name1680','2023-01-10 15:52:51'),(11824,'Name1678','2023-01-10 15:52:48'),(11823,'Name1679','2023-01-10 15:52:48'),(11822,'Name1677','2023-01-10 15:52:47')
;

需求与期望结果

需求:关联t1和t2,先按mdate、idate倒序排序,获取myyear和mycol组合唯一的对应最新时间的记录。

期望结果示例:

CREATE TABLE expectedresult(myid int, myyear int,mycol int, mdate timestamp,name varchar,idate timestamp);
INSERT INTO expectedresult VALUES
(11834,2022,1059,'2022-11-17 19:56:41','Name1727','2023-01-10 15:52:55')
;

错误实现及问题

以下SQL返回了mdate更早的错误记录:

create table t3 as(
select distinct on (subq1.myyear,subq1.mycol)
  *
  from(
Select 
t1.myid,
t1.myyear,
t1.mycol,
t1.mdate,
t2.name,
t2.idate
from t1
join t2
on t1.myid=t2.myid
order by t1.mdate desc, t2.idate desc) subq1)

问题原因:PostgreSQL的DISTINCT ON要求排序字段必须以分组字段(myyear, mycol)开头,否则无法保证每组取到的是最新记录。原SQL的子查询仅按时间排序,未先按分组字段排序,导致DISTINCT ON无法正确筛选每组的第一条记录。

正确实现方案

方案1:修正DISTINCT ON的排序逻辑

在查询中先按myyear、mycol分组,再按mdate、idate倒序排序,确保每组的第一条是最新记录:

CREATE TABLE t3 AS (
  SELECT DISTINCT ON (myyear, mycol)
    t1.myid,
    t1.myyear,
    t1.mycol,
    t1.mdate,
    t2.name,
    t2.idate
  FROM t1
  JOIN t2 ON t1.myid = t2.myid
  ORDER BY myyear, mycol, mdate DESC, idate DESC
);

方案2:使用窗口函数ROW_NUMBER()(兼容性更广)

通过窗口函数为每个myyear、mycol分组的记录按时间倒序编号,取编号为1的记录:

CREATE TABLE t3 AS (
  SELECT myid, myyear, mycol, mdate, name, idate
  FROM (
    SELECT 
      t1.myid,
      t1.myyear,
      t1.mycol,
      t1.mdate,
      t2.name,
      t2.idate,
      ROW_NUMBER() OVER (
        PARTITION BY t1.myyear, t1.mycol 
        ORDER BY t1.mdate DESC, t2.idate DESC
      ) AS rn
    FROM t1
    JOIN t2 ON t1.myid = t2.myid
  ) subq
  WHERE rn = 1
);

两种方案均可正确获取myyear和mycol组合对应的最新时间记录,方案2适用于更多数据库(如MySQL 8.0+、SQL Server等),方案1是PostgreSQL专属的高效写法。

内容的提问来源于stack exchange,提问作者Zio

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 14:45:31