3

正常に動作する次の PostgreSQL クエリを作成しました。ただし、非常に遅いようで、結果を返すのに最大 10 秒かかることもあります。私の声明には、これが遅くなる原因があると確信しています。

このクエリが遅い理由を特定できる人はいますか?

SELECT DISTINCT ON (school_classes.class_id,attendance_calendar.school_date)
  school_classes.class_id, school_classes.class_name, school_classes.grade_id
, school_gradelevels.linked_calendar, attendance_calendars.calendar_id
, attendance_calendar.school_date, attendance_calendar.minutes
, teacher_join_classes_subjects.staff_id, staff.first_name, staff.last_name  

FROM school_classes 
INNER JOIN school_gradelevels ON school_gradelevels.id=school_classes.grade_id 
INNER JOIN teacher_join_classes_subjects ON teacher_join_classes_subjects.class_id=school_classes.class_id 
INNER JOIN staff ON staff.staff_id=teacher_join_classes_subjects.staff_id 
INNER JOIN attendance_calendars ON attendance_calendars.title=school_gradelevels.linked_calendar 
INNER JOIN attendance_calendar ON attendance_calendar.calendar_id=attendance_calendars.calendar_id 

WHERE teacher_join_classes_subjects.syear='2013' 
AND staff.syear='2013' 
AND attendance_calendars.syear='2013' 
AND teacher_join_classes_subjects.does_attendance='Y' 
AND teacher_join_classes_subjects.subject_id IS NULL 
AND attendance_calendar.school_date<CURRENT_DATE 

AND attendance_calendar.school_date NOT IN (

SELECT com.school_date FROM attendance_completed com
WHERE  com.class_id=school_classes.class_id
AND   (com.period_id='101' AND attendance_calendar.minutes>='151' OR
       com.period_id='95'  AND attendance_calendar.minutes='150') )

NOT INを次のものに置き換えました。

AND NOT EXISTS (
    SELECT com.school_date
    FROM attendance_completed com
    WHERE com.class_id=school_classes.class_id
    AND com.school_date=attendance_calendar.school_date
    AND (com.period_id='101' AND attendance_calendar.minutes>='151' OR
         com.period_id='95'  AND attendance_calendar.minutes='150') )

EXPLAIN ANALYZE の結果:

