如何在 MySQL/MariaDB 中加入两个巨大的表?
Posted
技术标签:
【中文标题】如何在 MySQL/MariaDB 中加入两个巨大的表?【英文标题】:How to join two huge tables in MySQL/MariaDB? 【发布时间】:2020-12-17 02:56:27 【问题描述】:我有两个表 kw
有 250000 条记录,t_n
有 100000 条记录。
我加入了两个表以在以下查询中获得集体结果:
SELECT kw.id AS kw_id,
kw.word AS kw_word,
t_n.translation AS t_n_translation
FROM kw, t_n
WHERE kw.id = t_n.keyword_id
AND kw.word LIKE '1%'
GROUP BY t_n.translation
LIMIT 10
我还尝试了以下简单连接作为上述查询的替代方法:
SELECT kw.id AS kw_id,
kw.word AS kw_word,
t_n.translation AS t_n_translation
FROM kw
INNER JOIN t_n
ON kw.id = t_n.keyword_id
AND kw.word LIKE '1%'
GROUP BY t_n.translation
LIMIT 10
但是这两个查询都需要大约 60 到 150 秒,具体取决于内存和处理的系统资源。 这种执行超时对于扩展数据库和增加多个用户来说是难以忍受的。有没有什么有效的方法可以连接两个巨大的表?
更新 此查询用于 JQuery UI 自动完成。我使用 GROUP BY 子句来获取不同的值。是否有任何其他合适的解决方案来获得不同的值。
表格结构
--
-- Table structure for table `kw`
--
CREATE TABLE `kw` (
`id` int(32) NOT NULL,
`word` varchar(100) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL,
`pos` varchar(12) NOT NULL,
`definition` text NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-- --------------------------------------------------------
--
-- Table structure for table `t_n`
--
CREATE TABLE `t_n` (
`id` int(16) NOT NULL,
`keyword_id` int(16) NOT NULL,
`translation` varchar(64) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL,
`created_on` datetime NOT NULL,
`user_id` varchar(64) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
--
-- Indexes for dumped tables
--
--
-- Indexes for table `kw`
--
ALTER TABLE `kw`
ADD PRIMARY KEY (`id`);
--
-- Indexes for table `t_n`
--
ALTER TABLE `t_n`
ADD PRIMARY KEY (`id`);
--
-- AUTO_INCREMENT for dumped tables
--
--
-- AUTO_INCREMENT for table `kw`
--
ALTER TABLE `kw`
MODIFY `id` int(32) NOT NULL AUTO_INCREMENT;
--
-- AUTO_INCREMENT for table `t_n`
--
ALTER TABLE `t_n`
MODIFY `id` int(16) NOT NULL AUTO_INCREMENT;
COMMIT;
【问题讨论】:
能否附上执行计划? 在没有任何聚合函数的情况下,GROUP BY 子句永远不合适。如需进一步帮助,请参阅meta.***.com/questions/333952/… 1) 删除 GROUP BY - 这在逻辑上是错误的。 2)根据索引创建(我建议测试kw (word, id)
和t_n (keyword_id, translation)
。
没有排序的限制很少有意义。
请提供SHOW CREATE TABLE
。
【参考方案1】:
我猜你想要:
SELECT kw.id as kw_id, kw.word as kw_word, t_n.translation as t_n_translation
FROM kw INNER JOIN
t_n
ON kw.id = t_n.keyword_id AND kw.word LIKE '1%' ;
对于此查询,您需要在 kw(word, id)
和 t_n(keyword_id)
上建立索引:
create index idx_kw_word_id on kw(word, id);
create index idx_t_n_keyword_id on t_n(keyword_id);
【讨论】:
我尝试了您提供的查询,但仍然需要很长时间。我还在我的问题更新中提供了表格结构。我认为这是由于您在答案的最后一行中提到的一些缺少索引。 @Rashid 。 . .您还没有添加答案中推荐的索引。 你能告诉我在kw(word,id)
和t_n(keyword_id)
的字段上添加索引的过程吗?
好极了 - 现在添加索引后查询速度非常快。【参考方案2】:
正确的做法是用JOIN ON
表示表是如何关联的,用WHERE
表示过滤:
SELECT kw.id AS kw_id,
kw.word AS kw_word,
t_n.translation AS t_n_translation
FROM kw
INNER JOIN t_n
ON kw.id = t_n.keyword_id
WHERE kw.word LIKE '1%'
ORDER BY t_n.translation
LIMIT 10
尚不清楚GROUP BY
是否必要或正确。请解释它的目的,并告诉我们没有它会发生什么。没有ORDER BY
的LIMIT
很少有意义。也许您的意思是ORDER BY
而不是 GROUP BY
? (所以我做了那个改变。)
假设您没有任何 TEXT
列,这些索引应该会有所帮助:
kw: INDEX(word, id)
t_n: INDEX(keyword_id, translation)
这些索引是“复合的”,但不是很相关,并且是“覆盖”的,可以提高性能。
如果我能看到SHOW CREATE TABLE
,可能还有进一步的改进,比如修改PRIMARY KEY
。
更多
int(32)
-- (32)
没有任何意义。 INT
始终是一个 32 位(4 字节)的数字; SMALLINT
是一个 16 位(2 字节)的数字。
似乎keyword_id
在t_n
中是独一无二的。如果是这样,请将其设为PRIMARY KEY
,如果是id
,则完全删除。
到那时,这两个表似乎可以合并为一个?有很多不同的词,每个词都有一个翻译?但也许不是,因为我看到两张桌子的大小不同。请解释架构。
【讨论】:
。 .我在更新我的问题时提供了表格结构。 @Rashid - 我又添加了一些。【参考方案3】:我可以建议下一个解决方案:
添加索引:
CREATE INDEX keyword_id_ix on t_n(keyword_id);
使用LIKE
过滤大表:
SELECT * FROM kw WHERE kw.word LIKE '1%'
使用JOIN
将结果与第二个表合并:
SELECT
kw.id as kw_id, kw.word as kw_word, t_n.translation as t_n_translation
FROM (
SELECT * FROM kw WHERE kw.word LIKE '1%'
) kw
INNER JOIN t_n ON kw.id = t_n.keyword_id;
SQL 小提琴here
【讨论】:
不需要“派生表”(子查询);只是JOIN
两张桌子在一起。
@RickJames,当然我们可以在没有子查询的情况下使用直接 JOIN,但是在大表子查询上有助于减少连接前的数据集并提高性能。确定解决方案的效率必须在真实数据上进行测试以上是关于如何在 MySQL/MariaDB 中加入两个巨大的表?的主要内容,如果未能解决你的问题,请参考以下文章