0

こんにちは、読んでくれてありがとう

私のサイトには、スコア、ユーザー名、IP アドレスを最も重要な列として保存するクイズ機能があります。私は現在、必要な基準に基づいてハイスコアを取り戻す恐ろしい一連のビューを持っています...

最低スコアが最初に表示されますが、各クイズ ユーザーの最低スコアのみが表示されます。

複雑なのは、ユーザーが IP を変更した場合、つまり、同じユーザー名を保持しているが IP が異なる場合、またはユーザーが同じ IP アドレスを保持しているがユーザー名を変更した場合です。

例で説明する方が簡単です。

  • 最初の訪問者には 4 つのエントリがありますが、3 つの異なる IP アドレスからのものです
  • 2 つの IP アドレスからの 2 番目のユーザー
  • 1 つの IP アドレスを使用するが 3 つのユーザー名を使用する 3 番目のユーザー

VALUES(UserID、IPA、スコア) を持つテーブル

  • ユーザー 1、IP1、13
  • ユーザー 1、IP1、20
  • ユーザー 1、IP2、30
  • ユーザー 1、IP3、10
  • ユーザー 2、IP4、20
  • ユーザー 2、IP5、22
  • ユーザー 2、IP5、15
  • ユーザー 3、IP6、12
  • ユーザー 3、IP6、20
  • ユーザー 4、IP6、15
  • ユーザー 5、IP6、11

ハイスコ​​アクエリはあなたに

  1. ユーザー 1、IP3、10
  2. ユーザー 5、IP6、11
  3. ユーザー 2、IP5、15

スコア値が重複する可能性は非常に低いですが、可能だと思います。上記の図は、私の難問を説明するために単純化されています。

私のテーブルは現在15,000レコードを超えており、ビューがきしむので、誰でもこれらの重複を削除する効率的な方法を提案できますか?

どうもありがとう。

4

1 に答える 1

3

重複するタプルの発生を特定するの(UserID,IPA)は非常に簡単です。

SELECT s.UserID
     , s.IPA
  FROM mytable s
 GROUP
    BY s.UserID
     , s.IPA
HAVING COUNT(1) > 1

最低スコアを取得するMIN(s.Score)には、選択リストに追加できます。

重複を削除することは、一意性の保証がないように見えるという点で、もう少し困難です。除外したい行を別のテーブルにコピーしてから、名前を変更してテーブルを交換するか、元のテーブルを切り捨てて新しいテーブルから再ロードすることをお勧めする人もいます。(通常、これが最も効率的なアプローチであることが判明します。)

CREATE TABLE newtable LIKE mytable ;

INSERT INTO newtable (UserID,IPA,Score)
SELECT s.UserID
     , s.IPA
     , MIN(Score) AS Score
  FROM mytable s
 GROUP
    BY s.UserID
     , s.IPA ;

UserID だけで重複を識別したい場合は、同じアプローチが機能します。IPA 値がスコアが最も低い行から取得されることが重要でない場合は、少し簡単です。ユーザーのスコアが最も低い行を取得するクエリをまとめることができます。


各行に一意の識別子 (AUTO_INCREMENT id 列など) を追加せずに、既存のテーブルから行を削除することもできます。

これにより、最低スコアよりも高いスコアを持つ特定の (UserID,IPA) のすべての行が削除され、途中で取得されます。

DELETE t.*
  FROM mytable t
  JOIN ( SELECT s.UserID
              , s.IPA
              , MIN(s.Score)
           FROM mytable s
          GROUP
             BY s.Userid
              , s.IPA
       ) k
    ON k.UserID = t.UserID
   AND k.IPA = t.IPA
   AND k.Score < t.Score

(UserID,IPA,Score)しかし、それでも重複するタプルの重複が残ります。テーブルに行を一意にする他の列がないと、重複を削除するのが少し難しくなります。(ここでも、保持したい行を別のテーブルにコピーし、テーブルを交換するか、保存された行から元のテーブルをリロードするという手法が一般的です。


ファローアップ

ビューは一時的な MyISAM テーブル (MySQL はそれらを「派生テーブル」と呼びます) として実体化されるため、MySQL ではビュー (ストアド ビューとインライン ビューの両方) のパフォーマンスが高くなる可能性があることに注意してください。

しかし、相関サブクエリは、大規模なセットではさらに問題になる可能性があります。

だから、あなたの毒を選んでください。

テーブルにインデックスがある場合ON (userID, Score, IPA)、結果セットを取得する方法は次のとおりです。

SELECT IF(@prev_user=t.UserID,@i:=@i+1,@i:=1) AS seq
     , @prev_user := t.UserID AS UserID
     , t.IPA
     , t.Score
  FROM mytable t
  JOIN (SELECT @i := NULL, @prev_user := NULL) i
 GROUP
    BY t.UserID ASC
     , t.Score ASC
     , t.IPA ASC
HAVING seq = 1

これは、いくつかの MySQL 固有の機能を利用しています: user_variables と、GROUP BY がソートされた結果セットを返すという保証です。(EXPLAIN の出力には "Using index" と表示されます。これは、並べ替え操作を回避することを意味しますが、クエリは引き続き派生テーブルを作成します。user_variables を使用して、各 UserID の "最初の" 行を識別し、HAVING 句によってすべての行が削除されます。しかし、その最初の行。


テストケース:

create table mytable (UserID VARCHAR(6), IPA varchar(3), Score INT);
create index mytable_IX ON mytable (UserID, Score, IPA);
insert into mytable values ('User 1','IP1',13)
,('User 1','IP1',20)
,('User 1','IP2',30)
,('User 1','IP3',10)
,('User 2','IP4',20)
,('User 2','IP5',22)
,('User 2','IP5',15)
,('User 3','IP6',12)
,('User 3','IP6',20)
,('User 4','IP6',15)
,('User 5','IP6',11);

別のフォローアップ

結果セットから 'User 4' と 'User 5' を削除するには (なぜそれをしたいのか、またはそれを行う必要があるのか​​ はまったく明らかではありません。それらのユーザーがテーブルに 1 行しかないためである場合は、JOIN を追加することができます次のように、複数の行がある UserID 値のリストを取得するサブクエリ (インライン ビュー):

SELECT IF(@prev_user=t.UserID,@i:=@i+1,@i:=1) AS seq
     , @prev_user := t.UserID AS UserID
     , t.IPA
     , t.Score
  FROM mytable t
  JOIN ( SELECT d.UserID
           FROM mytable d
          GROUP
             BY d.UserID
         HAVING COUNT(1) > 1
       ) m
    ON m.UserID = t.UserID
 CROSS
  JOIN (SELECT @i := NULL, @prev_user := NULL) i
 GROUP
    BY t.UserID ASC
     , t.Score ASC
     , t.IPA ASC
HAVING seq = 1
于 2013-01-26T23:58:03.470 に答える