2

大学のコースに関する情報を含むデータベースを構築しています。各コースを関連付けることができます

  • 一人または複数の著者
  • 1 つまたは複数の分野
  • 1つまたは複数の機関
  • 1 つまたは複数のレベル

私のデータベースには次のテーブルが含まれています。

  • コース (cou_id, cou_name, cou_number, cou_year, cou_term)
  • 著者 (aut_id、aut_last)
  • 規律 (dis_id、dis_name)
  • 機関 (ins_id、ins_name、ins_classification)
  • レベル (lev_id、lev_name)
  • authorcourse (リンク テーブル)
  • コース規律(リンクテーブル)
  • コース機関(リンクテーブル)
  • courselevel (リンク テーブル)

データベースからすべてのコース (および対応する著者、分野、教育機関、およびレベル情報) を取得するために、次のクエリを使用します。

SELECT DISTINCT aut_last, c.cou_id, cou_name, cou_number, cou_year, cou_term, dis_name, ins_name, ins_classification, lev_name
    FROM authorcourse ac1
    INNER JOIN authorcourse ac2        
    ON ac1.cou_id = ac2.cou_id        
    INNER JOIN author a
    ON ac2.aut_id=a.aut_id
    INNER JOIN course c
    ON ac2.cou_id = c.cou_id
    INNER JOIN coursediscipline cd1
    ON ac2.cou_id = cd1.cou_id
    INNER JOIN coursediscipline cd2
    ON cd1.cou_id = cd2.cou_id
    INNER JOIN discipline d
    ON cd2.dis_id = d.dis_id
    INNER JOIN courseinstitution ci1
    ON ac2.cou_id = ci1.cou_id
    INNER JOIN courseinstitution ci2
    ON ci1.cou_id = ci2.cou_id
    INNER JOIN institution i
    ON ci2.ins_id = i.ins_id
    INNER JOIN courselevel cl1
    ON ac2.cou_id = cl1.cou_id
    INNER JOIN courselevel cl2
    ON cl1.cou_id = cl2.cou_id
    INNER JOIN level l
    ON cl2.lev_id = l.lev_id

このクエリは、データベースに「単純な」関係を持つ 15 のコースがある場合にうまく機能します。例えば:

 cou_name = 'course1', cou_number = 'C1', cou_year = '1999', cou_term = 'summer'
 aut_last = 'Doe1'
 dis_name = 'discipline1'
 ins_name = 'institution1', ins_classification = 'classification1'
 lev_name = 'level1'

--> 行 0 ~ 14 を表示 (合計 15、クエリにかかった時間は 0.0118 秒) EXPLAIN は次の表を生成します。

id select_type table type   possible_keys          key     key_len ref             rows Extra
1  SIMPLE      ac1   index  cou_id                 aut_id  2       NULL            15   Using index; Using  temporary
1  SIMPLE      ac2   ref    PRIMARY,aut_id,cou_id  cou_id  2       ccdb.ac1.cou_id 1    Using index
1  SIMPLE      a     eq_ref PRIMARY                PRIMARY 2       ccdb.ac2.aut_id 1    
1  SIMPLE      c     eq_ref PRIMARY                PRIMARY 2       ccdb.ac2.cou_id 1    Using where
1  SIMPLE      cd1   ref    PRIMARY,cou_id         PRIMARY 2       ccdb.ac1.cou_id 1    Using index
1  SIMPLE      cd2   ref    PRIMARY,cou_id,dis_id  PRIMARY 2       ccdb.ac2.cou_id 1    Using where; Using index
1  SIMPLE      d     eq_ref PRIMARY                PRIMARY 2       ccdb.cd2.dis_id 1    
1  SIMPLE      ci1   ref    PRIMARY,cou_id         PRIMARY 2       ccdb.ac2.cou_id 1    Using where; Using index
1  SIMPLE      ci2   ref    PRIMARY,cou_id,ins_id  PRIMARY 2       ccdb.ac2.cou_id 1    Using where; Using index
1  SIMPLE      i     eq_ref PRIMARY                PRIMARY 2       ccdb.ci2.ins_id 1    
1  SIMPLE      cl1   ref    PRIMARY,cou_id         PRIMARY 2       ccdb.cd1.cou_id 1    Using where; Using index
1  SIMPLE      cl2   ref    PRIMARY,cou_id,lev_id  PRIMARY 2       ccdb.cl1.cou_id 1    Using where; Using index
1  SIMPLE      l     eq_ref PRIMARY                PRIMARY 2       ccdb.cl2.lev_id 1    

問題:複数の関係を持つ 15 のコースがある場合、パフォーマンスが劇的に低下します。コース例:

cou_name = 'course1', cou_number = 'C1', cou_year = '1999', cou_term = 'summer'
aut_last = 'Doe1', 'Doe', 'Doe3', 'Doe4'
dis_name = 'discipline1', 'discipline2', 'discipline3', 'discipline4'
ins_name = 'institution1'(ins_classification = 'classification1'),     'institution2'(ins_classification = 'classification2'), 'institution3'(ins_classification =  'classification3'), 'institution4' (ins_classification = 'classification4')
lev_name = 'level1', 'level2', 'level3', 'level4'

