3

我有一张表,用于在我的数据库中构建组。该表包含组名称和 ID 的列表。我有另一个包含用户的表,以及显示关系的第三个表。(用户标识,组标识)。

情况是这样的,我需要创建一个属于特定组子集的用户 ID 列表。例如,我想要组 1、3 和 8 中的所有用户。这很简单。但它变得更加复杂,我可能需要一个包含在组 1、3 和 8 或 1、2 和 8 中的所有用户的列表。然后我可能需要排除符合该标准但也在组中的用户27.

所以我有一个脚本动态地创建一个查询,使用子查询在一定程度上起作用。我有两个问题。我认为我没有正确处理非部分,因为当我广告标准时,最终它只是有点挂起。(我认为这是我使用子选择而不是连接的结果,但我无法弄清楚如何使用连接来构建它。)

下面是一个带有 4 个 ANDed OR 组和 2 个 NOT 子句的查询示例。

请让我知道是否有更好的方法来优化这个 stmt。(我可以在 PHP 中处理它的动态构建)

如果我需要澄清任何事情或提供更多细节,请告诉我。


select * from users_table where username IN
(
    select user_id from
    (
        select distinct user_id from group_user_map where user_id in 
        (
            select user_id from 
            (
                select * from 
                (
                    select count(*) as counter, user_id from  
                    (
                        (
                            select distinct(user_id) from group_user_map where group_id in (2601,119)
                        ) 
                        union all
                        (
                            select distinct(user_id) from group_user_map where group_id in (58,226)
                        ) 
                        union all
                        (
                            select distinct(user_id) from group_user_map where group_id in (1299,525)
                        ) 
                        union all
                        (
                            select distinct(user_id) from group_user_map where group_id in (2524,128)
                        ) 
                    ) 
                    thegroups group by user_id
                ) 
                getall where counter = 4
            ) 
            getuserids
        ) 
        and user_id not in 
        (
            select user_id from group_user_map where group_id in (2572)
        ) 
    ) 
    biggergroup 
);

请注意,查询的第一部分是将 id 与用户名进行比较。这是因为我将用户名存储为另一个表中的 id。(这整个事情是两个完全不同的数据库之间的链接)。

(另外,如果看起来我有任何额外的子查询,那就是试图强制 mysql 首先评估内部查询。)

谢谢。

亚伦。

4

4 回答 4

1

如果您发布表结构和一些示例数据,将更容易理解您的问题。但这里有一些基于您当前查询的建议,您可能可以使用它们。

这些查询减少了您正在使用的子查询的数量。一个明显的变化是它获取user_id每个组的 's 列表的方式不同:

select user_id
from group_user_map 
where group_id in (2601,119)
union all
select user_id 
from group_user_map 
where group_id in (58,226)
union all
select user_id 
from group_user_map 
where group_id in (1299,525)
union all
select user_id 
from group_user_map 
where group_id in (2524,128);

这使用UNION ALL将列出所有的,user_id即使它们是重复的。一旦你有了这个user_id's 列表,你就可以count通过应用 acount(distinct user_id)并使用HAVING子句来查找那些出现 4 次的 's 。

首先,您可以将当前查询合并到WHERE子句中的以下版本:

select * 
from users_table 
where username IN (select user_id
                  from
                  (
                    select user_id
                    from group_user_map 
                    where group_id in (2601,119)
                    union all
                    select user_id 
                    from group_user_map 
                    where group_id in (58,226)
                    union all
                    select user_id 
                    from group_user_map 
                    where group_id in (1299,525)
                    union all
                    select user_id 
                    from group_user_map 
                    where group_id in (2524,128)
                  ) thegroups
                  where user_id not in (select user_id 
                                        from group_user_map 
                                        where group_id in (2572)) 
                  group by userid
                  having count(distinct userid) = 4);

WHERE或者,您可以在您加入的子查询中使用该子句中的查询:

