2

私の質問。パフォーマンスを低下させるサブクエリがあると思いますが、それを証明することはできません。JOINを使用した最初の試みは失敗しました。誰かがより高性能なソリューションを提供したり、これが実際にそのまま受け入れられることを確認したりできますか?

2つのテーブルがあります。1つはtodo-list(joblist)を含み、もう1つはすべてのユーザーの進行状況(userprogress)を追跡します。仕事はビデオを見ることはできますが、そうではありません。(eラーニングのサイトです。)

ビデオが視聴されると、列挙型フィールドで自動的に「終了」に設定されます。ユーザーは手動でビデオをスキップすることもできます(ステータス='スキップ')。

表の構造を以下に示します。

ユーザーがまったく視聴していない(userprogressにレコードがない)または視聴を開始した(status ='begin')最初のビデオを取得するには、このクエリを使用しています。

選択または順序付けに使用されているフィールドにインデックスを設定しました。しかし、それらがすべて必要かどうかはわかりません。

SELECTステートメントには2つの部分があります

  1. 表示またはスキップされたすべてのビデオをフェッチする内部副選択
  2. (1)で見つかったものの中にない最初のビデオをフェッチするメインステートメント

SQLインジェクションを回避するために、PHP(:email)には名前付きパラメーターがあります。

SELECT jl.where_to_do_it FROM joblist AS jl
INNER JOIN userprogress AS up
ON (jl.joblistID = up.joblistID)
WHERE jl.what_to_do = 'video'
     AND jl.joblistID NOT IN
      (
        SELECT injl.joblistID
        FROM joblist AS injl
        INNER JOIN userprogress AS inup
        ON (injl.joblistID = inup.joblistID)
        WHERE
             (inup.status = 'finished' OR inup.status = 'skipped')
          AND
             inup.email = :email
          AND
             injl.what_to_do = 'video'
      )
ORDER BY jl.joborder ASC
LIMIT 0,1

これはEXPLAINからの出力であり、理解するのに助けが必要です。

id select_type  table     type  possible_keys                 key         key_len  ref          rows   Extra
1  PRIMARY      jl        ref   PRIMARY,what_to_do            what_to_do  602      const        9      Using where; Using filesort
1  PRIMARY      up        ref   joblistID                     joblistID   3        jl.joblistID 1      Using index
2  DEP-SUB      injl  eq_ref    PRIMARY,what_to_do            PRIMARY     3        func         1      Using where
2  DEP-SUB      inup  eq_ref    nodup,email,joblistID,status  nodup       455      const,func   1      Using where

テーブルの作成コマンド:

CREATE TABLE IF NOT EXISTS `joblist` (
  `joblistID` mediumint(10) unsigned NOT NULL AUTO_INCREMENT,
  `what_to_do` varchar(200) COLLATE utf8_swedish_ci NOT NULL,
  `where_to_do_it` varchar(100) COLLATE utf8_swedish_ci NOT NULL,
  `joborder` mediumint(6) NOT NULL,
  `track` enum('fast','slow','bonus') COLLATE utf8_swedish_ci NOT NULL DEFAULT 'slow',
  `chapter` tinyint(11) unsigned NOT NULL COMMENT 'What book chapter it relates to',
  PRIMARY KEY (`joblistID`),
  KEY `nodupjobs` (`joborder`,`chapter`),
  KEY `what_to_do` (`what_to_do`),
  KEY `where_to_do_it` (`where_to_do_it`),
  KEY `joborder` (`joborder`),
  KEY `track` (`track`),
  KEY `chapter` (`chapter`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 COLLATE=utf8_swedish_ci COMMENT='Suggested working order';


CREATE TABLE IF NOT EXISTS `userprogress` (
  `upID` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `email` varchar(150) COLLATE utf8_swedish_ci NOT NULL COMMENT 'user id',
  `joblistID` mediumint(9) unsigned NOT NULL COMMENT 'foreign key',
  `progressdata` varchar(300) COLLATE utf8_swedish_ci DEFAULT NULL COMMENT 'JSON object describing progress',
  `percentage_complete` tinyint(3) unsigned DEFAULT NULL,
  `status` enum('begun','skipped','finished') COLLATE utf8_swedish_ci DEFAULT 'begun',
  `lastupdate` datetime NOT NULL,
  `approved` datetime DEFAULT NULL,
  PRIMARY KEY (`upID`),
  UNIQUE KEY `nodup` (`email`,`joblistID`),
  KEY `email` (`email`),
  KEY `joblistID` (`joblistID`),
  KEY `status` (`status`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 COLLATE=utf8_swedish_ci COMMENT='Keep track of what the user has done';
4

2 に答える 2

2

はい。それで合っています。INとNOTINは、mysqlで特にパフォーマンスが悪いです。改訂版は次のとおりです。

SELECT jl.where_to_do_it
FROM joblist jl INNER JOIN
     userprogress up
     ON (jl.joblistID = up.joblistID)
 WHERE jl.what_to_do = 'video' and
       not exists (
           (SELECT 1
            FROM joblist injl INNER JOIN
                 userprogress inup
                 ON (injl.joblistID = inup.joblistID)
            WHERE (inup.status = 'finished' OR inup.status = 'skipped') and
                  inup.email = :email and
                  injl.what_to_do = 'video' and
                  ini1.joblistid = j1.joblistid
          )
ORDER BY jl.joborder ASC
LIMIT 0,1
于 2012-08-17T01:09:21.120 に答える
1

サークルで実行しているように見えます...サブクエリは、ステータスが終了またはスキップされたビデオを探しています。次に、そのステータスを持たないものを探している外側のクエリで、このような条件に変更します

SELECT jl.where_to_do_it FROM joblist AS jl
INNER JOIN userprogress AS up
ON (jl.joblistID = up.joblistID)
WHERE jl.what_to_do = 'video'
 AND up.status <> 'finished' AND inup.status <> 'skipped'
 AND up.email = :email
 AND jl.what_to_do = 'video'

または、間違っていることを理解しているかもしれませんが、とにかく問題は NOT IN のようです (これを使用することはお勧めしません) 代わりに、条件のサブクエリを変更して、それと Left join を実行し、条件を追加してみてくださいAnd SQ.joblistID IS NULL

 SELECT jl.where_to_do_it FROM joblist AS jl
 INNER JOIN userprogress AS up
 ON (jl.joblistID = up.joblistID)
 LEFT JOIN (
    SELECT injl.joblistID
    FROM joblist AS injl
    INNER JOIN userprogress AS inup
    ON (injl.joblistID = inup.joblistID)
    WHERE
         (inup.status = 'finished' OR inup.status = 'skipped')
      AND
         inup.email = :email
      AND
         injl.what_to_do = 'video'
  ) SQ ON jl.joblistID = SQ.joblistID
 WHERE jl.what_to_do = 'video'
 AND SQ.joblistID IS NULL      
 ORDER BY jl.joborder ASC

しかし、私は最初のオプションがうまくいくと思います...

それが役に立てば幸い

于 2012-08-17T01:27:24.197 に答える