我已经实现了此查询:
#include <stdio.h>
#define __type_is_void(expr) _Generic((typeof(expr)*){0}, void*:1, default:0)
#define __expr_or_zero(expr) _Generic((typeof(expr)*){0}, void*:0, default:(expr))
#define DO(expr) \
_Generic((typeof(expr)*){0}, \
void*:__DO_VOID(expr), \
default:__DO(__expr_or_zero(expr)))
#define __DO(expr) \
({ typeof(expr) __ret; puts("do nonvoid"); __ret = (expr); __ret; })
#define __DO_VOID(expr) \
(void)({ puts("do void"); (void)(expr); })
void foo(void) { }
int bar(void) { return 1; }
int main(void)
{
DO(foo());
DO(bar());
return 0;
}
事件表具有数百万条记录。列时间戳是DATETIME,其他列是INT。该表经常被访问并且具有很多索引。
开始时,此查询花费了超过10分钟的时间来执行。我通过添加新索引来解决此问题
SELECT
evt.userId, evt.storeId, COUNT(1) AS totalVisits
FROM
Event evt
WHERE
evt.timestamp BETWEEN DATE_SUB(NOW(), INTERVAL 30 DAY) AND NOW()
AND
evt.subtype = 2
AND
userID IS NOT NULL
GROUP BY userId, storeId
HAVING totalVisits>16;
这很好,我在不到2秒的时间内得到了结果。
我遇到的问题是当我更改条件“间隔30天”时。例如,如果我设置INTERVAL 50 DAY,则MYSQL不使用我创建的索引。相反,它使用仅覆盖两列的另一个索引。
说明命令:
ALTER TABLE Event
ADD INDEX `Event_timestamp_subtype_userId_storeId` (`timestamp` ASC, `subType` ASC, `userId` ASC, `storeId` ASC);
说明输出:
EXPLAIN EXTENDED SELECT
evt.userId, evt.storeId, COUNT(1) AS totalVisits
FROM
Event evt
WHERE
evt.timestamp BETWEEN DATE_SUB(NOW(), INTERVAL 50 DAY) AND NOW()
AND
evt.subtype = 2
AND
evt.userID IS NOT NULL
GROUP BY userId, storeId
HAVING totalVisits>16;
因此,如果放置50天条件,查询是不可行的。如何使该查询使用正确的索引而不依赖于参数的值?
我正在使用mysql服务器5.7.23
谢谢!
致谢
答案 0 :(得分:1)
您有“很多索引”。弹枪无济于事。您是否有这些索引,并按给定的顺序排列了列?
INDEX(subtype, timestamp)
INDEX(subtype, userID)
优化器可能希望对WHERE
使用其中任何一个。而且,由于它无法消耗所有WHERE
(由于2个范围),因此它不会到达GROUP BY
中的列。
第一列(subtype
)已通过=
测试;这很容易。
第二列是“范围”,因此这是它可以处理的最后一件事。
通过将这些索引中的每一个都变成“覆盖”索引,可以进行小的改进:
INDEX(subtype, timestamp, storeID, userID)
INDEX(subtype, userID, timestamp, storeID)
现在处理只需要查看索引的BTree,而不必在该BTree和具有数据的BTree之间反弹。
(前2列按特定顺序;其他两列可以互换。)
如果这是一个“巨大”的表(数百万行),我们可以讨论另一种优化,因为您实际上需要2D索引。