我有3张桌子
items
tag_id
mark_id
tags_users
tag_id
user_id
marks_users
mark_id
user_id
有没有办法为没有items
和嵌套选择的特定user_id
选择唯一UNION
?
SELECT items.*
FROM items
INNER JOIN tags_users ON tags_users.tag_id = items.tag_id
AND tags_users.user_id = 5
UNION
SELECT items.*
FROM items
INNER JOIN marks_users ON marks_users.mark_id = items.mark_id
AND marks_users.user_id = 5
答案 0 :(得分:2)
(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的答案),你就可以有效地形成一个交叉连接,这种连接以随着行数的增加而迅速降低性能而闻名。 O(N²)。说,你有:
这将发生在@Gordon的查询中:
items
到tags_users
的行。每个项目连接到100行,结果
10,000 x 100 = 1,000,000行。 (!)marks_users
。每行连接到100个标记,从而产生
100,000,000行。 (!!)WHERE
子句已应用,许多重复项被DISTINCT
折叠,产生10,000行。使用EXPLAIN ANALYZE
进行测试。即使数量很少,而且随着数量的增加,这种差异也会很明显。
我在我的机器上使用此设置进行了一些快速测试(第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 ms
将user_id
中的条件拉入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 ms
(见上文查询)
使用此查询,如果符合条件,则会检查每一行一次。您不需要DISTINCT
,因为行不会重复开始。
总运行时间: 26.569 ms
为了完整性,使用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 ms
答案 1 :(得分:1)
我认为您可以通过将表连接在一起并查看标记和标记表中的用户ID来实现此目的。你必须要小心重复。
以下是如何执行此操作的示例:
select distinct i.tag_id, i.user_id
from items i left outer join
tags_users tu
on i.tag_id = tu.tag_id left outer join
marks_users mu
on i.mark_id = mu.mark_id
where 5 in (tu.user_id, mu.user_id)
或者您可以将where子句更改为:
where tu.user_id = 5 or mu.user_id = 5
我想强调的是,这个答案解决了原始问题,该问题询问了制定查询的特定方式(不使用连接或子查询)。此查询可能效率不高;但是,它回答了原来的问题。我不知道为什么原始问题将这些限制放在答案上,但我选择不解决限制,只讨论所提出的问题。我绝对使用工会和子查询;事实上,我有时会批评过度使用后者。
在某些数据库中,这将被有效地编译;其他人(比如postgres)似乎做得更差。但是,原始问题并没有指明数据的大小,也没有提供有关性能需求的任何提示。