我有以下(简化的)表格
users
+----+-------+
| id | name |
+----+-------+
| 1 | alpha |
| 3 | gamma |
| 5 | five |
| 7 | seven |
| 9 | nine |
+----+-------+
user_relationships
+--------------+----------------+----------------------+
| from_user_id | target_user_id | relationship_type_id |
+--------------+----------------+----------------------+
| 1 | 3 | 1 |
| 1 | 5 | -1 |
| 1 | 7 | 1 |
| 1 | 9 | 1 |
| 7 | 1 | 1 |
+--------------+----------------+----------------------+
Run Code Online (Sandbox Code Playgroud)
lation_type_id = 1 代表“以下”
role_type_id = -1 用于“阻止”
由此产生的 alpha 关系是:
伽玛的关系是:
我需要在输出中捕获上述关系:
Output
+----+-------+-----------------+----------------+--------------+----------------+
| id | name | following_count | followed_count | mutual_count | blocking_count |
+----+-------+-----------------+----------------+--------------+----------------+
| 1 | alpha | 2 | 0 | 1 | 1 |
| 3 | gamma | 0 | 1 | 0 | 0 |
| 5 | five | 0 | 0 | 0 | 0 |
| 7 | seven | 0 | 0 | 1 | 0 |
| 9 | nine | 0 | 1 | 0 | 0 |
+----+-------+-----------------+----------------+--------------+----------------+
Run Code Online (Sandbox Code Playgroud)
我已经花了几个小时来处理 GROUP BY、COUNT、HAVING、DISTINCT、SUM、(SELECT 中的 SUM)等组合,但无法让它工作。
请需要帮助或指导。我很高兴进一步尝试。
下面是基本的 MySQL 查询(没有我搞砸的实验)
select
u.id,
u.name,
r1.from_user_id, r1.target_user_id, r1.relationship_type_id,
r2.from_user_id, r2.target_user_id, r2.relationship_type_id,
r3.from_user_id, r3.target_user_id, r3.relationship_type_id
from users u
join user_relationships r1
on u.id = r1.from_user_id
join user_relationships r2
on u.id = r2.target_user_id
join user_relationships r3
on u.id = r3.from_user_id or u.id = r3.target_user_id;
Run Code Online (Sandbox Code Playgroud)
列following_count,mutual_count并且可以用条件聚合blocking_count来实现。你可以写一个子查询。followed_count
select u.id, u.name\n , coalesce(sum(r.relationship_type_id = 1 and r1.relationship_type_id is null), 0) as following_count\n , coalesce(sum(r.relationship_type_id = 1 and r1.relationship_type_id = 1), 0) as mutual_count\n , coalesce(sum(r.relationship_type_id = -1), 0) as blocking_count\n , (\n select count(*)\n from user_relationships r2\n left join user_relationships r3 \n on r3.from_user_id = r2.target_user_id\n and r3.target_user_id = r2.from_user_id \n where r2.target_user_id = u.id\n and r2.relationship_type_id = 1\n and r3.from_user_id is null\n ) as followed_count\nfrom users u\nleft join user_relationships r on r.from_user_id = u.id\nleft join user_relationships r1\n on r1.from_user_id = r.target_user_id\n and r1.target_user_id = r.from_user_id\ngroup by u.id, u.name;\nRun Code Online (Sandbox Code Playgroud)\n\n演示: http: //rextester.com/WJED13044
\n\n另一种方法是首先生成完整的外连接,以便在单行中获得两个方向的关系。那会是这样的
\n\nselect *\nfrom user_relationships r1\nfull outer join user_relationships r2\n on r2.from_user_id = r1.target_user_id\n and r1.from_user_id = r2.target_user_id\nRun Code Online (Sandbox Code Playgroud)\n\n但由于 MySQL 不支持完全外连接,我们需要这样的东西:
\n\nselect r.*, r1.relationship_type_id as type1, r2.relationship_type_id as type2\nfrom (\n select from_user_id uid1, target_user_id uid2 from user_relationships\n union distinct\n select target_user_id uid1, from_user_id uid2 from user_relationships\n) r\nleft join user_relationships r1\n on r1.from_user_id = r.uid1\n and r1.target_user_id = r.uid2\nleft join user_relationships r2\n on r2.target_user_id = r.uid1\n and r2.from_user_id = r.uid2;\nRun Code Online (Sandbox Code Playgroud)\n\n这将返回
\n\nuid1 \xe2\x94\x82 uid2 \xe2\x94\x82 type1 \xe2\x94\x82 type2\n\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xbc\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xbc\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xbc\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\n 7 \xe2\x94\x82 1 \xe2\x94\x82 1 \xe2\x94\x82 1\n 1 \xe2\x94\x82 7 \xe2\x94\x82 1 \xe2\x94\x82 1\n 1 \xe2\x94\x82 3 \xe2\x94\x82 1 \xe2\x94\x82 null\n 1 \xe2\x94\x82 5 \xe2\x94\x82 -1 \xe2\x94\x82 null\n 1 \xe2\x94\x82 9 \xe2\x94\x82 1 \xe2\x94\x82 null\n 3 \xe2\x94\x82 1 \xe2\x94\x82 null \xe2\x94\x82 1\n 5 \xe2\x94\x82 1 \xe2\x94\x82 null \xe2\x94\x82 -1\n 9 \xe2\x94\x82 1 \xe2\x94\x82 null \xe2\x94\x82 1\nRun Code Online (Sandbox Code Playgroud)\n\n这样我们就可以在单行中获得两个方向的关系,因此不需要该列的子查询followed_count,而是可以使用条件聚合。
select u.id, u.name\n , coalesce(sum(r1.relationship_type_id = 1 and r2.relationship_type_id is null), 0) as following_count\n , coalesce(sum(r2.relationship_type_id = 1 and r1.relationship_type_id is null), 0) as followed_count\n , coalesce(sum(r1.relationship_type_id = 1 and r2.relationship_type_id = 1), 0) as mutual_count\n , coalesce(sum(r1.relationship_type_id = -1), 0) as blocking_count\nfrom users u\nleft join (\n select from_user_id uid1, target_user_id uid2 from user_relationships\n union distinct\n select target_user_id uid1, from_user_id uid2 from user_relationships\n) r on r.uid1 = u.id\nleft join user_relationships r1\n on r1.from_user_id = r.uid1\n and r1.target_user_id = r.uid2\nleft join user_relationships r2\n on r2.target_user_id = r.uid1\n and r2.from_user_id = r.uid2\ngroup by u.id, u.name\norder by u.id;\nRun Code Online (Sandbox Code Playgroud)\n\n演示: http: //rextester.com/IFGLT77163
\n\n这也更灵活,因为我们现在可以轻松添加blocked_count列
, coalesce(sum(r2.relationship_type_id = -1), 0) as blocked_count\nRun Code Online (Sandbox Code Playgroud)\n\n如果您使用 MySQL 8 或 MariaDB 10.2,则可以使用CTE编写得更好一些:
\n\nwith bdr as ( -- bidirectional relations\n select from_user_id uid1, target_user_id uid2 from user_relationships\n union distinct\n select target_user_id uid1, from_user_id uid2 from user_relationships\n), rfoj as ( -- relations full outer join\n select uid1, uid2, r1.relationship_type_id type1, r2.relationship_type_id type2\n from bdr\n left join user_relationships r1\n on r1.from_user_id = bdr.uid1\n and r1.target_user_id = bdr.uid2\n left join user_relationships r2\n on r2.target_user_id = bdr.uid1\n and r2.from_user_id = bdr.uid2\n)\n select u.id, u.name\n , coalesce(sum(type1 = 1 and type2 is null), 0) as following_count\n , coalesce(sum(type2 = 1 and type1 is null), 0) as followed_count\n , coalesce(sum(type1 = 1 and type2 = 1), 0) as mutual_count\n , coalesce(sum(type1 = -1), 0) as blocking_count\n , coalesce(sum(type2 = -1), 0) as blocked_count\n from users u\n left join rfoj r on r.uid1 = u.id\n group by u.id, u.name\n order by u.id\nRun Code Online (Sandbox Code Playgroud)\n\n演示:https://www.db-fiddle.com/f/nEDXXkrLEj9F4dKfipzN9Q/0
\n\n在阅读了您的评论并查看了您对查询的尝试后,我也有了一个“见解”,并且认为应该可以仅通过两个连接而无需子查询来获得结果。
\n\n可以通过以下方式获得与 FULL OUTER JOIN 类似的结果:
\n\nselect u.*\n , coalesce(r1.from_user_id, r2.target_user_id) as uid1\n , coalesce(r2.from_user_id, r1.target_user_id) as uid2\n , r1.relationship_type_id as type1\n , r2.relationship_type_id as type2\nfrom users u\nleft join user_relationships r1 on r1.from_user_id = u.id\nleft join user_relationships r2\n on r2.target_user_id = u.id\n and (r2.from_user_id = r1.target_user_id or r1.from_user_id is null)\nRun Code Online (Sandbox Code Playgroud)\n\n然后我们只需要添加 GROUP BY 子句并执行条件聚合,就像我们在其他查询中所做的那样:
\n\nselect u.id, u.name\n , coalesce(sum(r1.relationship_type_id = 1 and r2.relationship_type_id is null), 0) as following_count\n , coalesce(sum(r2.relationship_type_id = 1 and r1.relationship_type_id is null), 0) as followed_count\n , coalesce(sum(r1.relationship_type_id = 1 and r2.relationship_type_id = 1), 0) as mutual_count\n , coalesce(sum(r1.relationship_type_id = -1), 0) as blocking_count\nfrom users u\nleft join user_relationships r1 on r1.from_user_id = u.id\nleft join user_relationships r2\n on r2.target_user_id = u.id\n and (r2.from_user_id = r1.target_user_id or r1.from_user_id is null)\ngroup by u.id, u.name\norder by u.id;\nRun Code Online (Sandbox Code Playgroud)\n\n演示: http: //rextester.com/UAS51627
\n\n子句(更新 2OR )中的条件可能会损害性能。这通常通过UNION 优化来解决,这将导致与完全外连接类似的解决方案。ON
就性能而言,带有LEFT JOIN子查询(更新 1)也不是最好的主意,因为 ON 子句不能使用任何索引。最好使用 INNER JOIN 来代替,并在应用程序中(如果确实需要)用缺失的用户(那些根本没有关系的用户)填充结果,或者直接将它们排除在外。
| 归档时间: |
|
| 查看次数: |
2277 次 |
| 最近记录: |