2

行の順序を満たすためにインデックスを使用できない場合、LIMITが小さい(つまり、一度に20行)ORDER BY句をすばやく返すにはどうすればよいですか?

テーブル「ノード」(以下に簡略化)から特定の数のタイトルを取得したいとします。ちなみに私はMySQLを使っています。

node_ID INT(11) NOT NULL auto_increment,
node_title VARCHAR(127) NOT NULL,
node_lastupdated INT(11) NOT NULL,
node_created INT(11) NOT NULL

ただし、返される行を特定のユーザーがアクセスできる行のみに制限する必要があります。多くのユーザーが多数のノードにアクセスしています。私はこの情報を大きなルックアップテーブルで事前に計算しています(物事を簡単にするための試み)。主キーは両方の列をカバーし、行の存在はユーザーグループがそのノードにアクセスできることを意味します。

viewpermission_nodeID INT(11) NOT NULL,
viewpermission_usergroupID INT(11) NOT NULL

したがって、私のクエリには次のようなものが含まれています

FROM
  node
  INNER JOIN viewpermission ON
    viewpermission_nodeID=node_ID
    AND viewpermission_usergroupID IN (<...usergroups of current user...>)

...また、GROUP BYまたはDISTINCTを使用して、ユーザーの「ユーザーグループ」の2つが両方ともそのノードにアクセスできる場合でも、ノードが1回だけ返されるようにします。

私の問題は、返される行が他のviewpermissionテーブルの値に依存するため、作成日または最終更新日で結果を並べ替えるORDERBY句がインデックスを使用する方法がないように見えることです。

したがって、MySQLは、条件に一致するすべての行を検索してから、それらをすべて自分で並べ替える必要があります。特定のユーザーに100万行があり、たとえば、最新の100行または最後の更新順に並べられた100〜200行を表示する場合、DBは、ユーザーが表示できる100万行を把握して並べ替える必要があります。この100行を返す前に、この結果セット全体がそれ自体になりますよね?

これを回避するための創造的な方法はありますか?私は次のように考えてきました:

  • どういうわけか、ビューパーミッションルックアップテーブルに日付を追加して、日付とパーミッションを含むインデックスを作成できるようにします。それは私が推測する可能性です。

編集:簡略化された質問

おそらく、次のように書き直すことで、質問を単純化できます。

このクエリを書き直したり、次のインデックスを作成して、インデックスを使用して順序付けを行うことができるようにする方法はありますか(行を選択するだけではありません)?

SELECT nodeid
FROM lookup
WHERE
  usergroup IN (2, 3)
GROUP BY
  nodeid

(usergroup)のインデックスを使用すると、WHERE部分をインデックスで満たすことができますが、GROUP BYは、これらの行に一時テーブルとファイルソートを強制します。WHERE句には、最初の列としてusergroupを持つインデックスが必要なため、(nodeid)のインデックスは私には何もしません。(usergroup、nodeid)のインデックスは、GROUP BYが変更される可能性のあるインデックスの最初の列ではないため、一時テーブルとファイルソートを強制します。

解決策はありますか?

4

4 に答える 4

3

自分の質問に答えることができますか?

私が説明したことを行う唯一の方法は、ルックアップ テーブルに、ユーザーがメンバーになりたい可能性のあるユーザー グループのすべての可能な組み合わせの行を含めることであることがわかったと思います。

これを行う代わりに、単純化された例を選択するには:

SELECT id FROM ids WHERE groups IN(1,2) ORDER BY id

行の選択と順序付けの両方にインデックスを使用する必要がある場合は、その IN(1,2) を抽象化して、範囲ではなく定数にする必要があります。つまり、次のようになります。

SELECT id FROM ids WHERE grouplist='1,2' ORDER BY id

もちろん、文字列 '1,2' を使用する代わりに、外部キーなどを使用することもできます。ポイントは、グループごとだけでなく、複数のグループの組み合わせごとに行を作成する必要があることです。

だから、私の答えがあります。

とにかく、私のアプリケーションでは、各ノードのユーザーグループのすべての可能な組み合わせのルックアップを維持することは価値がないと感じています。私の目的では、ほとんどのノードがほとんどのユーザーに表示されると予測しているため、フィルタリングでそれほどインデックスを必要としないため、単に GROUP BY でインデックスを使用するだけで問題ないと思います。

言い換えれば、元のクエリに対して採用するアプローチは、次のようなものになる可能性があります。

SELECT
    <fields>
FROM
  node
  INNER JOIN viewpermission ON
    viewpermission_nodeID=node_ID
    AND viewpermission_usergroupID IN (<...usergroups of current user...>)
  FORCE INDEX(node_created_and_node_ID)
GROUP BY
  node_created, node_ID

GROUP BY は、インデックスがインデックスの左端の列から始まり、処理される最初の非 const 非システム テーブルにある場合に、そのインデックスを使用できます。その後、結合はリスト全体 (既に順序付けされている) を処理し、現在のユーザーに表示されないもの (ごく一部) のみが INNER JOIN によって削除されます。

于 2009-02-26T08:14:26.560 に答える
0

注文する値をviewpermissionテーブルにコピーして、インデックスに追加します。

トリガーを使用して、他のテーブルの値を維持できます。

于 2009-02-26T03:01:17.870 に答える
0
select * from
(
select *
FROM  node  
INNER JOIN viewpermission 
ON    viewpermission_nodeID=node_ID    
AND viewpermission_usergroupID IN (<...usergroups of current user...>)
) a
order by a.node_lastupdated desc

内部クエリは、フィルタリングされたサブセットを提供します。これは、セット全体よりも大幅に小さいと私は理解しています。小さい方だけを並べ替える必要があります。

于 2009-02-26T03:05:33.743 に答える
0

GROUP BYORDER BYを同じクエリで使用すると、MySQL には問題があります。これによりファイルソートが発生し、おそらくパフォーマンスに対する最大のペナルティです。

の代わりに非相関サブクエリを使用することで、 DISTINCT(または)の必要性をなくすことができます。GROUP BYJOIN

SELECT * FROM node
WHERE node_id IN (
  SELECT viewpermission_nodeID
  FROM viewpermission
  WHERE viewpermissiong_usergroupID IN ( <...usergroups...> )
)
ORDER BY node_lastupdated DESC
LIMIT 100;

は と同じDISTINCTであるため、サブクエリでソートまたは実行する必要はありません。IN (1, 1, 2, 3)IN (1, 3, 2)

node_idMySQL は特定のクエリでテーブルごとに 1 つのインデックスしか使用できないことに注意してくださいnode_lastupdated。両方を使用することはできません。複合インデックスを作成したとしても、この場合は役に立ちません。

でさまざまなソリューションを分析することを忘れないでくださいEXPLAIN

于 2009-02-26T03:44:37.503 に答える