0

I have the following function:

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$$

Which basically just returns the closest date (previous date) to the input date. It runs extremely slow though, since the table is very large.

Anyone have any idea how to optimize this?

Chris
  • 433
  • 4
  • 17
  • Is `fp_v2_fp_basic_prices.p_date` indexed? What does `explain select ...` say? – Schwern Nov 14 '18 at 21:00
  • @Schwern I am not sure what indexed in this context means. I started using SQL 2 days ago. – Chris Nov 14 '18 at 21:03
  • Index are very important for efficient SQL. Your code is very impressive for day 2. – Schwern Nov 14 '18 at 21:12
  • @Schwern Thank you. I still feel like I struggle to really understand the ups and downs but I find your comment encouraging! – Chris Nov 14 '18 at 21:18

1 Answers1

1

First, check what explain select ... says.

Are fp_v2_fp_basic_prices.fsym_id and fp_v2_fp_basic_prices.p_date indexed? An index allows the database to quickly match and compare rows without having to look at all of them. If there's no index it will have to compare every row in the table.

For example...

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 |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+

This shows the query is doing a full table scan. It's using no key (index), the type of query is ALL, and it thinks it has to look at all 3 rows in the table.

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 |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+------------------------------+

This is after adding an index. Select tables optimized away tells us the optimizer has figured out it can use the index to optimize away the whole query.


In your case you're searching by two columns, fsym_id and p_date. MySQL will only use one index per table in a query. So even if you have an index on fsym_id and an index on p_date it will only use one. To make this query perform very well you need both in a single index.

alter table fp_v2_fp_basic_prices add index(p_date, fsym_id);

This will work for queries that only use p_date as well as queries which use both p_date + fsym_id together. So you don't need an index on just p_date. But it does not cover queries which only use fsym_id. See this answer for more detail.

See also:

Schwern
  • 153,029
  • 25
  • 195
  • 336
  • I just checked. The table is indexed on the date I try to look up even. – Chris Nov 14 '18 at 21:22
  • @Chris Check `fsym_id` as well. And be sure to check what `explain` says, maybe add that to your answer. MySQL can only use one index per table, so you may have to create a composite index of both `fsym_id` and `p_date`. See https://stackoverflow.com/questions/1823685/when-should-i-use-a-composite-index – Schwern Nov 14 '18 at 21:25
  • I use MySQL Workbench and it shows that there are two indexes and those are fsym_id and p_date. – Chris Nov 14 '18 at 21:28
  • Also it does not show when I use Explain Select since I call this function in another select statement. Basically I in my select statement I have: f_prevpricedate(p.fsym_id,p_date) AS Previous_Date – Chris Nov 14 '18 at 21:36
  • @Chris If there are two separate indexes it can only use one of them to partially cut down on the number of rows to scan. If you make one index for `p_date, fsym_id` that can replace the lone `p_date` index. To use `explain` copy your `SELECT MAX(f.p_date) INTO prevpricedate ...` out of its function and work with it as a single statement until you've got it optimized. If you're using MySQL 8 what you're trying to accomplish might be better done with a [window function](https://dev.mysql.com/doc/refman/8.0/en/window-functions.html). – Schwern Nov 14 '18 at 21:42
  • I have to go to bed now but I will look more into it tomorrow. Is there any way I can specify what index it should use to optimize the request? So that I could ask it to use p_date as the index when performing the max since it has 2 indexes. I don't understand what you write here. (Sorry) : " If you make one index for p_date, fsym_id that can replace the lone p_date index. " – Chris Nov 14 '18 at 21:53
  • @Chris [Yes](https://stackoverflow.com/a/11731841/14660), but it can only ever use one index per table. You need an index with both columns. I'll update the answer. – Schwern Nov 14 '18 at 23:02
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/183690/discussion-between-chris-and-schwern). – Chris Nov 15 '18 at 08:39