--> 行 0 ~ 29 を表示 (合計 3,840、クエリに 14.7039 秒かかりました) EXPLAIN は次の表を生成します。

 id select_type table type   possible_keys         key     key_len ref             rows Extra
 1  SIMPLE      c     ALL    PRIMARY               NULL    NULL    NULL            15   Using temporary
 1  SIMPLE      ac1   ref    PRIMARY,aut_id,cou_id cou_id  2       ccdb.c.cou_id   2    Using index
 1  SIMPLE      a     eq_ref PRIMARY               PRIMARY 2       ccdb.ac1.aut_id 1    
 1  SIMPLE      ac2   ref    cou_id                cou_id  2       ccdb.c.cou_id   2    Using index
 1  SIMPLE      cd1   ref    PRIMARY,cou_id        cou_id  2       ccdb.ac1.cou_id 2    Using where; Using index
 1  SIMPLE      cd2   ref    PRIMARY,cou_id,dis_id cou_id  2       ccdb.c.cou_id   2    Using index
 1  SIMPLE      d     eq_ref PRIMARY               PRIMARY 2       ccdb.cd2.dis_id 1    
 1  SIMPLE      ci1   ref    PRIMARY,cou_id        cou_id  2       ccdb.ac1.cou_id 2    Using where; Using index
 1  SIMPLE      ci2   ref    PRIMARY,cou_id,ins_id cou_id  2       ccdb.ac2.cou_id 2    Using where; Using index
 1  SIMPLE      i     eq_ref PRIMARY               PRIMARY 2       ccdb.ci2.ins_id 1    
 1  SIMPLE      cl1   ref    PRIMARY,cou_id        cou_id  2       ccdb.c.cou_id   2    Using index
 1  SIMPLE      cl2   ref    PRIMARY,cou_id,lev_id cou_id  2       ccdb.ci2.cou_id 2    Using where; Using index
 1  SIMPLE      l     eq_ref PRIMARY               PRIMARY 2       ccdb.cl2.lev_id 1    

PHP Web サイトを実行すると、「致命的なエラー: 最大実行時間が 30 秒を超えました...」というエラーが表示されます。</p>

質問: このクエリを高速化するにはどうすればよいですか? 結合のいくつかの異なる組み合わせを試し、(EXPLAIN の結果でわかるように) 関連する可能性があると思われるすべての列にインデックスを付けました。

どんな助けでも大歓迎です。

4

3 に答える 3

1

クエリに不要な結合があるようです。以下を実行することで同じことが得られると思います。おそらくクエリのパフォーマンスが向上します。

SELECT DISTINCT aut_last, c.cou_id, cou_name, cou_number, cou_year, cou_term, dis_name, ins_name, ins_classification, lev_name
    FROM authorcourse ac 
    INNER JOIN author a
    ON ac.aut_id=a.aut_id
    INNER JOIN course c
    ON ac.cou_id = c.cou_id
    INNER JOIN coursediscipline cd 
    ON ac.cou_id = cd.cou_id
    INNER JOIN discipline d
    ON cd.dis_id = d.dis_id
    INNER JOIN courseinstitution ci 
    ON ac.cou_id = ci.cou_id
    INNER JOIN institution i
    ON ci.ins_id = i.ins_id
    INNER JOIN courselevel cl 
    ON ac.cou_id = cl.cou_id
    INNER JOIN level l
    ON cl.lev_id = l.lev_id

何も達成していないように見える同じテーブルへの冗長な結合がありました。

于 2013-01-29T19:32:51.260 に答える
1

「コースビュー」タイプの詳細ページ用にこのすべてのデータを取得しているように見えますか?

もしそうなら、データベースでコースが作成されると、著者、分野、機関、およびレベルの数はどのくらいの頻度で変更されますか?

設定された時間から変更されない場合は、設定されたときに、次のように完全に非正規化されたテーブルにも設定します。

courseView (cou_id, cou_name, cou_number, cou_year, cou_term, data )

..そして「データ」には、すべてのデータのシリアル化された配列を入れるだけです。醜いですが、それは速くなるでしょう。

次に、コースIDで検索して1つを引き出すと、1行、1つのインデックスのみを検索して、すべてのデータを瞬時に引き出すことができます。

..

また、ユーザーが Authors で検索できるようにする場合でも、通常のような単純なクエリを使用して、正規化されたテーブルでこれを行うことができます。

于 2013-01-29T19:10:21.090 に答える
0

問題を完全に解決するのに十分な情報があるとは思えません。Explain ステートメントを見ると、すべてのインデックスが正しく設定されているように見えますが、取得している行数が問題になっています。

犯人は、非常に大きなテーブルでのテーブルスキャンが、取得している行数によって複合されていることが原因である可能性が最も高いです。これらの問題に遭遇したときの私の経験から、スローレスが発生する場所を特定し、そこから作業します。使用するいくつかの戦略は、一時テーブルまたはサブクエリです (これらは、それらをより小さく、より管理しやすいクエリに分割します。

また、PHP の致命的なエラーの問題を取り除くには、try / catch 例外ブロックを使用して適切に処理できるようにする必要があります。

于 2013-01-29T19:32:28.743 に答える