寻找一些好的示例
前几天在 #maria IRC 频道中,有人问了我一个关于DATETIME列的问题,他们想通过年份来SELECT筛选。答案(对你们许多人来说可能很明显)是使用YEAR()函数,如下所示
select * from t1 where YEAR(d) = 2011;
(以上假设表名为“t1”,并且DATETIME列名为“d”。)
在我的回复中,我提供了 AskMonty 知识库中日期和时间函数部分的链接,但是当我查看YEAR()函数的条目时,我注意到给出的示例(源自文件scripts/fill_help_tables.sql它位于 MySQL 和 MariaDB 源代码中)帮助不大
MariaDB [(none)]> SELECT YEAR('1987-01-01'); +--------------------+ | YEAR('1987-01-01') | +--------------------+ | 1987 | +--------------------+ 1 row in set (0.00 sec) MariaDB [(none)]>
以上当然是YEAR()函数的有效用法,但它不是实际应用中的用法(至少,我不知道为什么有人会在实际应用程序中使用以上SELECT语句)。因此我添加了一个我认为更有用的示例,特别是对于不熟悉使用 SQL 函数的人来说
CREATE TABLE t1 (d DATETIME); INSERT INTO t1 VALUES ("2007-01-30 21:31:07"), ("1983-10-15 06:42:51"), ("2011-04-21 12:34:56"), ("2011-10-30 06:31:41"), ("2011-01-30 14:03:25"), ("2004-10-07 11:19:34");
MariaDB [test]> select * from t1; +---------------------+ | d | +---------------------+ | 2007-01-30 21:31:07 | | 1983-10-15 06:42:51 | | 2011-04-21 12:34:56 | | 2011-10-30 06:31:41 | | 2011-01-30 14:03:25 | | 2004-10-07 11:19:34 | +---------------------+ 6 rows in set (0.02 sec) MariaDB [test]> select * from t1 where YEAR(d) = 2011; +---------------------+ | d | +---------------------+ | 2011-04-21 12:34:56 | | 2011-10-30 06:31:41 | | 2011-01-30 14:03:25 | +---------------------+ 3 rows in set (0.09 sec)
查看日期和时间函数部分以及其他地方的其他函数条目时,发现示例的模式是很有用,但是——就像最初的YEAR()示例一样——在现实世界中却毫无用处。
我现在已经为几个日期和时间函数添加了更好的示例,但它们可能还可以进一步改进,知识库中还有更多条目也可以从更好的示例中受益。 好消息是向知识库条目添加示例非常容易。坏消息是,如果我独自思考所有示例,它们会很相似并且可能很无聊,因此我想寻求一些帮助。
如果您曾经想为 MariaDB 贡献力量,但不知道从何开始,我有一个建议:查看 AskMonty 知识库的函数和运算符部分,找到一个或两个需要更好示例的函数,然后添加一些示例(可以直接添加或通过评论添加)。如果您不熟悉编辑知识库,请不用担心。我可以在事后整理并让您的示例看起来很漂亮。只需确保您那些超棒的新示例确实有效即可。 🙂
谢谢!
您的代码 (select * from t1 where YEAR(d) = 2011) 乍一看很有用,但实际上它不应该出现在参考手册中,因为它树立了一个不好的榜样。
在 WHERE 子句中使用函数通常意味着您的查询不会使用索引。如果您的表在列“d”上有一个索引并且有 100 万行,数据库将调用 YEAR 函数 100 万次。
遗憾的是,在这种情况下高效的查询应该是
select * from t1 where d between ‘2011-01-01’ and ‘2011-12-31 23:59:59’;
最终结果将是相同的,但如果在列“d”上有索引,此查询将运行得快得多。
mariadb > select version()G
*************************** 第 1 行 ***************************
version(): 5.2.6-MariaDB
1 row in set (0.00 sec)
mariadb > alter table t1 add key(d);
Query OK, 影响 6 行 (0.00 sec)
Records: 6 Duplicates: 0 Warnings: 0
mariadb > explain select * from t1 where YEAR(d) = 2011G
*************************** 第 1 行 ***************************
id: 1
select_type: SIMPLE
table: t1
type: index
possible_keys: NULL
key: d
key_len: 9
ref: NULL
rows: 6
Extra: 使用 where; 使用索引
1 row in set (0.00 sec)
# 请注意,即使它显示“使用索引”,它实际上并没有使用。
# 尝试添加几千行,您总是会得到一个
# 对此查询进行全表扫描。
mariadb > explain select * from t1 where d between ‘2011-01-01’ and ‘2011-12-31 23:59:59’G
*************************** 第 1 行 ***************************
id: 1
select_type: SIMPLE
table: t1
type: range
possible_keys: d
key: d
key_len: 9
ref: NULL
rows: 3
Extra: 使用 where; 使用索引
1 row in set (0.00 sec)
是否不应该使用 YEAR 函数,因为它在索引列上使用会导致索引无法被使用?在某些情况下是的,但我真的认为总是优先选择性能而不是可读的、可能更容易维护的代码是一个错误。而且,如果根本没有索引,或者拥有或使用索引的好处接近于零,那么我肯定会选择更容易维护和阅读的代码。
而不用索引有时反而更快!在上面的示例中,假设我们添加另一列“c”,即
CREATE TABLE t1 (d DATETIME, c INT, KEY(d));
那么在“d”上使用索引可能就没有那么有用了。如果所有或绝大多数行的“d”列值都在 2011 年,那么全表扫描会比使用索引更有效,因为
1) 我们反正会访问大部分行。
并且
2) 列“c”无论如何都必须从数据块中获取,因为它不是索引的一部分。
这并不是说我不支持性能,但在大多数情况下,总是优先考虑性能而不是可用性是一个错误。而在那些并非如此的情况下,那么又为什么要使用 RDBMS 呢?当然,Giuseppe 展示的技巧仍然有用,当这个特定的 SQL 确实是性能瓶颈,并且在列“d”上存在可用的索引,并且数据分布使得索引能够发挥作用时。但是性能不是一切!更好的性能也并非总是更好的应用程序!但有时是的。但您通常也希望有可维护性和易用性。
/Karlsson
我可以在 Windows 上写入 mysql,但无法在 Linux 上写入 mariadb!?