【问题标题】:Retrieving closest date to input date检索最接近输入日期的日期
【发布时间】:2019-04-17 21:59:25
【问题描述】:

我有以下功能:

DELIMITER $$
DROP FUNCTION IF EXISTS f_prevpricedate;
CREATE FUNCTION f_prevpricedate (id CHAR(8), startdate DATE)
RETURNS DATE

BEGIN

DECLARE prevpricedate DATE;

SELECT MAX(f.p_date) INTO prevpricedate
FROM fp_v2_fp_basic_prices AS f 
WHERE f.fsym_id = id AND f.p_date<startdate;

RETURN prevpricedate; 

END$$

这基本上只是返回最接近输入日期的日期(前一个日期)。但它运行得非常慢,因为表非常大。

有人知道如何优化吗?

【问题讨论】:

  • fp_v2_fp_basic_prices.p_date 是否已编入索引? explain select ... 说什么?
  • @Schwern 我不确定在这种情况下索引是什么意思。我 2 天前开始使用 SQL。
  • 索引对于高效的 SQL 非常重要。您的代码在第 2 天令人印象深刻。
  • @Schwern 谢谢。我仍然觉得我很难真正理解这些起伏,但我觉得你的评论令人鼓舞!

标签: mysql


【解决方案1】:

首先,查看explain select ... 所说的内容。

fp_v2_fp_basic_prices.fsym_idfp_v2_fp_basic_prices.p_date 是否已编入索引?索引允许数据库快速匹配和比较行,而无需查看所有行。如果没有索引,则必须比较表中的每一行。

例如...

mysql> select count(*) from foo;                                                                                                                              
+----------+
| count(*) |
+----------+
|        3 |
+----------+

mysql> explain select max(this) from foo where this < 42;
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | foo   | NULL       | ALL  | NULL          | NULL | NULL    | NULL |    3 |    33.33 | Using where |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+

这表明查询正在执行全表扫描。它没有使用键(索引),查询的类型是ALL,它认为它必须查看表中的所有 3 行。

mysql> alter table foo add index foo_this (this);
Query OK, 0 rows affected (0.07 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> explain select max(this) from foo where this < 42;
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+------------------------------+
| id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra                        |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+------------------------------+
|  1 | SIMPLE      | NULL  | NULL       | NULL | NULL          | NULL | NULL    | NULL | NULL |     NULL | Select tables optimized away |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+------------------------------+

这是在添加索引之后。 Select tables optimized away 告诉我们优化器已经发现它可以使用索引来优化整个查询。


在您的情况下,您正在搜索两列,fsym_idp_date。 MySQL 将在查询中对每个表只使用一个索引。因此,即使您在fsym_id 上有一个索引,在p_date 上有一个索引,它也只会使用一个。要使此查询执行得非常好,您需要将两者都放在一个索引中。

alter table fp_v2_fp_basic_prices add index(p_date, fsym_id);

这将适用于仅使用 p_date 的查询以及同时使用 p_date + fsym_id 的查询。因此,您不需要仅在p_date 上建立索引。但它不包括只使用fsym_id 的查询。详情请参阅this answer

另见:

【讨论】:

  • 我刚刚检查过了。该表在我尝试查找的日期编制索引。
  • @Chris 检查fsym_id。并且一定要检查explain 所说的内容,也许将其添加到您的答案中。 MySQL 每个表只能使用一个索引,因此您可能必须创建fsym_idp_date 的复合索引。见stackoverflow.com/questions/1823685/…
  • 我使用 MySQL Workbench,它显示有两个索引,它们是 fsym_id 和 p_date。
  • 当我使用解释选择时它也不会显示,因为我在另一个选择语句中调用了这个函数。基本上我在我的选择语句中我有: f_prevpriceate(p.fsym_id,p_date) AS Previous_Date
  • @Chris 如果有两个单独的索引,它只能使用其中一个来部分减少要扫描的行数。如果您为p_date, fsym_id 创建一个索引,它可以替换唯一的p_date 索引。要使用explain,请将您的SELECT MAX(f.p_date) INTO prevpricedate ... 从其函数中复制出来,并将其作为单个语句使用,直到您对其进行优化。如果您使用的是 MySQL 8,那么您想要完成的工作最好使用window function
猜你喜欢
  • 2021-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-08-01
  • 1970-01-01
  • 2011-02-07
  • 1970-01-01
  • 2018-06-04
  • 1970-01-01
相关资源
最近更新 更多