我最近接受了一项简单的技能测试,我收到了反馈意见:
“有一个小的索引优化可以改进 性能“。
技能测试涉及创建生日电子贺卡在线应用程序;用户注册,然后在他们的生日那天发送电子邮件给他们。我假设这是在运行mysql数据库的Linux服务器上,有大约400万条记录。
我已经尽力研究在数据库索引方面的进一步问题,但是根据我最好的研究和知识,我很难找到任何改进。我真的很感激这里有任何指示,所以我可以知道我哪里出错了;
数据库:
CREATE TABLE `birthdayCard`
(
`Email` VARCHAR(255),
`FirstName` CHAR(30),
`LastName` CHAR(30),
`Dob` DATE,
PRIMARY KEY (Email),
INDEX(Dob)
);
查询:
SELECT * FROM `birthdayCard`
WHERE MONTH(Dob) = MONTH(NOW())
AND DAY(Dob) = DAY(NOW());
答案 0 :(得分:2)
正如上面的评论中所解释的那样,INDEX(Dob)
未被使用 - 因为这是年 - 月 - 日的索引。您必须在月 - 日上创建索引。
可能不是最优雅的解决方案,但是:
CREATE TABLE `birthdayCard`(`Email` VARCHAR(255), `FirstName` CHAR(30), `LastName` CHAR(30),
`Mob` int, `Dob` int,
PRIMARY KEY (Email), INDEX(`Mob`, `Dob`));
请参阅http://sqlfiddle.com/#!2/db82ff/1
对于更好的(?)答案:由于MySQL不支持计算列,您可能需要触发器来填充“月 - 日”列,并在其上有索引:
CREATE TABLE `birthdayCard`(`Email` VARCHAR(255), `FirstName` CHAR(30), `LastName` CHAR(30),
`Dob` DATE,
`Birthday` CHAR(5),
PRIMARY KEY (Email), INDEX(`Birthday`));
CREATE TRIGGER ins_bithdayCard BEFORE INSERT ON `birthdayCard`
FOR EACH ROW
SET NEW.`birthday` = DATE_FORMAT(NEW.`Dob`, "%m%d");
CREATE TRIGGER upd_bithdayCard BEFORE UPDATE ON `birthdayCard`
FOR EACH ROW
SET NEW.`birthday` = DATE_FORMAT(NEW.`Dob`, "%m%d");
这允许“简单”插入,如果需要,保留原始示例中的完整Dob
:
insert into birthdayCard (Email, FirstName, LastNAme, Dob)
values ("x@y.com", "Sylvain", "Leroux", '2013-08-05');
必须修改SELECT
查询才能使用新的“搜索”列:
SELECT * FROM `birthdayCard` WHERE Birthday = DATE_FORMAT(NOW(), "%m%d");
答案 1 :(得分:0)
我不知道一个“小”的改进,但我可以想到一个大的......
索引只能用于“裸”字段,因此您当前的查询会导致昂贵的全表扫描。您应该转换WHERE表达式,以便函数调用不包含该字段:
SELECT * FROM `birthdayCard`
WHERE
Dob >= CURDATE()
AND Dob < DATE_ADD(CURDATE(), INTERVAL 1 DAY);
索引范围扫描可以满足:
ID SELECT_TYPE TABLE TYPE POSSIBLE_KEYS KEY KEY_LEN REF ROWS EXTRA
1 SIMPLE birthdayCard range Dob Dob 4 (null) 1 Using where
答案 2 :(得分:0)
我设法从公司直接收到了我的测试反馈,由于他们的回复到目前为止尚未分享,我认为我也可以选择这样做。
正如大多数人所强调的那样,问题来自DOB。根据我的解释,当查询存储为我所做日期的DOB时,查找日期和月份的查询执行类似于LIKE语句的查询。
这实际上意味着将查询存储的值1970-01-01(存储格式日期),类似于:
WHERE Dob LIKE '%01-01'
这意味着MYSQL引擎会循环遍历值中不需要的“1970-”部分。
建议的解决方案是仅存储(和索引)所需日期的部分(月,日)。一个4字符长度的整数对于此是完美的,特别是如果我们执行一个使用LEFT从左侧选择的查询和RIGHT SELECT函数。
表:
CREATE TABLE `birthdayCard`
(
`Email` VARCHAR(255),
`FirstName` CHAR(30),
`LastName` CHAR(30),
`Dob` INT(4),
PRIMARY KEY (Email),
INDEX(Dob)
);
查询:
SELECT * FROM `birthdayCard`
WHERE LEFT(Dob, 2) = MONTH(NOW())
AND RIGHT(Dob, 2) = DAY(NOW());
并不是其他方法不起作用,或者我的例子是错误的,但速度明智 - 这个提议的方法至少在我看来是最快的。如果你有兴趣;这个解决方案是由一位经验丰富的SQL资深人士和首席执行官提供的,他有20多年的编程时间。
答案 3 :(得分:-1)
我会考虑一些选择。
创建列并选择date_diff(dob
,interval - year(dob
)YEARS)
这给出了0000-08-04的日期,您可以轻松查询。您可以使用触发器使新列保持同步。
而不是使用日期类型。使用char(10)。更改列try后,将列更新为REVERSE(dob
)。然后,您可以非常快速地查询日期和月份,同时将其保留在1列并保持年份。这样做的好处是可以保留1列和所有信息
使用一些数学 - 虽然没有人想到任何方法。我确定有一些