ユニーク (コスト=2998.39..2998.41 行=3 幅=85) (実際の時間=10751.111..10751.118 行=1 ループ=1)
  -> 並べ替え (コスト = 2998.39..2998.40 行 = 3 幅 = 85) (実際の時間 = 10751.110..10751.110 行 = 2 ループ = 1)
        ソートキー: school_classes.class_id、attendance_calendar.school_date
        ソート方法:クイックソートメモリ:25kB
        -> ハッシュ結合 (コスト=2.03..2998.37 行=3 幅=85) (実際の時間=6409.471..10751.045 行=2 ループ=1)
              ハッシュ条件: ((teacher_join_classes_subjects.class_id = school_classes.class_id) AND (school_gradelevels.id = school_classes.grade_id))
              結合フィルター: (NOT (サブプラン 1))
              -> ネストされたループ (コスト = 0.00..120.69 行 = 94 幅 = 81) (実際の時間 = 2.468..1187.397 行 = 26460 ループ = 1)
                    結合フィルター: (attendance_calendars.calendar_id =attendance_calendar.calendar_id)
                    -> ネストされたループ (コスト=0.00..42.13 行=1 幅=70) (実際の時間=0.087..3.247 行=735 ループ=1)
                          結合フィルター: ((attendance_calendars.title)::text = (school_gradelevels.linked_calendar)::text)
                          -> ネストされたループ (コスト=0.00..40.80 行=1 幅=277) (実際の時間=0.077..1.005 行=245 ループ=1)
                                -> ネストされたループ (コスト=0.00..39.61 行=1 幅=27) (実際の時間=0.064..0.572 行=49 ループ=1)
                                      -> teacher_join_classes_subjects の Seq Scan (cost=0.00..10.48 rows=4 width=14) (実際の時間=0.022..0.143 rows=49 loops=1)
                                            フィルター: ((subject_id IS NULL) AND (syear = 2013::numeric) AND ((does_attendance)::text = 'Y'::text))
                                      -> スタッフの staff_pkey を使用したインデックス スキャン (コスト = 0.00..7.27 行 = 1 幅 = 20) (実際の時間 = 0.006..0.007 行 = 1 ループ = 49)
                                            索引条件: (staff.staff_id = teacher_join_classes_subjects.staff_id)
                                            フィルタ: (staff.syear = 2013::numeric)
                                ->attendance_calendars の Seq Scan (cost=0.00..1.18 rows=1 width=250) (実際の時間=0.003..0.006 rows=5 loops=49)
                                      フィルタ: (attendance_calendars.syear = 2013::numeric)
                          -> school_gradelevels のシーケンス スキャン (コスト=0.00..1.15 行=15 幅=11) (実際の時間=0.001..0.005 行=15 ループ=245)
                    ->attendance_calendar の Seq Scan (cost=0.00..55.26 rows=1864 width=18) (実際の時間=0.003..1.129 rows=1824 loops=735)
                          フィルタ: (attendance_calendar.school_date Hash (cost=1.41..1.41 rows=41 width=18) (実際の時間=0.040..0.040 rows=41 loops=1)
                    -> school_classes の Seq Scan (コスト=0.00..1.41 行=41 幅=18) (実際の時間=0.006..0.015 行=41 ループ=1)
              サブプラン 1
                ->attendance_completed com の Seq Scan (cost=0.00..958.28 rows=5 width=4) (実際の時間=0.228..5.411 rows=17 loops=1764)
                      フィルター: ((class_id = $0) AND (((period_id = 101::numeric) AND ($1 >= 151::numeric)) OR ((period_id = 95::numeric) AND ($1 = 150::numeric)) )))
4

1 に答える 1

2

NOT EXISTS優れた選択です。ほとんどの場合、よりも優れていNOT INます。詳細はこちら。 クエリを少し単純化しました (通常は問題ないようです)。

SELECT DISTINCT ON (c.class_id, a.school_date)
       c.class_id, c.class_name, c.grade_id
      ,g.linked_calendar, aa.calendar_id
      ,a.school_date, a.minutes
      ,t.staff_id, s.first_name, s.last_name  
FROM   school_classes                c
JOIN   teacher_join_classes_subjects t  USING (class_id)
JOIN   staff                         s  USING (staff_id)
JOIN   school_gradelevels            g  ON g.id = c.grade_id 
JOIN   attendance_calendars          aa ON aa.title = g.linked_calendar 
JOIN   attendance_calendar           a  ON a.calendar_id = aa.calendar_id 
WHERE  t.syear = 2013
AND    s.syear = 2013
AND    aa.syear = 2013
AND    t.does_attendance = 'Y'   -- looks like it should be boolean!
AND    t.subject_id IS NULL 
AND    a.school_date < CURRENT_DATE 
AND NOT EXISTS (
   SELECT 1
   FROM   attendance_completed x
   WHERE  x.class_id = c.class_id
   AND    x.school_date = a.school_date
   AND   (x.period_id = 101 AND a.minutes >= 151 OR  -- actually numbers?
          x.period_id =  95 AND a.minutes  = 150)
   )
ORDER BY c.class_id, a.school_date, ???

欠けているように見えるのはORDER BY 、あなたに付随するものDISTINCT ONです。ORDER BYの代わりにさらに項目を追加します???。選択する重複がある場合は、おそらくどれを選択するかを定義する必要があります

数値リテラルは単一引用符を必要とせず、boolean値はそのようにコーディングする必要があります。データ型に関する章
を再度参照することをお勧めします。

于 2013-10-31T17:32:14.343 に答える