select ut.* 
from users_table ut
inner join
(
  select user_id
  from
  (
    select user_id
    from group_user_map 
    where group_id in (2601,119)
    union all
    select user_id 
    from group_user_map 
    where group_id in (58,226)
    union all
    select user_id 
    from group_user_map 
    where group_id in (1299,525)
    union all
    select user_id 
    from group_user_map 
    where group_id in (2524,128)
  ) thegroups
  where user_id not in (select user_id 
                        from group_user_map 
                        where group_id in (2572)) 
  group by userid
  having count(distinct userid) = 4
) biggergroup
  on ut.username = biggergroup.user_id;
于 2013-04-28T20:22:32.323 回答
1

避免用于 IN 子句的子选择:-

SELECT * 
FROM users_table
INNER JOIN 
(
    SELECT Sub1.user_id 
    FROM (
            SELECT COUNT(*) AS counter, user_id   
            FROM (
                SELECT distinct(user_id) FROM group_user_map WHERE group_id IN (2601,119)
                UNION ALL
                SELECT distinct(user_id) FROM group_user_map WHERE group_id IN (58,226)
                UNION ALL
                SELECT distinct(user_id) FROM group_user_map WHERE group_id IN (1299,525)
                UNION ALL
                SELECT distinct(user_id) FROM group_user_map WHERE group_id IN (2524,128)
            ) thegroups
            GROUP BY user_id
            HAVING counter = 4
    ) Sub1
    LEFT OUTER JOIN (SELECT user_id FROM group_user_map WHERE group_id IN (2572)) Sub2
    ON group_user_map.user_id = Sub2.user_id
    WHERE Sub2.user_id IS NULL
) Sub3
ON  users_table.username = Sub3.user_id

或者避免使用 COUNT 来检查所有 4 个表中是否存在用户 ID,而是使用内部联接

SELECT * 
FROM users_table
INNER JOIN 
(
    SELECT Sub1.user_id 
    FROM (
        SELECT z.user_id   
        FROM (
            SELECT distinct(user_id) FROM group_user_map WHERE group_id IN (2601,119)) z
            INNER JOIN
            (SELECT distinct(user_id) FROM group_user_map WHERE group_id IN (58,226)) y ON z.user_id = y.user_id
            INNER JOIN
            (SELECT distinct(user_id) FROM group_user_map WHERE group_id IN (1299,525)) x ON z.user_id = x.user_id
            INNER JOIN
            (SELECT distinct(user_id) FROM group_user_map WHERE group_id IN (2524,128)) w ON z.user_id = w.user_id
    ) Sub1
    LEFT OUTER JOIN (SELECT user_id FROM group_user_map WHERE group_id IN (2572)) Sub2
    ON group_user_map.user_id = Sub2.user_id
    WHERE Sub2.user_id IS NULL
) Sub3
ON  users_table.username = Sub3.user_id

稍微清理一下第二个查询

SELECT * 
FROM users_table
INNER JOIN 
(
    SELECT z.user_id   
    FROM (SELECT distinct(user_id) FROM group_user_map WHERE group_id IN (2601,119)) z
    INNER JOIN (SELECT distinct(user_id) FROM group_user_map WHERE group_id IN (58,226)) y 
    ON z.user_id = y.user_id
    INNER JOIN (SELECT distinct(user_id) FROM group_user_map WHERE group_id IN (1299,525)) x 
    ON z.user_id = x.user_id
    INNER JOIN (SELECT distinct(user_id) FROM group_user_map WHERE group_id IN (2524,128)) w 
    ON z.user_id = w.user_id
    LEFT OUTER JOIN (SELECT user_id FROM group_user_map WHERE group_id IN (2572)) Sub2
    ON z.user_id = Sub2.user_id
    WHERE Sub2.user_id IS NULL
) Sub3
ON  users_table.username = Sub3.user_id

在下面的评论中使用您的 SQL,可以将其清理为:-

