【发布时间】:2016-01-28 21:53:14
【问题描述】:
我有一个如下所示的架构(具有适当索引的数百万条记录):
groups | interests
------ | ---------
user_id | user_id
group_id | interest_id
一个用户可以喜欢 0..多个兴趣并属于 0..多个组。
问题:给定一个组 ID,我想获取不属于该组的所有用户的所有兴趣,并且与属于该组的任何人共享至少一个兴趣提供相同的组。
由于上述内容可能令人困惑,这里有一个简单的示例 (SQLFiddle):
| 1 | 2 | 3 | 4 | 5 | (User IDs)
|-------------------|
| A | | A | | |
| B | B | B | | B |
| | C | | | |
| | | D | D | |
在上面的示例中,用户用数字标记,而兴趣用字符标记。
如果我们假设用户 1 和 2 属于组 -1,那么用户 3 和 5 会很有趣:
user_id interest_id
------- -----------
3 A
3 B
3 D
5 B
我已经编写了一个愚蠢且非常低效的查询,可以正确返回上述内容:
SELECT * FROM "interests" WHERE "user_id" IN (
SELECT "user_id" FROM "interests" WHERE "interest_id" IN (
SELECT "interest_id" FROM "interests" WHERE "user_id" IN (
SELECT "user_id" FROM "groups" WHERE "group_id" = -1
)
) AND "user_id" NOT IN (
SELECT "user_id" FROM "groups" WHERE "group_id" = -1
)
);
但我所有将其转换为正确连接查询的尝试都表明自己没有结果:查询返回的行数比它应该返回的行多,或者它只需要子查询的 10 倍,例如:
SELECT "iii"."user_id" FROM "interests" AS "iii"
WHERE EXISTS
(
SELECT "ii"."user_id", "ii"."interest_id" FROM "groups" AS "gg"
INNER JOIN "interests" AS "ii" ON "gg"."user_id" = "ii"."user_id"
WHERE EXISTS
(
SELECT "i"."interest_id" FROM "groups" AS "g"
INNER JOIN "interests" AS "i" ON "g"."user_id" = "i"."user_id"
WHERE "group_id" = -1 AND "i"."interest_id" = "ii"."interest_id"
) AND "group_id" != -1 AND "ii"."user_id" = "iii"."user_id"
);
在过去的两个晚上,我一直在努力优化这个查询......
我们将不胜感激任何能让我朝着正确方向前进的帮助或见解。 :)
PS:理想情况下,一个返回共同兴趣汇总计数的查询会更好:
user_id totalInterests commonInterests
------- -------------- ---------------
3 3 1/2 (either is fine, but 2 is better)
5 1 1
但是,我不确定它与在代码中执行相比会慢多少。
【问题讨论】:
-
是什么让您认为联接更“合适”?
-
@CL。只是探索性的基准,没有别的。我知道,对于 JOIN 与子查询,并不总是有明显的赢家。但是,事实证明,PhilipKelley 的答案比我原来的方法快 100 倍以上(我想主要是因为使用了 CTE)。
-
非递归 CTE 对查询优化器有 no 影响;它们只是作为子查询插入到
FROM cte中。它们仅对文档有用,或在多次使用时用于节省输入。
标签: sql sqlite join query-optimization