1

パフォーマンスの問題が発生しています。これにより、次の処理が約2.5秒で実行され、40行のみが返されます。

SELECT DISTINCT song.song_id, song.title, song.length, song.bpm, song.keysig 
FROM song 
INNER JOIN (
    SELECT song_id 
    FROM song_genre 
    WHERE genre_id IN ('25')
) genre1 ON genre1.song_id = song.song_id 
INNER JOIN (
    SELECT song_id 
    FROM song_production 
    WHERE production_id IN ('8')
) production1 ON production1.song_id = song.song_id 
WHERE approved='1' 
ORDER by song.priority DESC, song.song_id DESC 
LIMIT 0, 40

ORDER BYクエリを実行すると、0.01秒で実行が破棄されます。

使用しているJOINSが原因で、問題が情報のカウント方法に関連している可能性があることを理解しています。クエリをネストする必要があるかもしれませんが、100%ではありません。

idselect_typeテーブルタイプpossible_keyskeykey_len ref rows Extra
 1PRIMARY<派生3>ALLNULL NULL NULLNULL321一時的なものを使用します。filesortの使用
 1PRIMARY<派生2>ALLNULL NULL NULLNULL3424結合バッファーの使用
 1 PRIMARY song eq_ref PRIMARY PRIMARY 4Production1.song_id1使用場所
 3派生song_productionrefPRIMARY PRIMARY4339インデックスの使用
 2派生song_genreインデックスNULLPRIMARY8 NULL3424whereを使用します。インデックスの使用

song

