我有下一个(奇怪的)查询
SELECT DISTINCT c.id
FROM z1 INNER JOIN c c ON (z1.id=c.id)
INNER JOIN i ON (c.member_id=i.member_id)
WHERE DATE_FORMAT(CONCAT(i.birthyear,"-",i.birthmonth,"-",i.birthday),"%Y%m%d000000") BETWEEN '19820605000000' AND '19930604235959' AND c.id NOT IN (658887)
GROUP BY c.id
用户的生日在三个不同的列中保存在db中。但是这里的任务是找出年龄在特定范围内的用户的东西。
最糟糕的是,mysql将计算每个选定记录的年龄并将其与条件进行比较并且它不是很好:(有没有办法让它更快?
这是计划
+----+-------------+-------+--------+-------------------+---------+---------+--------------------+--------+----------+-----------------------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+--------+-------------------+---------+---------+--------------------+--------+----------+-----------------------------------------------------------+
| 1 | SIMPLE | z1 | index | PRIMARY | PRIMARY | 4 | NULL | 176659 | 100.00 | Using where; Using index; Using temporary; Using filesort |
| 1 | SIMPLE | c | eq_ref | PRIMARY,member_id | PRIMARY | 4 | z1.id | 1 | 100.00 | |
| 1 | SIMPLE | i | eq_ref | PRIMARY | PRIMARY | 4 | c.member_id | 1 | 100.00 | Using where |
+----+-------------+-------+--------+-------------------+---------+---------+--------------------+--------+----------+-----------------------------------------------------------+
答案 0 :(得分:3)
像往常一样,正确的答案是修复您的架构。即数据应该规范化,在可行的地方使用本机密钥并使用正确的数据类型。
看看你的帖子,至少你提供了一个EXPLAIN计划 - 但表结构也会有所帮助。
为什么查询中的表z1?您没有使用它明确过滤,也不会在任何地方使用结果。
你为什么要做一个DISTINCT和一个GROUP BY - 你要求DBMS做两次同样的工作。
为什么使用'c'作为'c'的别名?
为什么使用NOT IN排除单个值?
为什么要将日期值作为字符串进行比较?
优化器可能会对解决查询的最佳方法感到困惑 - 但是您没有提供任何信息来支持这一点 - 年龄规则过滤了多少比例的数据?使用birthday / i表来驱动查询可能会得到更好的结果:
SELECT DISTINCT c.id
FROM c
INNER JOIN i ON (c.member_id=i.member_id)
WHERE STR_TO_DATE(
CONCAT(i.birthyear,'-', i.birthmonth,'-',i.birthday)
,"%Y-%m-%d")
BETWEEN 19820605000000 AND 19930604235959
AND c.id <> 658887
AND i.birthyear BETWEEN 1982 AND 1993
答案 1 :(得分:1)
更改i
表,并添加名为TIMESTAMP
的{{1}}或DATETIME
列,其中包含date_of_birth
:
INDEX
并使用这个应该更快的查询:
ALTER TABLE i ADD date_of_birth DATETIME NOT NULL, ADD INDEX date_of_birth;
UPDATE i SET date_of_birth = CONCAT(i.birthyear,"-",i.birthmonth,"-",i.birthday);
答案 2 :(得分:1)
你让我解释一下我的意思。不幸的是,这有两个问题。
首先,我不认为这可以在一个简单的评论框中充分解释。
第二个是我真的不知道我在说什么,但我会去...
考虑以下示例 - 一个简单的实用程序表,其中包含最多2038的日期(当整个UNIX_TIMESTAMP事物停止工作时)...
CREATE TABLE calendar (
dt date NOT NULL DEFAULT '0000-00-00',
PRIMARY KEY (`dt`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
现在,以下查询在逻辑上是相同的......
SELECT * FROM calendar WHERE UNIX_TIMESTAMP(dt) BETWEEN 1370521405 AND 1370732400;
+------------+
| dt |
+------------+
| 2013-06-07 |
| 2013-06-08 |
| 2013-06-09 |
+------------+
SELECT * FROM calendar WHERE dt BETWEEN FROM_UNIXTIME(1370521405) AND FROM_UNIXTIME(1370732400);
+------------+
| dt |
+------------+
| 2013-06-07 |
| 2013-06-08 |
| 2013-06-09 |
+------------+
...而且MySQL非常聪明,可以利用(PK)索引解析两个查询(而不是阅读表本身 - yuk)。
但是,虽然第一个需要对整个索引进行全面扫描(好但不是很好),但第二个能够使用一个(或多个)值范围的密钥访问表(太棒了)...
EXPLAIN EXTENDED
SELECT * FROM calendar WHERE UNIX_TIMESTAMP(dt) BETWEEN 1370521405 AND 1370732400;
+----+-------------+----------+-------+---------------+---------+---------+------+-------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+----------+-------+---------------+---------+---------+------+-------+--------------------------+
| 1 | SIMPLE | calendar | index | NULL | PRIMARY | 3 | NULL | 10957 | Using where; Using index |
+----+-------------+----------+-------+---------------+---------+---------+------+-------+--------------------------+
EXPLAIN EXTENDED
SELECT * FROM calendar WHERE dt BETWEEN FROM_UNIXTIME(1370521405) AND FROM_UNIXTIME(1370732400);
+----+-------------+----------+-------+---------------+---------+---------+------+------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+----------+-------+---------------+---------+---------+------+------+--------------------------+
| 1 | SIMPLE | calendar | range | PRIMARY | PRIMARY | 3 | NULL | 3 | Using where; Using index |
+----+-------------+----------+-------+---------------+---------+---------+------+------+--------------------------+