I had a similar question open on SO for postgres - now having the same issue with mysql..
I have two tables -
Table A : 1MM rows, AsOfDate, Id, BId (foreign key to table B)
Table B : 50k rows, Id, Flag, ValidFrom, ValidTo
Table A contains multiple records per day between 2011/01/01 and 2011/12/31 across 100 BId's. Table B contains multiple non overlapping (between validfrom and validto) records for 100 Bids.
The task of the join will be to return the flag that was active for the BId on the given AsOfDate.
select
a.AsOfDate, b.Flag
from
A a inner Join B b on
a.BId = b.Id and b.ValidFrom <= a.AsOfDate and b.ValidTo >= a.AsOfDate
where
a.AsOfDate >= 20110101 and a.AsOfDate <= 20111231
This query takes over 3 minutes on a very high end server (+3Ghz) with 64Gb of memory.
+-------+-------------------------+
| Table | Create Table
|
+-------+-------------------------+
| a | CREATE TABLE `a` (
`asofdate` int(4) NOT NULL,
`bid` int(4) NOT NULL,
KEY `asofdate_bid` (`asofdate`,`bid`),
KEY `bid` (`bid`),
KEY `bid_asofdate` (`bid`,`asofdate`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 |
+-------+-------------------------+
+-------+-------------------------+
| Table | Create Table |
+-------+-------------------------+
| b | CREATE TABLE `b` (
`key` int(4) NOT NULL,
`id` int(4) NOT NULL,
`flag` char(1) NOT NULL,
`validfrom` int(4) NOT NULL,
`validto` int(4) NOT NULL,
KEY `id` (`id`),
KEY `validfrom` (`validfrom`),
KEY `validfrom_id` (`validfrom`,`id`),
KEY `id_validfrom` (`id`,`validfrom`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 |
+-------+-------------------------+
Here is the explain :
mysql> explain select count(1) from a a inner join b b on a.bid = b.id and b.validfrom <= a.asofdate and b.validto >= a.asofdate where a.asofdate >= 20120101 and a.asofdate <= 20121231;
+----+-------------+-------+------+----------------------------------------+--------------+---------+----------+-------+-----------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra
+----+-------------+-------+------+----------------------------------------+--------------+---------+----------+-------+-----------+
| 1 | SIMPLE | b | ALL | id,validfrom,validfrom_id,id_validfrom | NULL | NULL | NULL | 50510 | |
| 1 | SIMPLE | a | ref | asofdate_bid,bid,bid_asofdate | bid_asofdate | 4 | foo.b.id | 1433 | Using where; Using index |
+----+-------------+-------+------+----------------------------------------+--------------+---------+----------+-------+-----------+
SqlServer express and Postgres take ~300ms to execute the above query. I'm in the process of deciding on a multi-terabyte installation and it's not looking good for mySql (my preferred db) at the moment!
execution plan for the suggested queries
remove the join conditions (3 minutes):
mysql> EXPLAIN SELECT count(1) FROM a a
-> INNER JOIN b b ON a.bid = b.id
-> WHERE (a.asofdate >= 20120101 and a.asofdate <= 20121231)
-> AND (b.validfrom <= a.asofdate AND b.validto >= a.asofdate);
+----+-------------+-------+------+----------------------------------------+--------------+---------+----------+-------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+----------------------------------------+--------------+---------+----------+-------+--------------------------+
| 1 | SIMPLE | b | ALL | id,validfrom,validfrom_id,id_validfrom | NULL | NULL | NULL | 50510 | |
| 1 | SIMPLE | a | ref | asofdate_bid,bid,bid_asofdate | bid_asofdate | 4 | foo.b.id | 1433 | Using where; Using index |
+----+-------------+-------+------+----------------------------------------+--------------+---------+----------+-------+--------------------------+
2 rows in set (0.02 sec)
use straight_join actually changes the query plan and causes the time to go to 6 minutes:
mysql> EXPLAIN SELECT count(1) FROM a a STRAIGHT_JOIN b b ON a.bid = b.id WHERE (a.asofdate >= 20120101 and a.asofdate <= 20121231) AND (b.validfrom <= a.asofdate AND b.validto >= a.asofdate);
+----+-------------+-------+-------+----------------------------------------+--------------+---------+-----------+--------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------+----------------------------------------+--------------+---------+-----------+--------+--------------------------+
| 1 | SIMPLE | a | range | asofdate_bid,bid,bid_asofdate | asofdate_bid | 4 | NULL | 500296 | Using where; Using index |
| 1 | SIMPLE | b | ref | id,validfrom,validfrom_id,id_validfrom | id | 4 | foo.a.bid | 255 | Using where |
+----+-------------+-------+-------+----------------------------------------+--------------+---------+-----------+--------+--------------------------+
STRAIGHT_JOINinstead ofINNERto see if makes a difference. – a1ex07 Nov 8 '12 at 18:06