3

データベースにこれらの2つのテーブルがあります

  Student Table                   Student Semester Table
| Column     : Type     |       | Column     : Type     |
|------------|----------|       |------------|----------|
| student_id : integer  |       | student_id : integer  |      
| satquan    : smallint |       | semester   : integer  |
| actcomp    : smallint |       | enrolled   : boolean  | 
| entryyear  : smallint |       | major      : text     |
|-----------------------|       | college    : text     |
                                |-----------------------|

ここで、student_id は、student テーブルの一意のキーであり、student semester テーブルの外部キーです。学期の整数は、最初の学期は 1、2 番目の学期は 2 などです。

私は、入学年ごとに (そして時には彼らの土や行為のスコアによって) 学生を取得したいクエリを実行してから、学生学期テーブルからすべての学生に関連するデータを取得します。

現在、私のクエリは次のようになっています。

SELECT * FROM student_semester
WHERE student_id IN(
    SELECT student_id FROM student_semester
    WHERE student_id IN(
        SELECT student_id FROM student WHERE entryyear = 2006
    ) AND college = 'AS' AND ...
)
ORDER BY student_id, semester;

しかし、これにより、約 1,000 人の学生を選択すると、クエリの実行時間が比較的長くなります (400 ミリ秒)。実行計画によると、ほとんどの時間はハッシュ結合に費やされます。これを改善するために、satquan、actpcomp、および entryyear 列を student_semester テーブルに追加しました。これにより、クエリの実行時間が最大 90% 短縮されますが、多くの冗長データが発生します。これを行うより良い方法はありますか?

これらは私が現在持っているインデックスです (student_id の暗黙的なインデックスと共に):

CREATE INDEX act_sat_entryyear ON student USING btree (entryyear, actcomp, sattotal)
CREATE INDEX student_id_major_college ON student_semester USING btree (student_id, major, college)

クエリ プラン

QUERY PLAN
Hash Join  (cost=17311.74..35895.38 rows=81896 width=65) (actual time=121.097..326.934 rows=25680 loops=1)
  Hash Cond: (public.student_semester.student_id = public.student_semester.student_id)
  ->  Seq Scan on student_semester  (cost=0.00..14307.20 rows=698820 width=65) (actual time=0.015..154.582 rows=698820 loops=1)
  ->  Hash  (cost=17284.89..17284.89 rows=2148 width=8) (actual time=121.062..121.062 rows=1284 loops=1)
        Buckets: 1024  Batches: 1  Memory Usage: 51kB
        ->  HashAggregate  (cost=17263.41..17284.89 rows=2148 width=8) (actual time=120.708..120.871 rows=1284 loops=1)
              ->  Hash Semi Join  (cost=1026.68..17254.10 rows=3724 width=8) (actual time=4.828..119.619 rows=6184 loops=1)
                    Hash Cond: (public.student_semester.student_id = student.student_id)
                    ->  Seq Scan on student_semester  (cost=0.00..16054.25 rows=42908 width=4) (actual time=0.013..109.873 rows=42331 loops=1)
                          Filter: ((college)::text = 'AS'::text)
                    ->  Hash  (cost=988.73..988.73 rows=3036 width=4) (actual time=4.801..4.801 rows=3026 loops=1)
                          Buckets: 1024  Batches: 1  Memory Usage: 107kB
                          ->  Bitmap Heap Scan on student  (cost=71.78..988.73 rows=3036 width=4) (actual time=0.406..3.223 rows=3026 loops=1)
                                Recheck Cond: (entryyear = 2006)
                                ->  Bitmap Index Scan on student_act_sat_entryyear_index  (cost=0.00..71.03 rows=3036 width=0) (actual time=0.377..0.377 rows=3026 loops=1)
                                      Index Cond: (entryyear = 2006)
Total runtime: 327.708 ms

クエリに Seq Scan がないのは間違いでした。大学の条件に一致する行の数が原因で、Seq Scan が実行されていると思います。学生が少ないものに変更すると、インデックスが使用されます。ソース: https://stackoverflow.com/a/5203827/880928

