MySQL加入基于YEAR()索引 - 列添加或生成列

时间:2018-02-02 20:05:28

标签: mysql sql datetime mariadb

根据答案https://stackoverflow.com/a/1601812/4050261

我正在使用SQL查询,如下所示

FROM workdone
LEFT JOIN staffcost ON YEAR(workdone.date) = staffcost.costyear

上面的查询没有使用我在workdone.date列上的索引,因此非常慢。我有2个选项,我认为

选项1

添加另一列workdone.year,该列通过表oncreateonupdate事件进行更新。在查询中使用此列。

选项2

添加Generated (Virtual/Persistent)workdone.year,然后在查询中使用此列。

我的问题:

  1. 哪个选项更好?从绩效和数据的双重性角度来看?
  2. 我应该使用Virtual OR Persistent列类型吗?
  3. 还有更好的选择吗?
  4. 更新1.1

    我实现了OJones建议的解决方案,但解释说明我没有使用索引。我是否错误地阅读了以下截图? adminer Explain

3 个答案:

答案 0 :(得分:3)

你可以试试这个:

FROM workdone
LEFT JOIN staffcost ON workdone.date >= MAKEDATE(staffcost.costyear, 1)
                   AND workdone.date <  MAKEDATE(staffcost.costyear+1, 1)

这将允许在workdone.date上使用索引来搜索costyear的第一天到costyear+1的第一天之间的日期。

通常,这种范围搜索可以利用函数(例如YEAR(datestamp))可以使用的索引。

答案 1 :(得分:2)

我认为生成的列是更好的选择。不论你是否坚持它都没有区别。如果你将其编入索引,确实会有所不同。

MySQL(使用Innodb)支持虚拟列上的索引。所以,你可以做到这一点。或者保留列并使用它。

那就是说,我认为这个查询不会有太大的不同。 year选择不是高度限制性的。并且,您正在针对另一个表而不是常量执行此操作。 staffcost(costyear)上的索引似乎更重要。

答案 2 :(得分:1)

您的查询很好。但是使用LEFT JOIN的查询只能使用右表(staffcost)上的索引。左表(workdone)上没有索引可以支持连接。所以你需要的只是staffcost(costyear)上的索引。

您可以使用以下脚本对其进行测试:

DROP TABLE IF EXISTS `staffcost`;
CREATE TABLE IF NOT EXISTS `staffcost` (
  `id` int(10) unsigned NOT NULL,
  `costyear` year(4) NOT NULL,
  `data` text COLLATE utf8_unicode_ci,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

INSERT INTO `staffcost` (`id`, `costyear`, `data`) VALUES
    (1, '2018', '0.6555866465490187'),
    (2, '2019', '0.12234661925802624'),
    (3, '2020', '0.64497318737672'),
    (4, '2021', '0.8578261098431667'),
    (5, '2022', '0.354211017819318'),
    (6, '2023', '0.19757679030073508'),
    (7, '2024', '0.9252509287793663'),
    (8, '2025', '0.03352430372827156'),
    (9, '2026', '0.3918687630369037'),
    (10, '2027', '0.8587709347333489');

DROP TABLE IF EXISTS `workdone`;
CREATE TABLE IF NOT EXISTS `workdone` (
  `id` int(10) unsigned NOT NULL,
  `date` date NOT NULL,
  `data` text COLLATE utf8_unicode_ci,
  PRIMARY KEY (`id`),
  KEY `date` (`date`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

INSERT INTO `workdone` (`id`, `date`, `data`) VALUES
    (1, '2017-12-31', '0.40540353712197724'),
    (2, '2018-01-01', '0.8716141803857071'),
    (3, '2018-01-02', '0.1418603212962489'),
    (4, '2018-01-03', '0.09445909605776807'),
    (5, '2018-01-04', '0.04671454713373868'),
    (6, '2018-01-05', '0.9501954782290342'),
    (7, '2018-01-06', '0.6108337804776'),
    (8, '2018-01-07', '0.2035824984345422'),
    (9, '2018-01-08', '0.18541118147355615'),
    (10, '2018-01-09', '0.31630844279779907');

EXPLAIN
SELECT * FROM workdone
LEFT JOIN staffcost ON YEAR(workdone.date) = staffcost.costyear;

ALTER TABLE `staffcost` ADD INDEX `costyear` (`costyear`);

EXPLAIN
SELECT * FROM workdone
LEFT JOIN staffcost ON YEAR(workdone.date) = staffcost.costyear;

SELECT VERSION();

结果:

id|select_type|table    |type|possible_keys|key|key_len|ref|rows|Extra
 1|SIMPLE     |workdone |ALL |             |   |       |   |  10|
 1|SIMPLE     |staffcost|ALL |             |   |       |   |  10|Using where; Using join buffer (flat, BNL join)

id|select_type|table    |type|possible_keys|key     |key_len|ref |rows|Extra
1 |SIMPLE     |workdone |ALL |             |        |       |    |  10|
1 |SIMPLE     |staffcost|ref |costyear     |costyear|1      |func|   1|Using where

VERSION()
10.1.26-MariaDB

在线演示: http://rextester.com/JIAL51740