选择行取决于是否存在另一个表行

时间:2018-05-02 17:49:43

标签: sql postgresql

很难为这一个写好标题......

我的postgresql数据库中有4个表:

courses:
| id | data | created_at | updated_at | status |

course_versions:
| id | course_id | data | created_at | updated_at | status |

course_progresses:
| id | course_version_id | user_id | data | created_at | updated_at |

users:
| id | email |

课程可以有多个course_versions,用户可以拥有course_progresses,每个course_progress都连接到course_version。现在,我想获取特定用户的所有course_versions,也就是说,如果用户没有{_ 1}}的course_version的course_progress,它应该返回最新的course_version。如果用户的course_version为course id: 1的course_progress,则应返回course_version,无论是否有更新的course_versions ...获取它? :)

这是一个有效的查询:

course id: 2

虽然对我来说看起来很可怕......有没有更好的方法来编写这个查询?

2 个答案:

答案 0 :(得分:0)

您可以使用cross join创建用户和课程的2D矩阵。对于每个条目,请查找具有进度的版本或最新版本:

select  u.email
,       c.course_id
,       coalesce(progress_version.created_at, latest_version.created_at)
from    users u
cross join
        courses c
left join
        (
        select  cp.user_id
        ,       cv.course_id
        ,       cv.created_at
        from    course_progresses cp
        join    course_versions cv
        on      cv.id = cp.course_version_id
        ) progress_version
on      progress_version.user_id = u.id
        and progress_version.course_id = c.id
left join
        (
        select  row_number() over (partition by course_id 
                                   order by created_at desc) rn
        ,       *
        from    course_versions
        ) latest_version
on      latest_version.course_id = c.id
        and latest_version.rn = 1

答案 1 :(得分:0)

如果没有您的示例数据,很难写出来,但是:

with -- Example data, just to check that there are no syntax errors 
    courses(id, status) as (values
        (1,1),(2,1),(3,0)),
    course_versions(id, course_id, status, created_at) as (values
        (1,1,1,current_timestamp),(2,1,1,current_timestamp),(3,2,1,current_timestamp),(4,2,0,current_timestamp)),
    course_progresses(id, course_version_id, user_id) as (values
        (2,3,123))
select distinct on (cp.course_version_id, cv.course_id)
    cv.*
from
    course_versions as cv
        join courses as c on (c.id = cv.course_id and c.status = 1 and cv.status = 1)
        left join course_progresses cp on (cp.course_version_id = cv.id and cp.user_id = 123)
order by
    cp.course_version_id nulls last, cv.course_id, cv.created_at desc;