notepad.exe

つまり覚え書き

DBでテーブルのカラムにindexを張るとどれくらい変わるか調べる

SQLをいじっていて気になったので試してみました。 データベースはMySQLを使用しています。

使用するデータはこちらのサイト にあるsubmissionデータのcsvファイルです。

事前設定

csvファイルからデータを読み込むためにmysqlの設定を変更します。 デフォルトではファイルからの読み込みはセキュリティの問題上拒否されるようになっているのでこれを許可します。 my.cnfに以下の設定を追加します。

[mysqld]
loose-local-infile = 1

設定を書いたらサーバを再起動してMySQLにログインします。 ここでもログインコマンドにオプションを設定してログインします。

 $ mysql -u root -p --local-infile=1

データベース、テーブルの作成、データのロードを行います。SQL文は以下の通りです。 テーブル定義は元のデータに合わせて作っていて、idに主キーを設定した状態にします。

CREATE DATABASE IF NOT EXISTS atcoder;
USE atcoder;

DROP TABLE IF EXISTS submissions;
CREATE TABLE submissions (
    id             BIGINT        NOT NULL,
    epoch_second   BIGINT        NOT NULL,
    problem_id     VARCHAR (255) NOT NULL,
    contest_id     VARCHAR (255) NOT NULL,
    user_id        VARCHAR (255) NOT NULL,
    language       VARCHAR (255) NOT NULL,
    point          DOUBLE        NOT NULL,
    length         INT           NOT NULL,
    result         VARCHAR (255) NOT NULL,
    execution_time INT,

    PRIMARY KEY (id)
);

LOAD DATA 
    LOCAL INFILE './atcoder_submissions.csv' 
    INTO TABLE submissions 
    FIELDS TERMINATED BY ',' 
    LINES TERMINATED BY '\n'
    IGNORE 1 LINES;

何も考えずにクエリを発行してみる

まずはそもそもデータが何件あるか取得します。

mysql> select count(*) from submissions;
+----------+
| count(*) |
+----------+
|  3498339 |
+----------+
1 row in set (0.68 sec)

約350万件ですね。

全体のAC数を数えます

mysql> select count(*) from submissions where result='AC';
+----------+
| count(*) |
+----------+
|  1742578 |
+----------+
1 row in set (0.82 sec)

AC数が多いユーザ(重複なし)の上位10名を出してみます。

mysql> select user_id, count(distinct problem_id) as accepted_sum 
    -> from submissions 
    -> group by user_id order by accepted_sum desc limit 10;
+------------+--------------+
| user_id    | accepted_sum |
+------------+--------------+
| kmjp       |         2197 |
| ei13333    |         1930 |
| beet       |         1880 |
| latte0119  |         1810 |
| E869120    |         1652 |
| uwi        |         1573 |
| MAK_culcul |         1540 |
| square1001 |         1510 |
| leafmoon   |         1455 |
| mamekin    |         1426 |
+------------+--------------+
10 rows in set (48.57 sec)

流石に時間かかりますね。

カラムにindexを付ける

ユーザIDの絡むに対してindexをつけます

alter table submissions add index user_id(user_id)

そして同じクエリを実行

mysql> select user_id, count(distinct problem_id) as accepted_sum 
    -> from submissions 
    -> group by user_id order by accepted_sum desc limit 10;
+------------+--------------+
| user_id    | accepted_sum |
+------------+--------------+
| kmjp       |         2197 |
| ei13333    |         1930 |
| beet       |         1880 |
| latte0119  |         1810 |
| E869120    |         1652 |
| uwi        |         1573 |
| MAK_culcul |         1540 |
| square1001 |         1510 |
| leafmoon   |         1455 |
| mamekin    |         1426 |
+------------+--------------+
10 rows in set (22.88 sec)

半分以下になりましたね。

今回はテーブルが一つですが複数テーブルを使うクエリで適切にindexを設定すればより効果が大きく感じられると思います。