请帮助优化长时间运行的查询(左外连接,带有2个派生表)

时间:2010-12-31 07:27:08

标签: sql mysql optimization indexing query-optimization

我需要帮助的查询是:

SELECT d.bn, d.4700, d.4500, ... , p.`Activity Description`  
FROM   
( SELECT temp.bn, temp.4700, temp.4500, ....  
FROM `tdata` temp  
GROUP BY temp.bn  
HAVING (COUNT(temp.bn) = 1) ) d   
LEFT OUTER JOIN  
( SELECT temp2.bn, max(temp2.FPE) AS max_fpe, temp2.`Activity Description`
FROM `pdata` temp2
GROUP BY temp2.bn ) p  
ON p.bn = d.bn;

......表示对解决此问题并不重要的其他字段。

问题在于第二个派生表 - 它没有使用我创建的索引,我不知道为什么,这似乎是因为TEXT字段的处理方式。第一个子查询使用我创建的索引并且运行得非常快,但是第二个子查询显示“正在使用临时;使用filesort'。请查看我在下表create语句中创建的索引。任何人都可以帮我优化吗?

通过快速解释,第一个子查询仅用于选择具有唯一bn的记录,第二个子查询看起来有点古怪(其中max函数没有在结果集中使用)确保只有连接右侧的一条记录包含在结果集中。

我的表创建语句是

CREATE TABLE `tdata` (
`BN` varchar(15) DEFAULT NULL,
`4000` varchar(3) DEFAULT NULL,
`5800` varchar(3) DEFAULT NULL,
....
KEY `BN` (`BN`),
KEY `idx_t3010`(`BN`,`4700`,`4500`,`4510`,`4520`,`4530`,`4570`,`4950`,`5000`,`5010`,`5020`,`5050`,`5060`,`5070`,`5100`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8

CREATE TABLE `pdata` (
`BN` varchar(15) DEFAULT NULL,
`FPE` datetime DEFAULT NULL,
`Activity Description` text,
....
KEY `BN` (`BN`),
KEY `idx_programs_2009` (`BN`,`FPE`,`Activity Description`(100))
) ENGINE=MyISAM DEFAULT CHARSET=utf8

谢谢!

修改

感谢Malvolio和Brian Hooper的评论。 Malvolio的建议对我不起作用,因为在两个表中都有相同bn的记录,并且没有这些记录所特有的公共字段。

它真的归结为第二个派生表查询:

SELECT temp2.bn, max(temp2.FPE) AS max_fpe, temp2.Activity Description 
FROM pdata temp2 
GROUP BY temp2.bn;

无论我在这里做什么来创建Activity Description TEXT字段的索引,查询都不会根据EXPLAIN使用它。如果它会使用索引,我确信这个查询运行得很好(因为第一个派生表查询运行得非常快)。或者,如果有更好的方法来构造此查询以确保每个bn只有一个记录,那也可以。

感谢。

1 个答案:

答案 0 :(得分:1)

子选择通常是慢速查询的最快方式。我不确定你到底想要做什么,但你可以用FPE从pdata中选择BN并进行以下查询

SELECT p.* FROM pdata p 
LEFT JOIN pdata p0 ON p.BN = p0.BN AND p.FPE < p0.FPE 
WHERE p0.BN IS NULL

同样,如果你在tdata中有一些列是唯一的(或者在具有相同BN的行中是唯一的)

SELECT t.* FROM tdata t 
LEFT JOIN tdata t0 ON t.BN = t0.BN AND t.SOMEUNIQUEKEY != t0.SOMEUNIQUEKEY 
WHERE t0.BN IS NULL

关于子选择的奇怪之处:它们总是比等效连接慢得多。我认为这是一个错误。

修改

<鱼类学家胡珀(Hooper)不清楚LEFT JOIN pdata p0 ON p.BN = p0.BN ... WHERE p0.BN IS NULL的东西是如何起作用的。让我通过一个更简单的例子一步一步地完成它。你有一个表名,姓氏和名字,你想找到唯一的姓氏(即每个姓氏只有一个人持有。数据如下:

last first
Smith Will
Smith John
Smith Adam
Jones John

首先尝试左连接

SELECT n1.last, n1.first, n2.last, n2.first FROM names n1 
  LEFT JOIN names n2 ON n1.last = n2.last and n1.first != n2.first

将返回

last first last first
Smith Will Smith John 
Smith John Smith Will
Smith Adam Smith John 
Smith Will Smith Adam 
Smith John Smith Adam 
Smith Adam Smith Will
Jones John NULL  NULL

注意最后一行中的那些空值?这不是划船事故,这是普通内连接和左连接之间的区别。内连接(找到具有相同姓氏和不同名字的所有行对)将找到前六个,但忽略未配对的第七个。 LEFT JOIN的唯一功能是填充任何未被ON子句填充的空值。

现在我们只提取 那一行:

SELECT n1.last, n1.first, n2.last, n2.first FROM names n1 
  LEFT JOIN names n2 ON n1.last = n2.last and n1.first != n2.first
  WHERE n2.last IS NULL

并且(假设基础数据中没有空值),我们只得到ON子句无法匹配的行。

Ta,如果我可以这样,da。