select SQL_NO_CACHE id 
from users_table 
INNER JOIN ( SELECT distinct(user_id) FROM group_user_map WHERE group_id IN (0, 67) ) ij1 
ON users_table.username = ij1.user_id 
LEFT OUTER JOIN ( SELECT user_id FROM group_user_map WHERE group_id IN (0) ) Sub2 
ON users_table.username = Sub2.user_id 
WHERE Sub2.user_id IS NULL 

以同样的方式清理我的 SQL:-

SELECT users_table.* 
FROM users_table
INNER JOIN (SELECT distinct(user_id) FROM group_user_map WHERE group_id IN (2601,119)) z ON users_table.username = z.user_id
INNER JOIN (SELECT distinct(user_id) FROM group_user_map WHERE group_id IN (58,226)) y ON users_table.username = y.user_id
INNER JOIN (SELECT distinct(user_id) FROM group_user_map WHERE group_id IN (1299,525)) x ON users_table.username = x.user_id
INNER JOIN (SELECT distinct(user_id) FROM group_user_map WHERE group_id IN (2524,128)) w ON users_table.username = w.user_id
LEFT OUTER JOIN (SELECT user_id FROM group_user_map WHERE group_id IN (2572)) Sub2 ON users_table.username = Sub2.user_id
WHERE Sub2.user_id IS NULL

删除子选择并直接进行连接(可能有帮助或有阻碍,怀疑这取决于每组 group_id 记录有多少重复的 user_id 记录)

SELECT DISTINCT users_table.* 
FROM users_table
INNER JOIN group_user_map z ON users_table.username = z.user_id AND z.group_id IN (2601,119)
INNER JOIN group_user_map y ON users_table.username = y.user_id AND y.group_id IN (58,226)
INNER JOIN group_user_map x ON users_table.username = x.user_id AND x.group_id IN (1299,525)
INNER JOIN group_user_map w ON users_table.username = w.user_id AND w.group_id IN (2524,128)
LEFT OUTER JOIN group_user_map Sub2 ON users_table.username = Sub2.user_id AND Sub2.group_id IN (2572)
WHERE Sub2.user_id IS NULL
于 2013-05-01T09:15:26.620 回答
0

当您说“我想要组 1、3 和 8 中的所有用户”然后写下时,您的意思并不完全清楚

select distinct(user_id) from group_user_map where group_id in (58,226)

因为英语建议您需要一个同时属于所有三个组的用户,但 SQL 会为您提供属于任一组中的用户。所以你需要更清楚你到底想要什么。

有点难以相信您正在尝试查找所有 4 个超级组中的用户,每个超级组正好由 2 个组组成。这让我质疑你在做什么以及为什么。

根据您真正要遇到的情况,我可以想到几种不同的方法。显然,最简单的方法是将其分解为多个查询并将结果组合到您的代码中。如果组表不太大,您可以自动加入组表,但可能太大而无法加入 3 次。你可能会得到NOT EXISTS比 with更好的性能,NOT IN但可能不会。您可以尝试进一步利用聚合函数和CASE函数来计算中间表中的成功值,但这变得非常疯狂。更有可能你最好重新设计你的数据结构。

我看到您现有解决方案的主要问题是您创建的大量临时表。通常,您将需要某种临时表来执行如此复杂的操作,因此我将专注于将其限制为两个表,每个表都小于关系表。

于 2013-04-28T02:14:18.487 回答
0

这是正确的查询吗

  select * from users_table where username IN    
            (
(select distinct(user_id) from group_user_map where group_id in (2601,119)) a 
inner join
(select distinct(user_id) from group_user_map where group_id in (58,226)) b 
on a.user_id = b.user_id inner join 
(select distinct(user_id) from group_user_map where group_id in (1299,525)) c 
on a.user_id = c.user_id inner join 
(select distinct(user_id) from group_user_map where group_id in (2524,128)) d
on a.user_id = d.user_id 
)  and user_id  not in (select user_id from group_user_map where group_id in (2572))

我没有将所有联合并最终使用计数器为 4 进行过滤,而是替换为相交。请检查结果是否正确并且运行速度快?

维尼特

于 2013-05-02T11:36:04.970 回答