TransactionHistory表:
CREATE TABLE `TransactionHistory` (
`id` varchar(200) NOT NULL,
`transactionType` varchar(200) DEFAULT NULL,
`startDate` bigint(20) DEFAULT NULL,
`completionDate` bigint(20) DEFAULT NULL,
`userId` varchar(200) DEFAULT NULL,
`status` varchar(200) DEFAULT NULL,
`error_code` varchar(200) DEFAULT NULL,
`transactioNumber` varchar(200) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `transactioNumber_index` (`transactioNumber`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
用户表:
CREATE TABLE `User` (
`userId` varchar(200) NOT NULL,
`name` varchar(200) DEFAULT NULL,
PRIMARY KEY (`userId`),
KEY `userId_index` (`userId`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
情景:
我提出了这个问题:
SELECT tx.id,
CASE WHEN COUNT(*) = 1 THEN transactionType ELSE '' END as transactionType,
CASE WHEN COUNT(*) = 1 THEN status ELSE (
CASE WHEN COUNT(CASE WHEN STATUS = 'SUCCESS' THEN 1 END) = 0 THEN 'FAILED'
WHEN COUNT(CASE WHEN STATUS = 'FAILED' THEN 1 END) = 0 THEN 'SUCCESS'
ELSE 'WARNING' END) END as status,
CASE WHEN COUNT(*) = 1 THEN error_code ELSE (
CASE WHEN COUNT(CASE WHEN STATUS = 'SUCCESS' THEN 1 END) = 0 THEN '99'
WHEN COUNT(CASE WHEN STATUS = 'FAILED' THEN 1 END) = 0 THEN '0'
ELSE '-1' END) END as status
MAX(completionDate) as completionDate,
MIN(startDate) as startDate,
a.userId, a.name,
transactioNumber
FROM TransactionHistory tx LEFT JOIN User a ON tx.userId = a.userId
GROUP BY transactioNumber
LIMIT 0, 20 //pagination
但是,如果我需要添加过滤,则查询需要很长时间才能完成。我读过在GROUP BY而不是HAVING之前放置WHERE过滤器会更快,但我不能正确过滤status和error_code,因为WARNING和-1值只出现在GROUP BY之后
HAVING STATUS = 'WARNING'
此外,如果我需要计算分组条目的总数,则需要很长时间。
我的EXPLAIN显示以下内容
select_type: SIMPLE
table: tx
type: ALL
possible_keys: NULL
key_len: NULL
ref: NULL
rows: 1140654
Extra: Using temporary; Using filesort
select_type: SIMPLE
table: e
type: eq_ref
possible_keys: PRIMARY,id_index
key_len: 202
ref: db.tx.userId
rows: 1
Extra: Using where
答案 0 :(得分:1)
SUM(STATUS = 'SUCCESS')
可以缩短为
WHERE
这些必须按此顺序编写,并按以下顺序执行:GROUP BY
,HAVING
,HAVING
。您正确地发现,WHERE
无法转换为COUNT(*)
。
此外,如果我需要计算分组条目的总数,则需要很长时间。
我不知道你的意思 - 你多次使用transactioNumber
。
id
与GROUP BY
的关系是否为1:1?如果没有,则ORDER BY
无效。
您没有LIMIT
,因此(技术上),EXPLAIN SELECT ...
定义不明确。
运行JOIN
以查看优化程序如何执行查询。
这是一种可能有用的技术 - 推迟User
。首先,从查询中删除所有提及SELECT
的内容。然后,将SELECT z.id,
z.transactionType,
...
a.userId, a.name,
z.transactioNumber
FROM ( SELECT id,
IF(COUNT(*) = 1, transactionType, '') as transactionType,
...
FROM TransactionHistory
GROUP BY transactioNumber
ORDER BY transactioNumber
LIMIT 0, 20
) z
LEFT JOIN User a ON z.userId = a.userId
子查询设为:
JOIN
这样,WHERE
只会发生20次,而不会在TransactionHistory中每行发生一次。
修改强>
如果没有GROUP BY
子句,优化器将查找有助于ORDER BY
的索引。如果GROUP BY
与GROUP BY
相同,那么它可以同时执行ORDER BY
和ORDER BY
。如果它们不同,则ORDER BY
将成为单独的排序步骤。
具有混合方向的startdate DESC, transactionType ASC
(例如startdate DESC, transactionType DESC
)永远不能使用索引。它注定需要一个tmp表和排序。使用GROUP BY
(两个DESC)可能会更好地工作,而不会过多地改变语义。
如果优化器无法使用的索引 ORDER BY
和LIMIT
,那么它必须收集所有行并对其进行排序在应用INDEX
之前。
对于1140654行,您希望尝试查询并让ORDER BY
让优化程序在EXPLAIN
中完成所有操作 - 这样它只需要查看20行,而不是1140654. My pagination blog进入其中一些。
GROUP BY
可能会说“使用临时,使用filesort”。这可能适用于ORDER BY
和/或 GROUP BY
。但是,这隐藏了需要两个排序的情况,一个用于ORDER BY
,一个用于EXPLAIN FORMAT=JSON
。 var currentUser = PFUser.currentUser()
var email = currentUser["email"]
确实在需要多种排序时明确说明。
仍然,“filesort”不是邪恶的。真正的性能杀手需要使用1140654行而不是20行。