所有在数组中的PostgreSQL

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

添加有关架构和案例的更多信息:

  1. 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)
  2. 对话有很多用户,用户属于许多对话.为了找到对话中的所有用户,我正在使用此连接表.

  3. 最后,我试图在轨道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,但跳过最后一个查询的排序步骤.因此,根据多个因素(例如行数,内核数,缓存数,索引数等),查询计划可能会更快.

  • 对于您的第一个查询,您可以使用`... having count(distinct user_id)= 2`,然后您不需要唯一约束. (2认同)

hgm*_*mnz 7

我正在将这些用户折叠成一个数组.我也使用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)

编辑(一些资源)


Mad*_*nan 1

创建一个包含所有可能值的映射表并使用它

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)