从此MySQL选择+ join + group中删除“使用临时;使用filesort”

时间:2012-11-15 22:56:49

标签: mysql sql postgresql

我有以下查询:

select 
    t.Chunk as LeftChunk,
    t.ChunkHash as LeftChunkHash,
    q.Chunk as RightChunk,
    q.ChunkHash as RightChunkHash,
    count(t.ChunkHash) as ChunkCount
from
    chunks as t
    join
    chunks as q
    on
        t.ID = q.ID
group by LeftChunkHash, RightChunkHash

以下解释表:

id  select_type table   type    possible_keys   key key_len ref rows    Extra
1   SIMPLE  t   ALL IDIndex NULL    NULL    NULL    17796190    "Using temporary; Using filesort"
1   SIMPLE  q   ref IDIndex IDIndex 4   sotero.t.Id 12  

注意“使用临时;使用filesort”。

运行此查询时,我很快耗尽了RAM(可能是临时表的b / c),然后硬盘开始运行,查询速度变慢。

我认为这可能是一个索引问题,所以我开始添加一些有意义的东西:

Table   Non_unique  Key_name    Seq_in_index    Column_name Collation   Cardinality Sub_part    Packed  Null    Index_type  Comment Index_comment
chunks  0   PRIMARY 1   ChunkId A   17796190    NULL    NULL        BTREE       
chunks  1   ChunkHashIndex  1   ChunkHash   A   243783  NULL    NULL        BTREE       
chunks  1   IDIndex 1   Id  A   1483015 NULL    NULL        BTREE       
chunks  1   ChunkIndex  1   Chunk   A   243783  NULL    NULL        BTREE       
chunks  1   ChunkTypeIndex  1   ChunkType   A   2   NULL    NULL        BTREE       
chunks  1   chunkHashByChunkIDIndex 1   ChunkHash   A   243783  NULL    NULL        BTREE       
chunks  1   chunkHashByChunkIDIndex 2   ChunkId A   17796190    NULL    NULL        BTREE       
chunks  1   chunkHashByChunkTypeIndex   1   ChunkHash   A   243783  NULL    NULL        BTREE       
chunks  1   chunkHashByChunkTypeIndex   2   ChunkType   A   261708  NULL    NULL        BTREE       
chunks  1   chunkHashByIDIndex  1   ChunkHash   A   243783  NULL    NULL        BTREE       
chunks  1   chunkHashByIDIndex  2   Id  A   17796190    NULL    NULL        BTREE       

但仍然使用临时表。

数据库引擎是MyISAM。

如何摆脱临时使用;在此查询中使用filesort?

只需更改为InnoDB,无需解释根本原因,这不是一个特别令人满意的答案。此外,如果解决方案只是添加正确的索引,那么这比迁移到另一个数据库引擎要容易得多。

我是关系数据库的新手。所以我希望解决方案对专家来说是显而易见的。

EDIT1:

ID不是主键。 ChunkID是。每个ID大约有40个ChunkID。因此,向表中添加额外的ID会增加大约40行。每个唯一的块都有一个与之关联的唯一chunkHash。

EDIT2:

这是架构:

Field   Type    Null    Key Default Extra
ChunkId int(11) NO  PRI NULL    
ChunkHash   int(11) NO  MUL NULL    
Id  int(11) NO  MUL NULL    
Chunk   varchar(255)    NO  MUL NULL    
ChunkType   varchar(255)    NO  MUL NULL    

编辑3:

查询的最终目标是创建跨文档的单词共现表。 ChunkID是单词实例。每个实例都是与特定文档(ID)相关联的单词。每个文档大约有40个单词。大约100万份文件。因此,与(显然)正在创建的完整跨产品临时表相比,得到的共现表格被高度压缩。也就是说,完整的交叉乘积临时表是1密耳* 40 * 40 = 16亿行。压缩的结果表估计大约有4千万行。

编辑4:

添加postgresql标记以查看是否有任何postgresql用户可以在该SQL实现上获得更好的执行计划。如果是这样的话,我会切换。

3 个答案:

答案 0 :(得分:2)

更新了产生相同结果的查询。但它不会更快。

Create Index IX_ID On Chunks (ID);

Select
  LeftChunk,
  LeftChunkHash,
  RightChunk,
  RightChunkHash,
  Sum(ChunkCount)
From (
  Select 
    t.Chunk as LeftChunk,
    t.ChunkHash as LeftChunkHash,
    q.Chunk as RightChunk,
    q.ChunkHash as RightChunkHash,
    count(t.ChunkHash) as ChunkCount
  From
    chunks as t
      inner join
    chunks as q
      on t.ID = q.ID
  Group By
    t.ID,
    t.ChunkHash,
    q.ChunkHash 
  ) x
Group By
  LeftChunk,
  LeftChunkHash,
  RightChunk,
  RightChunkHash

摆弄示例测试数据http://sqlfiddle.com/#!3/ea1a5/2

最新的小提琴,将问题重新表述为单词和文档:http://sqlfiddle.com/#!3/f5aef/12

将问题重新表述为文件和文字,你有多少文件,有多少文字,有多少文件?

此外,使用类比的文档和单词,您会说您的查询是"对于文档中出现的所有单词对,它们在任何文档中一起出现的频率。如果文字A在文档中出现n次,而在同一文档中出现单词B m次,则总计为n * m次。"

答案 1 :(得分:2)

如何在加入前汇总表格?

摘要可能是:

 select count(*) count,
        Chunk,
        ChunkHash
   from chunks
  group by Chunk, ChunkHash

然后加入将是:

Select r.Chunk as RightChunk,
       r.ChunkHash as RightChunkHash,
       l.Chunk as LeftChunk,
       l.ChunkHash as LeftChunkHash
       sum (l.Count) + sum(r.Count) as Count
  from (
        select count(*) count,
               Chunk,
               ChunkHash
          from chunks
      group by Chunk, ChunkHash
       ) l
  join (
        select count(*) count,
               Chunk,
               ChunkHash
          from chunks
      group by Chunk, ChunkHash
       ) r on l.Chunk = r.Chunk
 group by r.Chunk, r.ChunkHash, l.Chunk, l.ChunkHash

我不确定的是你正在计算的东西。所以我的SUM()+ SUM()是一个猜测。您可能需要SUM()* SUM()。

此外,我假设当且仅当ChunkHash值相等时,两个Chunk值相等。

答案 2 :(得分:2)

我从MySQL迁移到PostgreSQL,查询执行时间从大约1.5天到大约10分钟。

这是PostgreSQL查询执行计划:

enter image description here

我不再使用MySQL了。