简单的索引优化

时间:2013-08-04 20:44:30

标签: mysql

我最近接受了一项简单的技能测试,我收到了反馈意见:

  

“有一个小的索引优化可以改进   性能“。

技能测试涉及创建生日电子贺卡在线应用程序;用户注册,然后在他们的生日那天发送电子邮件给他们。我假设这是在运行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());

4 个答案:

答案 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");

设定http://sqlfiddle.com/#!2/66111/3

答案 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列和所有信息

使用一些数学 - 虽然没有人想到任何方法。我确定有一些