我需要想法来优化此查询:
SELECT U.Id
, U.Name
, SUM(P.Credit) AS CreditAmount
, SUM(P.Debit) AS DebitAmount
FROM users U
LEFT JOIN payments P ON P.UserId = U.Id
WHERE U.StatusId = 1 /* Active Users Only */
GROUP BY U.Id
有关表格的一些信息:
users
表:10,000+条记录payments
表:2,000,000条记录,编入UserId
列查询最多需要2分钟。通过调查查询,我发现SUM
函数是查询速度慢的原因。尝试在没有SUM
的情况下执行查询只需不到2秒。
是否有空间来改进此查询?感谢
编辑1
这是我在使用EXPLAIN
:
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE U ALL 8304 Using where; Using temporary; Using filesort
1 SIMPLE P ref UserId UserId 5 Database.U.Id 361 ""
查询中正在使用索引payments.UserId
。有什么想法吗?
编辑2 表信息:
users "CREATE TABLE `users` (
`Id` int(11) NOT NULL auto_increment,
`StatusId` int(11) default NULL,
`Name` varchar(60) default NULL,
`TimeZone` varchar(100) default NULL,
PRIMARY KEY (`Id`),
KEY `StatusId` (`StatusId`),
CONSTRAINT `FK_User_Status` FOREIGN KEY (`StatusId`) REFERENCES `userStatus` (`Id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8"
payments "CREATE TABLE `payments` (
`Id` int(11) NOT NULL auto_increment,
`UserId` int(11) default NULL,
`Description` varchar(200) default NULL,
`Debit` decimal(11,2) default NULL,
`Credit` decimal(11,2) default NULL,
`Date` datetime default NULL,
PRIMARY KEY (`Id`),
KEY `UserId` (`UserId`),
CONSTRAINT `FK_Payment_User` FOREIGN KEY (`UserId`) REFERENCES `users` (`Id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8"
答案 0 :(得分:1)
如果添加以下索引:
CREATE INDEX ix_status ON users (StatusId, Id);
CREATE INDEX ix_userid ON payments (UserId, Credit, Debit);
并添加FORCE INDEX
选项:
SELECT U.Id
, U.Name
, SUM(P.Credit) AS CreditAmount
, SUM(P.Debit) AS DebitAmount
FROM
users U FORCE INDEX (ix_status)
LEFT JOIN
payments P FORCE INDEX (ix_userid) ON P.UserId = U.Id
WHERE
U.StatusId = 1 /* Active Users Only */
GROUP BY
U.Id;
您的查询几乎肯定会表现得更好。
答案 1 :(得分:1)
Kael,这是我运行的脚本,以及它的输出,以显示我在第一个答案中提供的索引和查询,将查询时间从大约14.3秒缩短到0.75秒:
DROP TABLE IF EXISTS payments;
DROP TABLE IF EXISTS users;
DROP TABLE IF EXISTS userStatus;
CREATE TABLE userStatus (
Id int(11) NOT NULL auto_increment,
PRIMARY KEY (Id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE users (
Id int(11) NOT NULL auto_increment,
StatusId int(11) default NULL,
Name varchar(60) default NULL,
TimeZone varchar(100) default NULL,
PRIMARY KEY (Id),
KEY StatusId (StatusId),
CONSTRAINT FK_User_Status FOREIGN KEY (StatusId) REFERENCES userStatus (Id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE payments (
Id int(11) NOT NULL auto_increment,
UserId int(11) default NULL,
Description varchar(200) default NULL,
Debit decimal(11,2) default NULL,
Credit decimal(11,2) default NULL,
Date datetime default NULL,
PRIMARY KEY (Id),
KEY UserId (UserId),
CONSTRAINT FK_Payment_User FOREIGN KEY (UserId) REFERENCES users (Id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS ids;
CREATE TABLE IF NOT EXISTS ids (
id INT UNSIGNED AUTO_INCREMENT NOT NULL,
PRIMARY KEY (id)
) ENGINE=MyISAM; -- must be MyISAM
INSERT INTO ids SET id = NULL; # inserts a 1
UPDATE ids SET id=0;
ALTER TABLE ids AUTO_INCREMENT=1;
INSERT INTO ids SELECT NULL FROM ids; # 1 1
INSERT INTO ids SELECT NULL FROM ids; # 2 2
INSERT INTO ids SELECT NULL FROM ids; # 4 4
INSERT INTO ids SELECT NULL FROM ids; # 8 8
INSERT INTO ids SELECT NULL FROM ids; # 10 16
INSERT INTO ids SELECT NULL FROM ids; # 20 32
INSERT INTO ids SELECT NULL FROM ids; # 40 64
INSERT INTO ids SELECT NULL FROM ids; # 80 128
INSERT INTO ids SELECT NULL FROM ids; # 100 256
INSERT INTO ids SELECT NULL FROM ids; # 200 512
INSERT INTO ids SELECT NULL FROM ids; # 400 1,024
INSERT INTO ids SELECT NULL FROM ids; # 800 2,048
INSERT INTO ids SELECT NULL FROM ids; # 1000 4,096
INSERT INTO ids SELECT NULL FROM ids; # 2000 8,192
INSERT INTO ids SELECT NULL FROM ids; # 4000 16,384
INSERT INTO
userStatus
(id)
SELECT
id
FROM
ids
WHERE
id > 0
ORDER BY
id
LIMIT 2;
INSERT INTO
users
(Id, StatusId)
SELECT
id,
IF(RAND() < .9, 1, 2) -- 90% of users are 'Active'
FROM
ids
WHERE
id > 0 AND
id <= 20000
ORDER BY
id;
INSERT INTO
payments
(Id, UserId, Debit, Credit, `Date`)
SELECT
NULL,
i1.id,
ROUND(RAND() * 1000, 2),
ROUND(RAND() * 1000, 2),
NOW() - INTERVAL FLOOR(RAND() * 86400 * 1000) SECOND
FROM
ids i1,
ids i2
WHERE
i1.id > 0 AND
i1.id <= 20000 AND
i2.id < 100
ORDER BY
RAND();
EXPLAIN
SELECT U.Id
, U.Name
, SUM(P.Credit) AS CreditAmount
, SUM(P.Debit) AS DebitAmount
FROM users U
LEFT JOIN payments P ON P.UserId = U.Id
WHERE U.StatusId = 1 /* Active Users Only */
GROUP BY U.Id;
SELECT U.Id
, U.Name
, SUM(P.Credit) AS CreditAmount
, SUM(P.Debit) AS DebitAmount
FROM users U
LEFT JOIN payments P ON P.UserId = U.Id
WHERE U.StatusId = 1 /* Active Users Only */
GROUP BY U.Id;
SELECT U.Id
, U.Name
, SUM(P.Credit) AS CreditAmount
, SUM(P.Debit) AS DebitAmount
FROM users U
LEFT JOIN payments P ON P.UserId = U.Id
WHERE U.StatusId = 1 /* Active Users Only */
GROUP BY U.Id;
SELECT U.Id
, U.Name
, SUM(P.Credit) AS CreditAmount
, SUM(P.Debit) AS DebitAmount
FROM users U
LEFT JOIN payments P ON P.UserId = U.Id
WHERE U.StatusId = 1 /* Active Users Only */
GROUP BY U.Id;
CREATE INDEX ix_status ON users (StatusId, Id);
CREATE INDEX ix_userid ON payments (UserId, Credit, Debit);
EXPLAIN
SELECT U.Id
, U.Name
, SUM(P.Credit) AS CreditAmount
, SUM(P.Debit) AS DebitAmount
FROM
users U FORCE INDEX (ix_status)
LEFT JOIN
payments P FORCE INDEX (ix_userid) ON P.UserId = U.Id
WHERE
U.StatusId = 1 /* Active Users Only */
GROUP BY
U.Id;
SELECT U.Id
, U.Name
, SUM(P.Credit) AS CreditAmount
, SUM(P.Debit) AS DebitAmount
FROM
users U FORCE INDEX (ix_status)
LEFT JOIN
payments P FORCE INDEX (ix_userid) ON P.UserId = U.Id
WHERE
U.StatusId = 1 /* Active Users Only */
GROUP BY
U.Id;
SELECT U.Id
, U.Name
, SUM(P.Credit) AS CreditAmount
, SUM(P.Debit) AS DebitAmount
FROM
users U FORCE INDEX (ix_status)
LEFT JOIN
payments P FORCE INDEX (ix_userid) ON P.UserId = U.Id
WHERE
U.StatusId = 1 /* Active Users Only */
GROUP BY
U.Id;
SELECT U.Id
, U.Name
, SUM(P.Credit) AS CreditAmount
, SUM(P.Debit) AS DebitAmount
FROM
users U FORCE INDEX (ix_status)
LEFT JOIN
payments P FORCE INDEX (ix_userid) ON P.UserId = U.Id
WHERE
U.StatusId = 1 /* Active Users Only */
GROUP BY
U.Id;
这是输出:
[ross@titanv ~]$ mysql -vvv < ex.sql temp
--------------
DROP TABLE IF EXISTS payments
--------------
Query OK, 0 rows affected (0.01 sec)
--------------
DROP TABLE IF EXISTS users
--------------
Query OK, 0 rows affected (0.00 sec)
--------------
DROP TABLE IF EXISTS userStatus
--------------
Query OK, 0 rows affected (0.01 sec)
--------------
CREATE TABLE userStatus (
Id int(11) NOT NULL auto_increment,
PRIMARY KEY (Id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
--------------
Query OK, 0 rows affected (0.01 sec)
--------------
CREATE TABLE users (
Id int(11) NOT NULL auto_increment,
StatusId int(11) default NULL,
Name varchar(60) default NULL,
TimeZone varchar(100) default NULL,
PRIMARY KEY (Id),
KEY StatusId (StatusId),
CONSTRAINT FK_User_Status FOREIGN KEY (StatusId) REFERENCES userStatus (Id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
--------------
Query OK, 0 rows affected (0.00 sec)
--------------
CREATE TABLE payments (
Id int(11) NOT NULL auto_increment,
UserId int(11) default NULL,
Description varchar(200) default NULL,
Debit decimal(11,2) default NULL,
Credit decimal(11,2) default NULL,
Date datetime default NULL,
PRIMARY KEY (Id),
KEY UserId (UserId),
CONSTRAINT FK_Payment_User FOREIGN KEY (UserId) REFERENCES users (Id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
--------------
Query OK, 0 rows affected (0.01 sec)
--------------
DROP TABLE IF EXISTS ids
--------------
Query OK, 0 rows affected (0.00 sec)
--------------
CREATE TABLE IF NOT EXISTS ids (
id INT UNSIGNED AUTO_INCREMENT NOT NULL,
PRIMARY KEY (id)
) ENGINE=MyISAM
--------------
Query OK, 0 rows affected (0.01 sec)
--------------
INSERT INTO ids SET id = NULL
--------------
Query OK, 1 row affected (0.00 sec)
--------------
UPDATE ids SET id=0
--------------
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
--------------
ALTER TABLE ids AUTO_INCREMENT=1
--------------
Query OK, 1 row affected (0.00 sec)
Records: 1 Duplicates: 0 Warnings: 0
--------------
INSERT INTO ids SELECT NULL FROM ids
--------------
Query OK, 1 row affected (0.00 sec)
Records: 1 Duplicates: 0 Warnings: 0
--------------
INSERT INTO ids SELECT NULL FROM ids
--------------
Query OK, 2 rows affected (0.00 sec)
Records: 2 Duplicates: 0 Warnings: 0
--------------
INSERT INTO ids SELECT NULL FROM ids
--------------
Query OK, 4 rows affected (0.00 sec)
Records: 4 Duplicates: 0 Warnings: 0
--------------
INSERT INTO ids SELECT NULL FROM ids
--------------
Query OK, 8 rows affected (0.00 sec)
Records: 8 Duplicates: 0 Warnings: 0
--------------
INSERT INTO ids SELECT NULL FROM ids
--------------
Query OK, 16 rows affected (0.00 sec)
Records: 16 Duplicates: 0 Warnings: 0
--------------
INSERT INTO ids SELECT NULL FROM ids
--------------
Query OK, 32 rows affected (0.01 sec)
Records: 32 Duplicates: 0 Warnings: 0
--------------
INSERT INTO ids SELECT NULL FROM ids
--------------
Query OK, 64 rows affected (0.00 sec)
Records: 64 Duplicates: 0 Warnings: 0
--------------
INSERT INTO ids SELECT NULL FROM ids
--------------
Query OK, 128 rows affected (0.00 sec)
Records: 128 Duplicates: 0 Warnings: 0
--------------
INSERT INTO ids SELECT NULL FROM ids
--------------
Query OK, 256 rows affected (0.00 sec)
Records: 256 Duplicates: 0 Warnings: 0
--------------
INSERT INTO ids SELECT NULL FROM ids
--------------
Query OK, 512 rows affected (0.00 sec)
Records: 512 Duplicates: 0 Warnings: 0
--------------
INSERT INTO ids SELECT NULL FROM ids
--------------
Query OK, 1024 rows affected (0.00 sec)
Records: 1024 Duplicates: 0 Warnings: 0
--------------
INSERT INTO ids SELECT NULL FROM ids
--------------
Query OK, 2048 rows affected (0.00 sec)
Records: 2048 Duplicates: 0 Warnings: 0
--------------
INSERT INTO ids SELECT NULL FROM ids
--------------
Query OK, 4096 rows affected (0.01 sec)
Records: 4096 Duplicates: 0 Warnings: 0
--------------
INSERT INTO ids SELECT NULL FROM ids
--------------
Query OK, 8192 rows affected (0.01 sec)
Records: 8192 Duplicates: 0 Warnings: 0
--------------
INSERT INTO ids SELECT NULL FROM ids
--------------
Query OK, 16384 rows affected (0.02 sec)
Records: 16384 Duplicates: 0 Warnings: 0
--------------
INSERT INTO
userStatus
(id)
SELECT
id
FROM
ids
WHERE
id > 0
ORDER BY
id
LIMIT 2
--------------
Query OK, 2 rows affected (0.01 sec)
Records: 2 Duplicates: 0 Warnings: 0
--------------
INSERT INTO
users
(Id, StatusId)
SELECT
id,
IF(RAND() < .9, 1, 2)
FROM
ids
WHERE
id > 0 AND
id <= 20000
ORDER BY
id
--------------
Query OK, 20000 rows affected (0.11 sec)
Records: 20000 Duplicates: 0 Warnings: 0
--------------
INSERT INTO
payments
(Id, UserId, Debit, Credit, `Date`)
SELECT
NULL,
i1.id,
ROUND(RAND() * 1000, 2),
ROUND(RAND() * 1000, 2),
NOW() - INTERVAL FLOOR(RAND() * 86400 * 1000) SECOND
FROM
ids i1,
ids i2
WHERE
i1.id > 0 AND
i1.id <= 20000 AND
i2.id < 100
ORDER BY
RAND()
--------------
Query OK, 2000000 rows affected (21.52 sec)
Records: 2000000 Duplicates: 0 Warnings: 0
--------------
EXPLAIN
SELECT U.Id
, U.Name
, SUM(P.Credit) AS CreditAmount
, SUM(P.Debit) AS DebitAmount
FROM users U
LEFT JOIN payments P ON P.UserId = U.Id
WHERE U.StatusId = 1
GROUP BY U.Id
--------------
+----+-------------+-------+------+---------------+----------+---------+-----------+-------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+----------+---------+-----------+-------+-------------+
| 1 | SIMPLE | U | ref | StatusId | StatusId | 5 | const | 10274 | Using where |
| 1 | SIMPLE | P | ref | UserId | UserId | 5 | temp.U.Id | 1 | |
+----+-------------+-------+------+---------------+----------+---------+-----------+-------+-------------+
2 rows in set (0.00 sec)
--------------
SELECT U.Id
, U.Name
, SUM(P.Credit) AS CreditAmount
, SUM(P.Debit) AS DebitAmount
FROM users U
LEFT JOIN payments P ON P.UserId = U.Id
WHERE U.StatusId = 1
GROUP BY U.Id
--------------
+-------+------+--------------+-------------+
| Id | Name | CreditAmount | DebitAmount |
+-------+------+--------------+-------------+
| 1 | NULL | 47824.73 | 49580.71 |
| 2 | NULL | 46426.02 | 52019.69 |
...
| 19999 | NULL | 50041.10 | 47696.88 |
| 20000 | NULL | 51923.69 | 50349.38 |
+-------+------+--------------+-------------+
18057 rows in set (14.60 sec)
--------------
SELECT U.Id
, U.Name
, SUM(P.Credit) AS CreditAmount
, SUM(P.Debit) AS DebitAmount
FROM users U
LEFT JOIN payments P ON P.UserId = U.Id
WHERE U.StatusId = 1
GROUP BY U.Id
--------------
+-------+------+--------------+-------------+
| Id | Name | CreditAmount | DebitAmount |
+-------+------+--------------+-------------+
| 1 | NULL | 47824.73 | 49580.71 |
| 2 | NULL | 46426.02 | 52019.69 |
...
| 19999 | NULL | 50041.10 | 47696.88 |
| 20000 | NULL | 51923.69 | 50349.38 |
+-------+------+--------------+-------------+
18057 rows in set (14.10 sec)
--------------
SELECT U.Id
, U.Name
, SUM(P.Credit) AS CreditAmount
, SUM(P.Debit) AS DebitAmount
FROM users U
LEFT JOIN payments P ON P.UserId = U.Id
WHERE U.StatusId = 1
GROUP BY U.Id
--------------
+-------+------+--------------+-------------+
| Id | Name | CreditAmount | DebitAmount |
+-------+------+--------------+-------------+
| 1 | NULL | 47824.73 | 49580.71 |
| 2 | NULL | 46426.02 | 52019.69 |
...
| 19999 | NULL | 50041.10 | 47696.88 |
| 20000 | NULL | 51923.69 | 50349.38 |
+-------+------+--------------+-------------+
18057 rows in set (14.17 sec)
--------------
CREATE INDEX ix_status ON users (StatusId, Id)
--------------
Query OK, 0 rows affected (0.05 sec)
Records: 0 Duplicates: 0 Warnings: 0
--------------
CREATE INDEX ix_userid ON payments (UserId, Credit, Debit)
--------------
Query OK, 0 rows affected (5.09 sec)
Records: 0 Duplicates: 0 Warnings: 0
--------------
EXPLAIN
SELECT U.Id
, U.Name
, SUM(P.Credit) AS CreditAmount
, SUM(P.Debit) AS DebitAmount
FROM
users U FORCE INDEX (ix_status)
LEFT JOIN
payments P FORCE INDEX (ix_userid) ON P.UserId = U.Id
WHERE
U.StatusId = 1
GROUP BY
U.Id
--------------
+----+-------------+-------+------+---------------+-----------+---------+-----------+-------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+-----------+---------+-----------+-------+-------------+
| 1 | SIMPLE | U | ref | ix_status | ix_status | 5 | const | 10274 | Using where |
| 1 | SIMPLE | P | ref | ix_userid | ix_userid | 5 | temp.U.Id | 10002 | Using index |
+----+-------------+-------+------+---------------+-----------+---------+-----------+-------+-------------+
2 rows in set (0.00 sec)
--------------
SELECT U.Id
, U.Name
, SUM(P.Credit) AS CreditAmount
, SUM(P.Debit) AS DebitAmount
FROM
users U FORCE INDEX (ix_status)
LEFT JOIN
payments P FORCE INDEX (ix_userid) ON P.UserId = U.Id
WHERE
U.StatusId = 1
GROUP BY
U.Id
--------------
+-------+------+--------------+-------------+
| Id | Name | CreditAmount | DebitAmount |
+-------+------+--------------+-------------+
| 1 | NULL | 47824.73 | 49580.71 |
| 2 | NULL | 46426.02 | 52019.69 |
...
| 19999 | NULL | 50041.10 | 47696.88 |
| 20000 | NULL | 51923.69 | 50349.38 |
+-------+------+--------------+-------------+
18057 rows in set (0.75 sec)
--------------
SELECT U.Id
, U.Name
, SUM(P.Credit) AS CreditAmount
, SUM(P.Debit) AS DebitAmount
FROM
users U FORCE INDEX (ix_status)
LEFT JOIN
payments P FORCE INDEX (ix_userid) ON P.UserId = U.Id
WHERE
U.StatusId = 1
GROUP BY
U.Id
--------------
+-------+------+--------------+-------------+
| Id | Name | CreditAmount | DebitAmount |
+-------+------+--------------+-------------+
| 1 | NULL | 47824.73 | 49580.71 |
| 2 | NULL | 46426.02 | 52019.69 |
...
| 19999 | NULL | 50041.10 | 47696.88 |
| 20000 | NULL | 51923.69 | 50349.38 |
+-------+------+--------------+-------------+
18057 rows in set (0.75 sec)
--------------
SELECT U.Id
, U.Name
, SUM(P.Credit) AS CreditAmount
, SUM(P.Debit) AS DebitAmount
FROM
users U FORCE INDEX (ix_status)
LEFT JOIN
payments P FORCE INDEX (ix_userid) ON P.UserId = U.Id
WHERE
U.StatusId = 1
GROUP BY
U.Id
--------------
+-------+------+--------------+-------------+
| Id | Name | CreditAmount | DebitAmount |
+-------+------+--------------+-------------+
| 1 | NULL | 47824.73 | 49580.71 |
| 2 | NULL | 46426.02 | 52019.69 |
...
| 19999 | NULL | 50041.10 | 47696.88 |
| 20000 | NULL | 51923.69 | 50349.38 |
+-------+------+--------------+-------------+
18057 rows in set (0.75 sec)
Bye
答案 2 :(得分:0)
根据此link,我无法加快SUM
功能。这里的主要问题是我选择了大量的记录。
我考虑重新设计users
表,添加一个新列,例如TotalPayment
,它将保留credit
表中debit
减去payments
的数量。我知道它违反了数据库规范化规则,但我想这次重新设计是为了节省性能。
非常感谢你的帮助。