pex*_*pex 25 sql postgresql activerecord ruby-on-rails relational-division
实现子句中最简单,最快速的方法是什么,必须匹配数组中的所有元素 - 使用时不仅仅是一个IN?毕竟它应该像mongodb的$ all.
考虑到conversation_users是conversation_id和user_id之间的连接表的群组对话,我有类似这样的想法:
WHERE (conversations_users.user_id ALL IN (1,2))
Run Code Online (Sandbox Code Playgroud)
更新 16.07.12
添加有关架构和案例的更多信息:
join-table非常简单:
Table "public.conversations_users"
Column | Type | Modifiers | Storage | Description
-----------------+---------+-----------+---------+-------------
conversation_id | integer | | plain |
user_id | integer | | plain |
Run Code Online (Sandbox Code Playgroud)对话有很多用户,用户属于许多对话.为了找到对话中的所有用户,我正在使用此连接表.
最后,我试图在轨道scope上找出一个红宝石,根据它的参与者找到我的对话 - 例如:
scope :between, ->(*users) {
joins(:users).where('conversations_users.user_id all in (?)', users.map(&:id))
}
Run Code Online (Sandbox Code Playgroud)更新 23.07.12
我的问题是找到一个完全匹配的人.因此:
(1,2,3)如果查询,则之间的对话将不匹配(1,2)
Ale*_*ore 29
假设连接表遵循良好实践并且定义了唯一的复合键,即防止重复行的约束,那么类似下面的简单查询应该这样做.
select conversation_id from conversations_users where user_id in (1, 2)
group by conversation_id having count(*) = 2
Run Code Online (Sandbox Code Playgroud)
值得注意的是,末尾的数字2是user_id列表的长度.如果user_id列表改变长度,那显然需要改变.如果您无法将连接表不包含重复,变"COUNT(*)",以"计数(不同的user_id)"在性能上的一些可能的成本假设.
即使对话还包括其他用户,此查询也会查找包含所有指定用户的所有会话.
如果只想要与指定用户集完全对话,则一种方法是在where子句中使用嵌套子查询,如下所示.注意,第一行和最后一行与原始查询相同,只有中间的两行是新的.
select conversation_id from conversations_users where user_id in (1, 2)
and conversation_id not in
(select conversation_id from conversations_users where user_id not in (1,2))
group by conversation_id having count(*) = 2
Run Code Online (Sandbox Code Playgroud)
同样,如果数据库支持,您可以使用set difference运算符.以下是Oracle语法中的示例.(对于Postgres或DB2,将关键字"minus"更改为"except".)
select conversation_id from conversations_users where user_id in (1, 2)
group by conversation_id having count(*) = 2
minus
select conversation_id from conversations_users where user_id not in (1,2)
Run Code Online (Sandbox Code Playgroud)
一个好的查询优化器应该相同地处理最后两个变体,但检查您的特定数据库是否确定.例如,在Oracle 11gR2的查询计划将减号来进行排序前两组对话的ID,但跳过最后一个查询的排序步骤.因此,根据多个因素(例如行数,内核数,缓存数,索引数等),查询计划可能会更快.
我正在将这些用户折叠成一个数组.我也使用CTE(WITH子句中的东西)来使它更具可读性.
=> select * from conversations_users ;
conversation_id | user_id
-----------------+---------
1 | 1
1 | 2
2 | 1
2 | 3
3 | 1
3 | 2
(6 rows)
=> WITH users_on_conversation AS (
SELECT conversation_id, array_agg(user_id) as users
FROM conversations_users
WHERE user_id in (1, 2) --filter here for performance
GROUP BY conversation_id
)
SELECT * FROM users_on_conversation
WHERE users @> array[1, 2];
conversation_id | users
-----------------+-------
1 | {1,2}
3 | {1,2}
(2 rows)
Run Code Online (Sandbox Code Playgroud)
编辑(一些资源)
创建一个包含所有可能值的映射表并使用它
select
t1.col from conversations_users as t1
inner join mapping_table as map on t1.user_id=map.user_id
group by
t1.col
having
count(distinct conversations_users.user_id)=
(select count(distinct user_id) from mapping)
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
15150 次 |
| 最近记录: |