链接者(tag_id, mark_id)
SELECT DISTINCT i.*
FROM tags_users tu
JOIN marks_users mu USING (user_id)
JOIN items i USING (tag_id, mark_id)
WHERE tu.user_id = 5;
如果DISTINCT
您在列上定义了多列主键或唯一键,则不需要。
由tag_id
或链接 mark_id
@Gordon 的回答是完全有效的。但它会表现得很糟糕。
这会快得多:
SELECT i.*
FROM items i
WHERE EXISTS (
SELECT 1
FROM tags_users tu
WHERE tu.tag_id = i.tag_id
AND tu.user_id = 5
)
OR EXISTS (
SELECT 1
FROM marks_users mu
WHERE mu.mark_id = i.mark_id
AND mu.user_id = 5
);
假设条目items
本身在 上是唯一的(tag_id, mark_id)
。
为什么要快得多?
如果您JOIN
使用两个不相关的表(如@Gordon 的回答),您可以有效地形成一个cross join,它以随着行数的增加而迅速降低性能而闻名。O(N²)。说,你有:
- 100 个用户,100 个标签和 100 个标记。
- 每个组合都存在(简单的假设设置,现实生活中的数据将不太平衡)。
- 在每个表中产生 10,000 行。
这将发生在@Gordon 的查询中:
- 加入 to 的
items
行tags_users
。每个项目连接到 100 行,结果为 10,000 x 100 = 1,000,000 行。(!)
- 加入到
marks_users
. 每行连接到 100 个标记,从而产生 100,000,000 行。(!!)
- 应用该
WHERE
子句并且许多重复项被 折叠DISTINCT
,从而产生 10,000 行。
用 测试EXPLAIN ANALYZE
。即使数量很少并且数量不断增加,差异也会很明显。
SQL小提琴。
基准
我在我的机器上使用此设置进行了一些快速测试(第 9.1 页):
戈登的询问
SELECT DISTINCT i.*
FROM items i
LEFT JOIN tags_users tu on i.tag_id = tu.tag_id
LEFT JOIN marks_users mu on i.mark_id = mu.mark_id
WHERE 5 IN (tu.user_id, mu.user_id);
总运行时间:38229.860 毫秒
消毒版
将条件拉user_id
入JOIN
子句从根本上减少了组合,但它仍然是一个(更小)的 cross join。
SELECT DISTINCT i.*
FROM items i
LEFT JOIN tags_users tu on i.tag_id = tu.tag_id AND tu.user_id = 5
LEFT JOIN marks_users mu on i.mark_id = mu.mark_id AND mu.user_id = 5
WHERE tu.user_id = 5 OR mu.user_id = 5;
总运行时间:110.450 毫秒
使用 EXISTS 半连接
(参见上面的查询)
使用此查询,每行检查一次是否符合条件。您不需要 a DISTINCT
,因为从一开始就不会重复行。
总运行时间:26.569 毫秒
联盟
为了完整起见,带有UNION
. 使用UNION
, 不UNION ALL
删除重复项:
SELECT i.*
FROM items i
JOIN tags_users tu ON i.tag_id = tu.tag_id AND tu.user_id = 5
UNION
SELECT i.*
FROM items i
JOIN marks_users mu ON i.mark_id = mu.mark_id AND mu.user_id = 5;
总运行时间:178.901 毫秒