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

MySQLで累積合計をフェッチするための最適なクエリ

    変数を使用できます-どの結合よりもはるかに高速です:

    SELECT
        id,
        size,
        @total := @total + size AS cumulativeSize,
    FROM table, (SELECT @total:=0) AS t;
    

    これは、Debian5.0を実行している128MBのRAMを搭載したPentiumIIIでの簡単なテストケースです。

    テーブルを作成します:

    DROP TABLE IF EXISTS `table1`;
    
    CREATE TABLE `table1` (
        `id` int(11) NOT NULL auto_increment,
        `size` int(11) NOT NULL,
        PRIMARY KEY  (`id`)
    ) ENGINE=InnoDB;
    

    20,000個のランダムな数字で埋める:

    DELIMITER //
    DROP PROCEDURE IF EXISTS autofill//
    CREATE PROCEDURE autofill()
    BEGIN
        DECLARE i INT DEFAULT 0;
        WHILE i < 20000 DO
            INSERT INTO table1 (size) VALUES (FLOOR((RAND() * 1000)));
            SET i = i + 1;
        END WHILE;
    END;
    //
    DELIMITER ;
    
    CALL autofill();
    

    行数を確認してください:

    SELECT COUNT(*) FROM table1;
    
    +----------+
    | COUNT(*) |
    +----------+
    |    20000 |
    +----------+
    

    累積合計クエリを実行します:

    SELECT
        id,
        size,
        @total := @total + size AS cumulativeSize
    FROM table1, (SELECT @total:=0) AS t;
    
    +-------+------+----------------+
    |    id | size | cumulativeSize |
    +-------+------+----------------+
    |     1 |  226 |            226 |
    |     2 |  869 |           1095 |
    |     3 |  668 |           1763 |
    |     4 |  733 |           2496 |
    ...
    | 19997 |  966 |       10004741 |
    | 19998 |  522 |       10005263 |
    | 19999 |  713 |       10005976 |
    | 20000 |    0 |       10005976 |
    +-------+------+----------------+
    20000 rows in set (0.07 sec)
    

    更新

    元の質問でgroupIdによるグループ化を見逃していたので、確かに少し注意が必要でした。次に、一時テーブルを使用するソリューションを作成しましたが、それが気に入らなかったので、面倒で非常に複雑でした。私は立ち去ってさらに調査を行い、はるかに簡単で高速なものを思いつきました。

    これについてすべてのクレジットを主張することはできません。実際、これは行番号をエミュレート 一般的なMySQLクエリ から 。

    美しくシンプル、エレガント、そして非常に高速です:

    SELECT fileInfoId, groupId, name, size, cumulativeSize
    FROM (
        SELECT
            fileInfoId,
            groupId,
            name,
            size,
            @cs := IF(@prev_groupId = groupId, @cs+size, size) AS cumulativeSize,
            @prev_groupId := groupId AS prev_groupId
        FROM fileInfo, (SELECT @prev_groupId:=0, @cs:=0) AS vars
        ORDER BY groupId
    ) AS tmp;
    

    外側のSELECT ... AS tmpを削除できます prev_groupIDを気にしない場合 返される列。それがないと、わずかに速く動作することがわかりました。

    簡単なテストケースは次のとおりです。

    INSERT INTO `fileInfo` VALUES
    ( 1, 3, 'name0', '10'),
    ( 5, 3, 'name1', '10'),
    ( 7, 3, 'name2', '10'),
    ( 8, 1, 'name3', '10'),
    ( 9, 1, 'name4', '10'),
    (10, 2, 'name5', '10'),
    (12, 4, 'name6', '10'),
    (20, 4, 'name7', '10'),
    (21, 4, 'name8', '10'),
    (25, 5, 'name9', '10');
    
    SELECT fileInfoId, groupId, name, size, cumulativeSize
    FROM (
        SELECT
            fileInfoId,
            groupId,
            name,
            size,
            @cs := IF(@prev_groupId = groupId, @cs+size, size) AS cumulativeSize,
            @prev_groupId := groupId AS prev_groupId
        FROM fileInfo, (SELECT @prev_groupId := 0, @cs := 0) AS vars
        ORDER BY groupId
    ) AS tmp;
    
    +------------+---------+-------+------+----------------+
    | fileInfoId | groupId | name  | size | cumulativeSize |
    +------------+---------+-------+------+----------------+
    |          8 |       1 | name3 |   10 |             10 |
    |          9 |       1 | name4 |   10 |             20 |
    |         10 |       2 | name5 |   10 |             10 |
    |          1 |       3 | name0 |   10 |             10 |
    |          5 |       3 | name1 |   10 |             20 |
    |          7 |       3 | name2 |   10 |             30 |
    |         12 |       4 | name6 |   10 |             10 |
    |         20 |       4 | name7 |   10 |             20 |
    |         21 |       4 | name8 |   10 |             30 |
    |         25 |       5 | name9 |   10 |             10 |
    +------------+---------+-------+------+----------------+
    

    20,000行のテーブルの最後の数行のサンプルを次に示します。

    |      19481 |     248 | 8CSLJX22RCO | 1037469 |       51270389 |
    |      19486 |     248 | 1IYGJ1UVCQE |  937150 |       52207539 |
    |      19817 |     248 | 3FBU3EUSE1G |  616614 |       52824153 |
    |      19871 |     248 | 4N19QB7PYT  |  153031 |       52977184 |
    |        132 |     249 | 3NP9UGMTRTD |  828073 |         828073 |
    |        275 |     249 | 86RJM39K72K |  860323 |        1688396 |
    |        802 |     249 | 16Z9XADLBFI |  623030 |        2311426 |
    ...
    |      19661 |     249 | ADZXKQUI0O3 |  837213 |       39856277 |
    |      19870 |     249 | 9AVRTI3QK6I |  331342 |       40187619 |
    |      19972 |     249 | 1MTAEE3LLEM | 1027714 |       41215333 |
    +------------+---------+-------------+---------+----------------+
    20000 rows in set (0.31 sec)
    


    1. オフセット制限のあるクエリの選択が遅すぎる

    2. SQLクエリのBETWEEN句のExcelセルで日付を使用する

    3. リモートホストからpostgresに接続できません

    4. SQL再帰CTE:プロパティによってリンクされたオブジェクトの検索