sql >> データベース >  >> RDS >> Mysql

最小/最大フィールドを持つMySQLデータベースからレコードをプルするための標準的な方法は何ですか?

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

    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', '[email protected]', 99, 4),
        (13428721960000, 'Li', 'Kai Li', '1979-02-15', '[email protected]', 12, 2),
        (13428722180001, 'Zhang', 'Xi Xiong', '1993-11-09', '[email protected]', 5, 5),
        (13428739950000, 'Zhou', 'Ji Hai', '1991-06-06', '[email protected]', 234, 1),
        (13428739950001, 'Pan', 'Yao', '1992-05-12', '[email protected]', 43, 2),
        (13428740010001, 'Jin', 'Denny', '1994-06-02', '[email protected]', 198, 3),
        (13428740010002, 'Li', 'Fonzie', '1991-02-02', '[email protected]', 75, 3),
        (13428743370000, 'Ma', 'Haggar', '1991-08-16', '[email protected]', 47, 4),
        (13428743590001, 'Ren', 'Jenny', '1990-03-29', '[email protected]', 5, 2),
        (13428774040000, 'Chen', 'Dragon', '1999-04-12', '[email protected]', 23, 5),
        (13428774260001, 'Wang', 'Doctor', '1996-09-30', '[email protected]', 1, 5),
        (13430100000000, 'Chanz', 'Heyvery', '1994-04-04', '[email protected]', 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 |
    

    ほぼ同じ。どちらのクエリもインデックスを使用せず、すべての行がスキャンされます。ここで、列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 |
    

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

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

    これがお役に立てば幸いです。




    1. SQL Server ROWCOUNT_BIG()

    2. PHP-While / Elseエラー?

    3. postgresでISO-8601グレゴリオ暦の日付テーブルを作成する方法

    4. MaxScaleを使用して中間MySQLまたはMariaDBマスターをBinlogサーバーに置き換える方法