0

日曜日の午後に家にいて、データベースから、クラスの順位が最も低い生徒を知りたいとします。データベース テーブルが次のようになっているとします。

+----------------+-----------+------------+------------+----------------------------+------+-------+
| uid            | last_name | first_name | dob        | email                      | rank | grade |
+----------------+-----------+------------+------------+----------------------------+------+-------+
| 13428700000001 | Smith     | John       | 1990-12-03 | ...@gmail.com              |   99 |     4 |
| 13428721960000 | Li        | Kai Li     | 1979-02-15 | ...@miryclay.com           |   12 |     2 |
| 13428722180001 | Zhang     | Xi Xiong   | 1993-11-09 | ...@163.com                |    5 |     5 |
| 13428739950000 | Zhou      | Ji Hai     | 1991-06-06 | ...@msn.com                |  234 |     1 |
| 13428739950001 | Pan       | Yao        | 1992-05-12 | ...@email.com              |   43 |     2 |
| 13428740010001 | Jin       | Denny      | 1994-06-02 | ...@yahoo.com              |  198 |     3 |
| 13428740010002 | Li        | Fonzie     | 1991-02-02 | ...@hotmail.com            |   75 |     3 |
| 13428743370000 | Ma        | Haggar     | 1991-08-16 | ...@haggars.com            |   47 |     4 |
| 13428743590001 | Ren       | Jenny      | 1990-03-29 | ...@email.com              |    5 |     2 |
| 13428774040000 | Chen      | Dragon     | 1999-04-12 | ...@aol.com                |   23 |     5 |
| 13428774260001 | Wang      | Doctor     | 1996-09-30 | ...@yahoo.com              |    1 |     5 |
| 13430100000000 | Chanz     | Heyvery    | 1994-04-04 | ...@gmail.com              |  107 |     2 |
+----------------+-----------+------------+------------+----------------------------+------+-------+

経由でこれを行うことができますSELECT * FROM students WHERE rank = (SELECT MAX(rank) FROM students);が、これが最も簡単で最も一般的な方法ですか?

4

1 に答える 1

4

この方法も珍しくありません。

SELECT s1.*
FROM students s1
LEFT JOIN students s2 ON s1.rank < s2.rank
WHERE s2.uid IS NULL;

LEFT JOIN は、s1.rank が最大値の場合、それより大きい値を持つ s2.rank は存在せず、s2 行の値は NULL になることに基づいて機能します。

しかし、私はあなたのやり方が最も一般的で理解しやすい方法だと思います。

編集:なぜそれが遅いのかという質問について:

このクエリのパフォーマンスは、「どれだけ慎重に作成するか」に依存します。あなたのデータを例に取ります:

drop table if exists students;
CREATE TABLE students
    (`uid` bigint, `last_name` varchar(5), `first_name` varchar(8), `dob` varchar(10), `email` varchar(16), `rank` int, `grade` int)
;

INSERT INTO students
    (`uid`, `last_name`, `first_name`, `dob`, `email`, `rank`, `grade`)
VALUES
    (13428700000001, 'Smith', 'John', '1990-12-03', '...@gmail.com', 99, 4),
    (13428721960000, 'Li', 'Kai Li', '1979-02-15', '...@miryclay.com', 12, 2),
    (13428722180001, 'Zhang', 'Xi Xiong', '1993-11-09', '...@163.com', 5, 5),
    (13428739950000, 'Zhou', 'Ji Hai', '1991-06-06', '...@msn.com', 234, 1),
    (13428739950001, 'Pan', 'Yao', '1992-05-12', '...@email.com', 43, 2),
    (13428740010001, 'Jin', 'Denny', '1994-06-02', '...@yahoo.com', 198, 3),
    (13428740010002, 'Li', 'Fonzie', '1991-02-02', '...@hotmail.com', 75, 3),
    (13428743370000, 'Ma', 'Haggar', '1991-08-16', '...@haggars.com', 47, 4),
    (13428743590001, 'Ren', 'Jenny', '1990-03-29', '...@email.com', 5, 2),
    (13428774040000, 'Chen', 'Dragon', '1999-04-12', '...@aol.com', 23, 5),
    (13428774260001, 'Wang', 'Doctor', '1996-09-30', '...@yahoo.com', 1, 5),
    (13430100000000, 'Chanz', 'Heyvery', '1994-04-04', '...@gmail.com', 107, 2)
;

クエリの説明は次のようになります。

| ID | SELECT_TYPE |    TABLE | TYPE | POSSIBLE_KEYS |    KEY | KEY_LEN |    REF | ROWS |       EXTRA |
-------------------------------------------------------------------------------------------------------
|  1 |     PRIMARY | students |  ALL |        (null) | (null) |  (null) | (null) |   12 | Using where |
|  2 |    SUBQUERY | students |  ALL |        (null) | (null) |  (null) | (null) |   12 |             |

このような私のクエリからのもの:

| ID | SELECT_TYPE | TABLE | TYPE | POSSIBLE_KEYS |    KEY | KEY_LEN |    REF | ROWS |       EXTRA |
----------------------------------------------------------------------------------------------------
|  1 |      SIMPLE |    s1 |  ALL |        (null) | (null) |  (null) | (null) |   12 |             |
|  1 |      SIMPLE |    s2 |  ALL |        (null) | (null) |  (null) | (null) |   12 | Using where |

