我在创建最佳 SQL 查询时遇到了问题。我有私有(private)消息系统,用户可以在其中向许多用户或用户组发送一条消息。收件人存储在单个文本列中(不要问我为什么我不负责设计它),就像这样:
[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] ";
}
但我认为这是非常糟糕的方式。有没有办法在单个查询中获取用户和指定组?
最佳答案
由于用户和组仅通过他们在列表中的顺序位置链接,因此您需要利用它。
快速而肮脏的方法是 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 的像 user
或 group
作为标识符。
这样,数组中具有相同序号位置的元素(从逗号分隔列表转换而来)形成一行。两个数组都需要具有相同数量的元素,否则操作将生成笛卡尔积。根据你的描述,这里应该是这样的。添加代码以验证是否可能违反该条件。
更清洁的替代品
虽然上面的工作可靠,但它是 SRF(设置返回函数)的非标准 Postgres 特性,有些人不赞成。
有更简洁的方法可以做到这一点。即将发布的 9.4 版 Postgres 将发布一个新功能:WITH ORDINALITY
,允许更简洁的代码。这个相关的答案证明了两者:
PostgreSQL unnest() with element number
关于php - 多对多关系、IN 运算符和不正确结果的可能性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21782374/