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

标签 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 字段的处理方式。第一个子查询使用我创建的索引并且运行得非常快,但是第二个子查询的 EXPLAIN 显示“使用临时;”使用文件排序'。请参阅我在下表创建语句中创建的索引。谁能帮我优化一下吗?

通过快速解释,第一个子查询旨在仅选择具有唯一 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 的记录,并且没有这些记录所独有的公共(public)字段。

它实际上归结为第二个派生表查询:

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

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

谢谢。

最佳答案

子选择通常是实现慢速查询的最快方法。我不确定你到底想做什么,但你可以使用以下查询从 pdata 和 FPE 中选择 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

子选择有一些奇怪的地方:它们总是比等效的连接慢得多。我认为这是一个错误。

编辑

鱼类学家胡珀并不清楚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。

关于sql - 请帮助优化长时间运行的查询(左外连接,带有 2 个派生表),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4568767/

相关文章:

mysql - sql中如何设置group_concat分隔符为列字段

c# - SQL 表中的搜索结果

sql - 将第二个 SUM 添加到当前查询

Dart 上空函数的表现

c++ - boost::lambda::bind 表达式是否会阻止内联/是否有其他形式不会?

c# - 映射 GridView ASP/C# 的单元格/列中的值

mysql - PubChem数据库导入mysql

带有内部连接的 MySQL 查询返回零行

mysql - 无法获取日期时间小于系统日期的记录

c# - C#什么时候使用锁线程?