许多与很多关系,IN运算符和不正确结果的可能性

时间:2014-02-14 14:47:02

标签: php postgresql optimization many-to-many

我在创建最佳SQL查询时遇到问题。我有私人消息系统,用户可以向许多用户或用户组发送单个消息。收件人存储在单个文本列中(不要问我为什么我不负责设计),如下所示:

[60,63,103,68]

另外我添加了新的文本列,其中放置了用户所属的组,因此我可以在数据库中作为组:

[55,11,11,0]

现在我想获得所有用户(接收者)及其群组。我有表用户和组ID之间的关系。问题是单个用户可以属于多个组,例如用户60可以在组ID 55和11中。我想以最佳方式进行(可以有50多个接收器存储在列中......)所以我可以写这样的查询:

SELECT u.name, u.last_name, g.group_name

                    FROM 
                        user u
                    LEFT JOIN
group g ON u.id = g.user_id
                    WHERE
                        u.id IN (".$users.") and
g.id IN (".$groups.")

不幸的是,查询返回的组名可能不正确 - 与我在WHERE中放置的组ID相关联。我可以创建PHP foreach并使用我拥有的ID获取用户及其组:

foreach($user as $key => $single)
{
$sql = "...
      where u.id = $single AND g.id = $group[$key] ";
}

但我认为这是非常糟糕的方式。有没有办法在单个查询中获取用户和指定的组?

1 个答案:

答案 0 :(得分:1)

由于用户和群组仅通过列表中的序号位置进行关联,因此您需要使用它。

快速而肮脏的方法是unnest()并行:

SELECT u.name, u.last_name, g.group_name
FROM  (   
   SELECT unnest(string_to_array('3,1,2', ',')::int[])    AS usr_id  -- users
        , unnest(string_to_array('10,11,12', ',')::int[]) AS grp_id  -- groups
  ) sel
JOIN   usr_grp ug USING (usr_id, grp_id)
JOIN   usr u USING (usr_id)
JOIN   grp g USING (grp_id);

请注意我将SQL key words替换为usergroup作为标识符。

-> SQLfiddle

这样,数组中具有相同序号位置的元素(从逗号分隔列表转换)形成一行。两个数组都需要相同数量的元素,否则操作将导致笛卡尔积。根据你的描述,这应该就是这种情况。添加代码以验证是否可能违反该条件。

清洁替代品

虽然上述工作可靠,但它是SRF的非标准Postgres功能(设置返回功能),但有些人不赞成。

有更简洁的方法可以做到这一点。即将发布的Postgres版本9.4将发布一个新功能:WITH ORDINALITY,允许更清晰的代码。这个相关的答案证明了两者:
PostgreSQL unnest() with element number