我有以下查询:
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实现上获得更好的执行计划。如果是这样的话,我会切换。
答案 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查询执行计划:
我不再使用MySQL了。