sql >> Database teknologi >  >> RDS >> Mysql

Optimal forespørgsel til at hente en kumulativ sum i MySQL

Du kan bruge en variabel - den er langt hurtigere end enhver joinforbindelse:

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

Her er en hurtig testcase på en Pentium III med 128 MB RAM, der kører Debian 5.0:

Opret tabellen:

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;
 

Udfyld med 20.000 tilfældige tal:

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();
 

Tjek rækkeantallet:

SELECT COUNT(*) FROM table1;

+----------+
| COUNT(*) |
+----------+
|    20000 |
+----------+
 

Kør den kumulative samlede forespørgsel:

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)
 

OPDATERING

Jeg havde savnet grupperingen efter groupId i det oprindelige spørgsmål, og det gjorde det helt sikkert en smule vanskeligere. Jeg skrev derefter en løsning, der brugte et midlertidigt bord, men jeg kunne ikke lide det - det var rodet og alt for kompliceret. Jeg tog afsted og forskede lidt mere og er kommet frem til noget langt enklere og hurtigere.

Jeg kan ikke gøre krav på al æren for dette – faktisk kan jeg næsten ikke gøre krav på noget overhovedet, da det blot er en modificeret version af Emuler rækkenummer fra Almindelige MySQL-forespørgsler .

Det er smukt enkelt, elegant og meget hurtigt:

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;
 

Du kan fjerne den ydre SELECT ... AS tmp hvis du ikke har noget imod prev_groupID kolonne returneres. Jeg fandt ud af, at den kørte marginalt hurtigere uden den.

Her er en simpel testcase:

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 |
+------------+---------+-------+------+----------------+
 

Her er et eksempel på de sidste par rækker fra en tabel med 20.000 rækker:

| 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. MySQL my.cnf fil - fundet mulighed uden forudgående gruppe

  2. pgDash-alternativer - PostgreSQL-databaseovervågning med ClusterControl

  3. Beregn antallet af poster for hver dato mellem 2 datoer

  4. Indsæt vælg MySQL med forberedte udsagn