学生学期テーブルが含まれる entryyear 列を含むクエリ

SELECT * FROM student_semester
WHERE student_id IN(
    SELECT student_id FROM student_semester
    WHERE entryyear = 2006 AND collgs = 'AS'
) ORDER BY student_id, semester;

クエリ プラン

Sort  (cost=18597.13..18800.49 rows=81343 width=65) (actual time=72.946..74.003 rows=25680 loops=1)
  Sort Key: public.student_semester.student_id, public.student_semester.semester
  Sort Method: quicksort  Memory: 3546kB
  ->  Nested Loop  (cost=9843.87..11962.91 rows=81343 width=65) (actual time=24.617..40.751 rows=25680 loops=1)
        ->  HashAggregate  (cost=9843.87..9845.73 rows=186 width=4) (actual time=24.590..24.836 rows=1284 loops=1)
              ->  Bitmap Heap Scan on student_semester  (cost=1612.75..9834.63 rows=3696 width=4) (actual time=10.401..23.637 rows=6184 loops=1)
                    Recheck Cond: (entryyear = 2006)
                    Filter: ((collgs)::text = 'AS'::text)
                    ->  Bitmap Index Scan on entryyear_act_sat_semester_enrolled_cumdeg_index  (cost=0.00..1611.82 rows=60192 width=0) (actual time=10.259..10.259 rows=60520 loops=1)
                          Index Cond: (entryyear = 2006)
        ->  Index Scan using student_id_index on student_semester  (cost=0.00..11.13 rows=20 width=65) (actual time=0.003..0.010 rows=20 loops=1284)
              Index Cond: (student_id = public.student_semester.student_id)
Total runtime: 74.938 ms
4

3 に答える 3

2

クエリを実行する別の方法は、ウィンドウ関数を使用することです。

select t.*  -- Has the extra NumMatches column.  To eliminate it, list the columns you want
from (select ss.*,
             sum(case when ss.college = 'AS' and s.entry_year = 206 then 1 else 0 end) over
                  (partition by student_id) as NumMatches
      from student_semester ss join
           student s
           on ss.student_id = s.student_id
    ) t
where NumMatches > 0;

ウィンドウ関数は通常、集計に参加するよりも高速であるため、これがうまく機能する可能性があると思います。

于 2013-05-08T17:51:05.760 に答える
2

あなたのクエリのクリーンバージョンは

select ss.*
from
    student s
    inner join
    student_semester ss using(student_id)
where
    s.entryyear = 2006
    and exists (
        select 1
        from student_semester
        where
            college = 'AS'
            and student_id = s.student_id
    )
order by ss.student_id, semester
于 2013-05-08T15:37:34.480 に答える
0

2006 年に入学し、AS カレッジに在籍したことがある学生が必要なようです。

バージョン 1。

SELECT sem.*
FROM student s JOIN student_semester sem USING (student_id)
WHERE s.entry_year=2006
     AND student_id IN (SELECT student_id 
                        FROM student_semester s2 WHERE s2.college='AS')
     AND /* other criteria */
ORDER BY sem.student_id, semester;

バージョン 2

SELECT sem.*
FROM student s JOIN student_semester sem USING (student_id)
WHERE s.entry_year=2006
     AND EXISTS 
         (SELECT 1 FROM student_semester s2 
          WHERE s2.student_id = s.student_id AND s2.college='AS')
          -- CREATE INDEX foo on student_semester(student_id, college);
     AND /* other criteria */
ORDER BY sem.student_id, semester;

どちらも速いと思いますが、一方が他方よりも優れているかどうか (またはまったく同じ計画) は、PG の謎です。

[編集]これは準結合のないバージョンです。学生がASにいるたびに複数のヒットが発生するため、うまく機能するとは思いません。

SELECT DISTINCT ON ( /* PK of sem */ )
FROM student s 
   JOIN student_semester sem USING (student_id) 
   JOIN student_semester s2  USING (student_id)
WHERE s.entry_year=2006
   AND s2.college='AS'
ORDER BY sem.student_id, semester;
于 2013-05-08T17:21:14.050 に答える