ほぼ同じ。どちらのクエリもインデックスを使用せず、すべての行がスキャンされます。次に、 column にインデックスを追加しますrank

drop table if exists students;
CREATE TABLE students
    (`uid` bigint, `last_name` varchar(5), `first_name` varchar(8), `dob` varchar(10), `email` varchar(16), `rank` int, `grade` int
    , key rankkey(rank)
    )
;

クエリからの説明:

| ID | SELECT_TYPE |    TABLE |   TYPE | POSSIBLE_KEYS |     KEY | KEY_LEN |    REF |   ROWS |                        EXTRA |
-----------------------------------------------------------------------------------------------------------------------------
|  1 |     PRIMARY | students |    ref |       rankkey | rankkey |       5 |  const |      1 |                  Using where |
|  2 |    SUBQUERY |   (null) | (null) |        (null) |  (null) |  (null) | (null) | (null) | Select tables optimized away |

対私の:

| ID | SELECT_TYPE | TABLE | TYPE | POSSIBLE_KEYS |    KEY | KEY_LEN |    REF | ROWS |       EXTRA |
----------------------------------------------------------------------------------------------------
|  1 |      SIMPLE |    s1 |  ALL |        (null) | (null) |  (null) | (null) |   12 |             |
|  1 |      SIMPLE |    s2 |  ALL |       rankkey | (null) |  (null) | (null) |   12 | Using where |

あなたのクエリはインデックスを使用していますが、私のものは使用していません。

ここで、主キーをテーブルに追加します。

drop table if exists students;
CREATE TABLE students
    (`uid` bigint, `last_name` varchar(5), `first_name` varchar(8), `dob` varchar(10), `email` varchar(16), `rank` int, `grade` int
    , key rankkey(rank)
    , primary key(uid)
    );

あなたの質問から説明してください:

| ID | SELECT_TYPE |    TABLE |   TYPE | POSSIBLE_KEYS |     KEY | KEY_LEN |    REF |   ROWS |                        EXTRA |
-----------------------------------------------------------------------------------------------------------------------------
|  1 |     PRIMARY | students |    ref |       rankkey | rankkey |       5 |  const |      1 |                  Using where |
|  2 |    SUBQUERY |   (null) | (null) |        (null) |  (null) |  (null) | (null) | (null) | Select tables optimized away |

そして私から:

| ID | SELECT_TYPE | TABLE |  TYPE | POSSIBLE_KEYS |     KEY | KEY_LEN |    REF | ROWS |                                EXTRA |
-------------------------------------------------------------------------------------------------------------------------------
|  1 |      SIMPLE |    s1 |   ALL |        (null) |  (null) |  (null) | (null) |   12 |                                      |
|  1 |      SIMPLE |    s2 | index |       rankkey | rankkey |       5 | (null) |   12 | Using where; Using index; Not exists |

このようにして、それらはおそらく同等に高速です。そして、これはクエリとテーブルが通常どのように構築されるかです。すべてのテーブルには主キーが必要です。ランク列でクエリ フィルタリングを頻繁に実行している場合は、もちろんインデックスを作成する必要があります。なのでほとんど違いはありません。それが一意のインデックスおよび/またはクラスター化されたインデックスである場合、テーブルにある行の数にすべて依存します。しかし、それは今では少し行き過ぎです。ただし、この例では、検査される行数に違いがあることに注意してください。小さなデータでは違いはありませんが、大きなデータ ボリュームでは確かに違いがあります。しかし (!) この動作は、インデックスに応じて、両方のクエリで変わる可能性があります。

クエリを書いた人が間違いを犯したらどうしますか? 彼が次のように書いたらどうでしょう。

SELECT s1.*
FROM students s1
LEFT JOIN students s2 ON s1.rank < s2.rank
WHERE s2.last_name IS NULL;

クエリは引き続き機能し、有効ですが、

| ID | SELECT_TYPE | TABLE | TYPE | POSSIBLE_KEYS |    KEY | KEY_LEN |    REF | ROWS |       EXTRA |
----------------------------------------------------------------------------------------------------
|  1 |      SIMPLE |    s1 |  ALL |        (null) | (null) |  (null) | (null) |   12 |             |
|  1 |      SIMPLE |    s2 |  ALL |       rankkey | (null) |  (null) | (null) |   12 | Using where |

ここでもインデックスは使用されません。

主キーをもう一度削除して、次のようにクエリを記述したらどうなるでしょうか。

SELECT s1.*
FROM students s1
LEFT JOIN students s2 ON s1.rank < s2.rank
WHERE s2.rank IS NULL;

| ID | SELECT_TYPE | TABLE |  TYPE | POSSIBLE_KEYS |     KEY | KEY_LEN |    REF | ROWS |                    EXTRA |
-------------------------------------------------------------------------------------------------------------------
|  1 |      SIMPLE |    s1 |   ALL |        (null) |  (null) |  (null) | (null) |   12 |                          |
|  1 |      SIMPLE |    s2 | index |       rankkey | rankkey |       5 | (null) |   12 | Using where; Using index |

インデックスが再び使用されます。

結論:どちらのクエリも、正しく実行されれば、同じように高速に実行されるはずです。インデックスがランク列にある限り、あなたのものは高速です。インデックスを念頭に置いて書かれている場合、私のものにも同じことが当てはまります。

お役に立てれば。

于 2013-06-28T08:59:56.937 に答える