这里有一个真正的头颅。以下是我的两个测试表来说明:
CREATE TABLE IF NOT EXISTS `atest` (
`id` int(10) unsigned NOT NULL DEFAULT '0',
`testval` double DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `testval` (`testval`)
) ENGINE=MyISAM DEFAULT CHARSET=UTF8;
CREATE TABLE IF NOT EXISTS `atestb` (
`id` bigint(16) NOT NULL DEFAULT '0',
`lookup` bigint(16) NOT NULL,
PRIMARY KEY (`id`),
KEY `lookup` (`lookup`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
表中有太多记录要粘贴数据值,但您可以在此处下载包含数据的表: https://dl.dropboxusercontent.com/u/2301830/lookuptest.sql
我的问题:我必须在atestb.lookup中找到第一个记录,该记录大于或等于atest.testval中atest中每条记录的值。
尝试了几种不同的方法之后,得出这个结果的最快方法是:
SELECT a.id, a.testval,
(SELECT lookup
FROM atestb
WHERE lookup >= a.testval
ORDER BY lookup LIMIT 1)
AS closest
FROM atest a
这样既有效又快速。但问题是我需要使用此查询来创建新表。在这种情况下,它是一个临时表,但我已经尝试使用永久表并获得相同的结果。如果我跑:
CREATE TEMPORARY TABLE tmptbl
SELECT a.id, a.testval,
(SELECT lookup
FROM atestb
WHERE lookup >= a.testval
ORDER BY lookup LIMIT 1)
AS closest
FROM atest a
它只是无限期挂起“发送数据......”而且我必须终止这个过程。我也尝试创建表,然后使用INSERT INTO TABLE tmptbl,我得到相同的结果。
问题显然在于子查询派生列,因为如果我将其留下或替换不同的值,它就可以工作。如果atest中只有少量记录,它也有效,但实际表有45K记录。
如果有另一种方法在没有子查询的情况下进行查询,那也没关系,但是这样的话:
SELECT a . * , MIN( b.lookup )
FROM `atest` a
LEFT JOIN atestb b ON b.lookup >= a.testval
GROUP BY a.id
由于联接在> =上,...也非常低效。所以它也挂了。我的子查询效果很好......但它不会插入到表中。
EXPLAIN的结果(无论是普通的SELECT还是INSERT ... SELECT都是一样的):
id | select_type |table |type | possible_keys | key | key_len | ref | rows | Extra
-------------------------------------------------------------------------------------------------------------------------------------
1 | PRIMARY | a | ALL | NULL | NULL | NULL | NULL |45886 | NULL
2 | DEPENDENT SUBQUERY | atestb | index | lookup | lookup | 8 | NULL | 1 | Range checked for each record (index map: 0x2); Using index`
知道这里发生了什么吗?