多个子查询连接中的效率/清除代码

时间:2016-06-14 15:50:36

标签: postgresql subquery

我已经构建了一个大的子查询,需要过滤,然后以三种不同的方式求和()。我已经聚合了它,然后将这些聚合加在一起,但这给我留下了一个包含三个相同部分(子查询)的查询。

是否有更高效,编程或干净的方法来实现这一目标?我知道清洁和高效可能会大不相同。

我突出显示了每个联接的更改部分。

select 
total_times.student_id
, total_times.section_id
, present_times.total as present
, relation_times.total as relation
, total_times.total as total
from (
    select 
    s.student_id
    , sec.section_id
    , sum(sec_times.time) as total
    from students s
    join section_student_aff secsaff on secsaff.student_id = s.student_id
    join sections sec on sec.section_id = secsaff.section_id
    join (
        select distinct
        site.site_id
        , cal.date
        , sec.section_id
        , (time.end_time - time.start_time) as time
        from calendar_days cal
        join terms t on cal.date between t.start_date and t.end_date
        join sessions ses on ses.session_id = t.session_id
        join timeblocks blk on blk.session_id = ses.session_id
        join timeblock_times time on time.timeblock_id = blk.timeblock_id
        join sites site on site.site_id = ses.site_id
        join section_timeblock_aff sectaff on sectaff.timeblock_id = blk.timeblock_id
        join sections sec on sec.section_id = sectaff.section_id
        where ses.academic_year = 2016
        and time.day_of_week = extract(dow from cal.date)
        order by site_id, date, section_id
        ) sec_times
        on sec_times.section_id = sec.section_id

    --- Part 1 (no filter) ---

    group by s.student_id, sec.section_id
    ) total_times
join (
    select 
    s.student_id
    , sec.section_id
    , sum(sec_times.time) as total
    from students s
    join section_student_aff secsaff on secsaff.student_id = s.student_id
    join sections sec on sec.section_id = secsaff.section_id
    join (
        select distinct
        site.site_id
        , cal.date
        , sec.section_id
        , (time.end_time - time.start_time) as time
        from calendar_days cal
        join terms t on cal.date between t.start_date and t.end_date
        join sessions ses on ses.session_id = t.session_id
        join timeblocks blk on blk.session_id = ses.session_id
        join timeblock_times time on time.timeblock_id = blk.timeblock_id
        join sites site on site.site_id = ses.site_id
        join section_timeblock_aff sectaff on sectaff.timeblock_id = blk.timeblock_id
        join sections sec on sec.section_id = sectaff.section_id
        where ses.academic_year = 2016
        and time.day_of_week = extract(dow from cal.date)
        order by site_id, date, section_id
        ) sec_times
        on sec_times.section_id = sec.section_id

    --- Part 2 ---
    where exists (
        select null
        from student_attendance sa
        join attendance_flags af on af.attendance_flag_id = sa.attendance_flag_id
        where
        af.is_present=true
        and sa.date = sec_times.date
        and sa.sa_student_id = s.student_id
        )
    --------------

    group by s.student_id, sec.section_id
    ) present_times
    on present_times.student_id = total_times.student_id
    and present_times.section_id = total_times.section_id
join (
    select 
    s.student_id
    , sec.section_id
    , sum(sec_times.time) as total
    from students s
    join section_student_aff secsaff on secsaff.student_id = s.student_id
    join sections sec on sec.section_id = secsaff.section_id
    join (
        select distinct
        site.site_id
        , cal.date
        , sec.section_id
        , (time.end_time - time.start_time) as time
        from calendar_days cal
        join terms t on cal.date between t.start_date and t.end_date
        join sessions ses on ses.session_id = t.session_id
        join timeblocks blk on blk.session_id = ses.session_id
        join timeblock_times time on time.timeblock_id = blk.timeblock_id
        join sites site on site.site_id = ses.site_id
        join section_timeblock_aff sectaff on sectaff.timeblock_id = blk.timeblock_id
        join sections sec on sec.section_id = sectaff.section_id
        where ses.academic_year = 2016
        and time.day_of_week = extract(dow from cal.date)
        order by site_id, date, section_id
        ) sec_times
        on sec_times.section_id = sec.section_id

    --- Part 3 ---
    where sec_times.date between secsaff.entry_date and secsaff.leave_date
    --------------

    group by s.student_id, sec.section_id
    ) relation_times
    on relation_times.student_id = total_times.student_id
    and relation_times.section_id = total_times.section_id
order by student_id, section_id

1 个答案:

答案 0 :(得分:1)

使用WITH和条件求和,它变得更易于管理(也许更快):

with
  sec_times as (
    select distinct
    site.site_id
    , cal.date
    , sec.section_id
    , (time.end_time - time.start_time) as time
    from calendar_days cal
    join terms t on cal.date between t.start_date and t.end_date
    join sessions ses on ses.session_id = t.session_id
    join timeblocks blk on blk.session_id = ses.session_id
    join timeblock_times time on time.timeblock_id = blk.timeblock_id
    join sites site on site.site_id = ses.site_id
    join section_timeblock_aff sectaff on sectaff.timeblock_id = blk.timeblock_id
    join sections sec on sec.section_id = sectaff.section_id
    where ses.academic_year = 2016
    and time.day_of_week = extract(dow from cal.date)
    ),
  sec_times_full as (
    select 
    s.student_id
    , sec.section_id
    , sec_times.time
    , exists (
        select null
        from student_attendance sa
        join attendance_flags af on af.attendance_flag_id = sa.attendance_flag_id
        where
        af.is_present=true
        and sa.date = sec_times.date
        and sa.sa_student_id = s.student_id
        ) as is_present
    , sec_times.date between secsaff.entry_date and secsaff.leave_date as is_relation
    from students s
    join section_student_aff secsaff on secsaff.student_id = s.student_id
    join sections sec on sec.section_id = secsaff.section_id
    join sec_times on sec_times.section_id = sec.section_id
    )
select 
  student_id
  , section_id
  , sum(CASE WHEN is_present  THEN time END) as present
  , sum(CASE WHEN is_relation THEN time END) as relation
  , sum(time) as total
from sec_times_full
group by student_id, section_id
order by 1, 2