【发布时间】:2015-07-08 15:42:18
【问题描述】:
我有一个包含多对多关系的应用程序。我需要从一个表中选择与另一表中变量集的所有行相关联的所有行。
例如,我需要选择与bar 实体A、B、C 和E 关联的所有foo 实体。用户可以选择 1、5、12 或 50 个bar 实体来过滤foo 实体
表格中的相关字段:(id 使用 uuid)
/* ~20k rows */
CREATE TABLE `foo` (
`id` char(36) COLLATE utf8_unicode_ci NOT NULL,
`title` text COLLATE utf8_unicode_ci NOT NULL,
PRIMARY KEY (`id`),
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
/* ~30k rows */
CREATE TABLE `bar` (
`id` char(36) COLLATE utf8_unicode_ci NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
/* ~150k rows */
CREATE TABLE `foo_bar` (
`id` char(36) COLLATE utf8_unicode_ci NOT NULL,
`foo_id` char(36) COLLATE utf8_unicode_ci DEFAULT NULL,
`bar_id` char(36) COLLATE utf8_unicode_ci DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `foo_id_foreign` (`foo_id`),
KEY `bar_id_foreign` (`bar_id`),
CONSTRAINT `bar_id_foreign` FOREIGN KEY (`bar_id`)
REFERENCES `bar` (`id`) ON DELETE CASCADE,
CONSTRAINT `foo_id_foreign` FOREIGN KEY (`foo_id`)
REFERENCES `foo` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
我尝试了从不同 SO 答案中看到的两种不同方法:多个连接和子查询。多个连接似乎工作得相当好,但它似乎不是高度可扩展的。运行子查询似乎应该可以更好地扩展,但运行数小时。
多连接。它可以工作,但是正如预期的那样,每次额外的连接都会以指数方式增加经过的时间。 3 bars 大约需要 800 毫秒,这绝对是很高的。解释看起来很合理。
select `foo`.*
from `foo`
inner join foo_bar `fb1` on `fb1`.`foo_id` = `foo`.`id`
inner join bar `b1` on `b1`.`id` = `fb1`.`bar_id` AND `b1`.`id` = :some_uuid1
inner join foo_bar `fb2` on `fb2`.`foo_id` = `foo`.`id`
inner join bar `b2` on `b2`.`id` = `fb2`.`bar_id` AND `b2`.`id` = :some_uuid2
inner join foo_bar `fb3` on `fb3`.`foo_id` = `foo`.`id`
inner join bar `b3` on `b3`.`id` = `fb3`.`bar_id` AND `b3`.`id` = :some_uuid3
group by `foo`.`id`
order by `foo`.`title` asc
limit 25 offset 0
子查询。无限期运行。 where in (subquery) 与 inner join subquery 的效果相同,但最终解释看起来有点不同。
select `foo`.*
from `foo`
inner join (
select `foo_id`
from `foo_bar`
inner join `bar`
on `bar`.`id` = `foo_bar`.`bar_id`
where `bar`.`id` in (:some_uuid1, :some_uuid2, :some_uuid3)
group by `foo_id`
having COUNT(*) = 3
) as `subset` on `foo`.`id` = `subset`.`foo_id`
order by `foo`.`title` asc
limit 25 offset 0
解释:
id select_type table type key key_len rows extra
1 PRIMARY derived ALL NULL NULL 6618 Using temporary; Using filesort
1 PRIMARY foo eq_ref PRIMARY 108 1
2 DERIVED bar const PRIMARY 108 1 Using index; Using temporary; Using filesort
2 DERIVED foo_bar ref bar_id_foreign 109 16094 Using where
我的问题是我可以应用任何优化来使这种情况变得可用和可扩展吗?
【问题讨论】:
标签: mysql database many-to-many