0

問題

MySQL でのランキングの使用例を検討していますが、明確な「最善の解決策」はまだ決まっていません。次のようなテーブルがあります。

CREATE TABLE mytable (
  item_id int unsigned NOT NULL,
  # some other data fields,
  item_score int unsigned NOT NULL,
  PRIMARY KEY (item_id),
  KEY item_score (item_score)
) ENGINE=MyISAM;

その中には数百万のレコードがあり、最も一般的な書き込み操作は item_score を新しい値で更新することです。item_id および/またはそのスコアを指定して、そのランキングを取得する必要があります。現在、それを達成する 2 つの方法を知っています。

より高いスコアを持つ COUNT() 個のアイテム

SELECT COUNT(*) FROM mytable WHERE item_score > $foo;

行番号を割り当てる

SET @rownum := 0;
SELECT rank FROM (
    SELECT @rownum := @rownum + 1 AS rank, item_id
    FROM mytable ORDER BY item_score DESC ) AS result
WHERE item_id = $foo;

どれ?

それらは同じように動作しますか、それとも異なる動作をしますか? もしそうなら、なぜそれらは違うのですか?どちらを選ぶべきですか?

より良いアイデアはありますか?

より良い/より速いアプローチはありますか? 私が思いつくことができる唯一のものは、事前に計算されたランキングを保存するための別のテーブル/memcache/NoSQL/何でもありますが、mytable更新するたびにソートして読み取る必要があります。これは、「読み取りランク」クエリの数が更新数よりも (はるかに?) 多い場合にのみ良いアプローチになると思いますが、「読み取りランク」クエリが更新クエリの数。

4

1 に答える 1

0

テーブルにインデックスがあるため、使用する唯一のクエリは理にかなっています

-- findByScore    
SELECT COUNT(*) FROM mytable WHERE item_score > :item_score; 
-- findById
SELECT COUNT(*) FROM mytable WHERE item_score > (select item_score from mytable where item_id = :item_id); 

1 つのアイテム ID のランクのみが必要なため、findById では、パフォーマンスに関しては、対応する結合と大差ありません。

多くのアイテムのランクが必要な場合は、結合を使用することをお勧めします。

「行番号の割り当て」を使用すると、インデックスを使用しないため、ここで競合することはできません(クエリではまったく使用されず、それでもそれほど良くないことを改善する場合でも)

また、割り当てインデックスを使用するいくつかの隠れたトラップがある場合があります。同じスコアの項目が複数ある場合、最後の項目のランクが表示されます。

無関係: SQL インジェクションから保護するために、可能であれば PDO を使用してください。

于 2012-11-20T12:51:31.720 に答える