【问题标题】:Why Index (on primary key column) is not used?为什么不使用索引(在主键列上)?
【发布时间】:2018-02-20 13:28:21
【问题描述】:

我有一个日期表,其中有一列 date (PK)。 CREATE 脚本在这里:

CREATE TABLE date_table (
  date                DATE
 ,year                INT(4)
 ,month               INT(2)
 ,day                 INT(2)
 ,month_pad           VARCHAR(2)
 ,day_pad             VARCHAR(2)
 ,month_name          VARCHAR(10)
 ,year_month_index    INT(6)
 ,year_month_hypname  VARCHAR(7)
 ,year_month_name     VARCHAR(15)
 ,week_day_index      INT(1)
 ,day_name            VARCHAR(9)
 ,week                INT(2)
 ,week_interval       VARCHAR(13)
 ,weekend_fl          INT(1)
 ,quarter_num         INT(1)
 ,quarter_num_pad     VARCHAR(2)
 ,quarter_name        VARCHAR(2)
 ,year_quarter_index  INT(6)
 ,year_quarter_name   VARCHAR(7)
 ,PRIMARY KEY (date)
);

现在我想从该表中选择具有动态值的行,例如使用LAST_DAY()DATE_SUB(DATE_FORMAT(SYSDATE(),'%Y-01-01'), INTERVAL X YEAR) 等。

当我的一个查询失败并且没有在 30 秒内执行时,我就知道有问题了,看起来原因是主键列上的索引没有使用。这是我的结果(很抱歉使用图像而不是复制查询,但我认为它足够简洁,并且查询足够短/简单):

首先,奇怪的是BETWEEN 与使用>=<= 的工作方式不同。其次,看起来索引仅用于常量值。如果你仔细观察,你可以看到在右侧(使用了>=<=),它显示了~9K 行,这是表中行的一半(表大约有~18k 行,日期从 2000-01-01 到 `2050-12-31)。

【问题讨论】:

  • 如果您将LAST_DAY(SYSDATE()) 存储在一个变量中并在查询中使用它会有所不同吗?
  • 你知道什么是主键吗?
  • 日期是reserved/key word。也许引擎变得混乱。要么以不同的方式命名列,要么确保将其包装在反引号中。
  • @Andrew:好吧,它有效,但不幸的是我正在为 SSAS 在 SSDT 中创建一个模型,看起来我不能使用变量:(。
  • @MonkeySupersonic - 在 MySQL 中(与其他一些产品不同),PRIMARY KEY 是唯一的,是一个索引,用于唯一标识每一行以用于事务目的。也就是说,PK 可以并将用作索引如果优化器确定它是最优的。

标签: mysql indexing primary-key between


【解决方案1】:

SYSDATE() 返回它执行的时间。这与 NOW() 的行为不同,后者返回一个常数时间,指示语句开始执行的时间。 (在存储的函数或触发器中,NOW() 返回函数或触发语句开始执行的时间。)

-- https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_sysdate

也就是说,优化器不会将此视为“常数”。否则,优化器会急切地评估任何“常量表达式”,然后尝试利用知道该值的优势。

另请参阅sysdate_is_now 选项。

底线:不要将SYSDATE() 用于正常的日期时间使用;使用NOW()CURDATE()

【讨论】:

    【解决方案2】:

    看起来如果我使用CURRENT_DATE()(或NOW())而不是SYSDATE(),它就可以工作。这两个查询:

    SELECT *
      FROM date_table t
     WHERE 1 = 1
       AND t.ddate >= LAST_DAY(CURRENT_DATE()) AND t.ddate <= LAST_DAY(CURRENT_DATE());
    
    SELECT *
      FROM date_table t
     WHERE 1 = 1
       AND t.ddate >= LAST_DAY(NOW()) AND t.ddate <= LAST_DAY(NOW());
    

    给出同样的结果,就是这样:

    我会接受我的答案作为解决方案,但我仍在寻找解释。我认为这可能与SYSDATE() 不是DATE 有关,但NOW() 也不是DATE...

    编辑:忘了补充,BETWEEN 也如我所见。

    【讨论】:

    • 不都是DATETIME吗?或者这些只是DATE
    • dev.mysql.com/doc/refman/5.5/en/date-and-time-functions.html - NOW() 和 SYSDATE() 是 DATETIME,但 CURRENT_DATE() 只是一个 DATE。我不会说我理解它(而且你甚至不能将 SYSDATE() 转换为 DATE,它不起作用),但是……至少它起作用了。
    猜你喜欢
    • 2019-02-08
    • 2018-06-21
    • 2021-10-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-06
    • 1970-01-01
    • 2017-10-10
    相关资源
    最近更新 更多