CREATE TABLE `song` (
`song_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`title` tinytext,
`length` varchar(5) DEFAULT NULL,
`Type` varchar(1) DEFAULT NULL,
`Vocals` varchar(10) DEFAULT NULL,
`Themes` varchar(10) DEFAULT NULL,
`Explicit` varchar(10) DEFAULT NULL,
`timesig` varchar(3) DEFAULT NULL,
`keysig` varchar(250) NOT NULL,
`bpm` int(3) DEFAULT NULL,
`speed` varchar(7) DEFAULT NULL,
`Era` varchar(10) DEFAULT NULL,
`Language` varchar(10) DEFAULT NULL,
`Keywords` varchar(10) DEFAULT NULL,
`description` mediumtext,
`search_description` longtext NOT NULL,
`key` varchar(25) NOT NULL,
`priority` int(2) NOT NULL,
`approved` int(1) NOT NULL DEFAULT '0',
PRIMARY KEY (`song_id`),
FULLTEXT KEY `description` (`description`),
FULLTEXT KEY `search_description` (`search_description`),
FULLTEXT KEY `title` (`title`),
FULLTEXT KEY `head_desc` (`title`,`search_description`)
) ENGINE=MyISAM 
  AUTO_INCREMENT=1388 
  DEFAULT CHARSET=utf8 ;

song_genre

CREATE TABLE `song_genre` (
`genre_id` int(10) NOT NULL,
`song_id` int(10) NOT NULL, 
PRIMARY KEY (`genre_id`,`song_id`)
) ENGINE=MyISAM 
  DEFAULT CHARSET=latin1 ;

song_production

CREATE TABLE `song_production` (
`production_id` int(10) NOT NULL,
`song_id` int(10) NOT NULL,
PRIMARY KEY (`production_id`,`song_id`)
) ENGINE=MyISAM 
  DEFAULT CHARSET=latin1 ;
4

3 に答える 3

2

最初のステップ:

、、は列であり、ではないgenre_idのでproduction_id、のような数値を引用符で囲むのは無駄です。それらの引用を解除します。approvedintegerchar'25'25

を削除するとどうなりDISTINCTますか?(テーブルの構造、主キー、外部キー、および一意の制約がないとわかりません)。結果に複数の同一の行が表示されますか?いいえの場合は、を削除してDISTINCTください。はいの場合、それを削除して追加しGROUP BY song.song_idます:

SELECT song.song_id, song.title, song.length, song.bpm, song.keysig 
FROM song 
  INNER JOIN (
    SELECT song_id 
    FROM song_genre 
    WHERE genre_id IN (25)
  ) genre1 ON genre1.song_id = song.song_id 
  INNER JOIN (
    SELECT song_id 
    FROM song_production 
    WHERE production_id IN (8)
  ) production1 ON production1.song_id = song.song_id 
WHERE approved = 1 

----- GROUP BY song.song_id           --- not needed at all (with these tables)
                                                        --- (and structure)
ORDER BY song.priority DESC, song.song_id DESC 
LIMIT 0, 40 ;

2番目のステップ:

有用なインデックスを追加します。のインデックス(approved, priority, song_id)はクエリに役立つ場合があります。

また、song.song_id列はUNSIGNED INTwhileとして定義され、song_genre.song_idおよびsong_production.song_idはとして定義されていSIGNED INTます。それらも変換していただければ幸いですUNSIGNED INT

また、(一意の)インデックスを追加(song_id, genre_id)(song_id, production_id)ます。これらはこのクエリには役立たないかもしれませんが、他の状況では間違いなくそのようなインデックスが必要になります。


3番目のステップ:

他の方法でクエリを書き直してみてください。たとえば、派生テーブルがない場合:

SELECT song.song_id, song.title, song.length, song.bpm, song.keysig 
FROM song 
  INNER JOIN
    song_genre AS genre1 
        ON genre1.song_id = song.song_id 
  INNER JOIN 
    song_production AS production1
        ON production1.song_id = song.song_id 
WHERE song.approved = 1
  AND genre1.genre_id IN (25)
  AND production1.production_id IN (8)
ORDER BY song.priority DESC
       , song.song_id DESC 
LIMIT 0, 40 ;

またはとEXISTS

SELECT song.song_id, song.title, song.length, song.bpm, song.keysig 
FROM song 
WHERE song.approved = 1
  AND EXISTS
      ( SELECT *
        FROM song_genre AS genre1 
        WHERE genre1.song_id = song.song_id 
          AND genre1.genre_id IN (25)
      ) 
  AND EXISTS
      ( SELECT *
        FROM song_production AS production1
        WHERE production1.song_id = song.song_id
          AND production1.production_id IN (8)
      )
ORDER BY song.priority DESC
       , song.song_id DESC 
LIMIT 0, 40 ;

どちらがより高速に実行されるかをテストします。

于 2012-05-28T18:22:15.140 に答える
1

MySQLはORDER BY制限を適用する前にすべての行で実行するため、songテーブルが大きく、適切にインデックスが作成されていない場合でも、速度は低下します。クエリを高速化するために使用できるいくつかの方法について、MySQLPerformanceBlogに投稿があります。ORDER BY ... LIMIT

サブクエリは少し不要であることに注意してください。結合がそれを処理します。次のようにクエリを書き直すことができます。

SELECT DISTINCT song.song_id, song.title, song.length, song.bpm, song.keysig 
FROM song 
JOIN song_genre g
  ON g.song_id = song.song_id 
JOIN song_production p
  ON p.song_id = song.song_id 
WHERE approved='1' 
  AND g.genre_id IN ('25')
  AND p.production_id IN ('8')
ORDER by priority DESC, song_id DESC 
LIMIT 0, 40

DISTINCTまた、id / title / length / bpm / keysigの値がまったく同じである複数の曲を作成できる場合を除いて、SELECTでが必要かどうかさえわかりません。

于 2012-05-28T18:22:38.063 に答える
-1

わかりました、クエリを書き直します:

    SELECT DISTINCT song.song_id, song.title, song.length, song.bpm, song.keysig
FROM song
    INNER JOIN (
        SELECT song_id
        FROM song_genre
        WHERE genre_id LIKE '%'
    ) genre1
        ON genre1.song_id = song.song_id
    INNER JOIN (
        SELECT song_id
        FROM song_production
        WHERE production_id IN ('5')
    ) production1
        ON production1.song_id = song.song_id
WHERE approved='1'
ORDER by song.priority DESC, song.song_id DESC
LIMIT 0, 40

まず、インデックスにsong_idを持つテーブルsongに順序を適用します。そして私も優先権を願っています。そうでない場合は、これにインデックスを追加すると、順序は一時テーブルとファイルソートを通過しません。

順序付けのテーブルを指定しない場合、MySQLは順序付けを行うために間違ったテーブルを選択する可能性があります。

于 2012-05-28T17:45:26.680 に答える