2

我最近参加了一个简单的技能测试,得到了反馈:

“有一个小的索引优化可以提高性能。”

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

4 回答 4

2

正如上面评论中所解释的,INDEX(Dob)没有使用 - 因为这是year-month-day的索引。您必须在month-day创建索引。

可能不是最优雅的解决方案,但是:

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

于 2013-08-04T20:58:52.797 回答
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
于 2013-08-05T07:22:16.343 回答
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());

并不是说其他​​方法不起作用,或者我的例子是错误的,而是速度方面——至少在我看来,这个提议的方法是最快的。如果您有兴趣;这个解决方案是由一位拥有 20 年编程经验的资深 SQL 资深人士兼 CEO 提供的。

于 2013-08-06T15:18:08.903 回答
-1

我会考虑几个选项。

创建一个列并选择 date_diff( dob, interval - year( dob) YEARS) 这给出了一个日期 0000-08-04,您可以轻松查询。您可以使用触发器使新列保持同步。

而不是使用日期类型。使用一个字符(10)。当列 try 已更改时,将列更新为 REVERSE( dob)。然后,您可以非常快速地查询日期和月份,同时将其保留在 1 列中并保留年份。这具有保留 1 列和所有信息的优点

使用一些数学——尽管没有想到任何方法。我确定有一些

于 2013-08-04T21:21:52.283 回答