【问题标题】:How to query for values that share another value in common如何查询共享另一个共同值的值
【发布时间】:2014-06-21 04:16:54
【问题描述】:

服务器上的 Greenplum 4.2.2.4(如 PostgreSQL 8.2)。

我有如下数据:

id    | user
------+------
12345 | bob
12345 | jane
12345 | mary
44455 | user1
44455 | user2
44455 | user3
67890 | bob
53756 | bob
53756 | bob
53756 | bob
25246 | jane
54383 | jane
54383 | jane
54383 | jane

我只想返回多个唯一“用户”值共享“id”的行。但是,我也在根据我感兴趣的“用户”值列表进行查询。例如:

用户在哪里('mary','bob','user2')

我希望查询返回:

id    | user
------+------
12345 | bob
12345 | jane
12345 | mary
44455 | user1
44455 | user2
44455 | user3

我该怎么做?

【问题讨论】:

  • 目前还不清楚您是否要在结果中折叠(id, user) 上的重复项。

标签: sql postgresql greenplum


【解决方案1】:

您可以使用窗口函数来做到这一点:

select id, user
from (select t.*, min(user) over (partition by id) as minuser,
             max(user) over (partition by id) as maxuser
      from table t
     ) t
where minuser <> maxuser;

EDTI:没有窗口函数(我认为自 Postgres 8.1 以来就存在,但我相信 Erwin 在这件事上),你可以用 joingroup by 做同样的事情:

select t.id, t.user
from table t join
     (select user, min(user) as minuser, max(user) as maxuser
      from table t
      group by user
      having min(user) <> max(user)
     ) tu
     on t.user = tu.user;

【讨论】:

  • Answer 不适用于 Postgres 8.2,它还不知道窗口函数。
【解决方案2】:

试试这个解决方案:

在 t1 中,不唯一的重复行(如 )被转换为一条记录。

然后,在外括号中,那些仅与一个用户共享的 id 被过滤(如 或 现在转换为一条记录)。

具有这些 id 的记录就是答案:

select *
    from OriginalTable
    where id in 
        (
        select id 
            from ( 
                select distinct id, user
                    from OriginalTable
                ) as t1
            group by id
            having count(*) > 1
        )

【讨论】:

    【解决方案3】:

    试试这个查询。我在一个有 350 万行的 postgresql 表中进行了测试,大约耗时 1.7 秒。

    select id,
           uname 
    from   (
        select 
               id,
               uname,
               count(*) over (partition by id,uname) as count_of_unique_id_share,
               count(*) over (partition by id) as count_of_id_share 
        from 
               (select * from (select distinct id,uname from <TABLE>) z 
            where  id in (select id from <TABLE> where uname in ('mary','bob','user2')))y ) x 
    where 
            count_of_unique_id_share = 1 and count_of_id_share > 1
    

    【讨论】:

    • Answer 不适用于 Postgres 8.2,它还不知道窗口函数。
    【解决方案4】:
    CREATE TABLE users( id INTEGER NOT NULL
            , username varchar
            );
    
    INSERT INTO users (id, username) VALUES
      (12345 , 'bob' )
    , (12345 , 'jane' )
    , (12345 , 'mary' )
    , (44455 , 'user1' )
    , (44455 , 'user2' )
    , (44455 , 'user3' )
    , (67890 , 'bob' )
    , (53756 , 'bob' )
    , (53756 , 'bob' )
    , (53756 , 'bob' )
    , (25246 , 'jane' )
    , (54383 , 'jane' )
    , (54383 , 'jane' )
    , (54383 , 'jane' )
            ;
    
    SELECT *
    FROM users u1
    WHERE EXISTS (
            SELECT *
            FROM users u2
            -- id must at least have one of these three usernames
            WHERE u2.username IN ('mary','bob','user2')
            AND u2.id = u1.id
            AND EXISTS (
                    SELECT *
                    FROM users u3
                    WHERE u3.id = u2.id
                    -- and there must exist a different username for this id
                    AND u3.username <> u2.username
                    )
            );
    

    结果:

    CREATE TABLE
    INSERT 0 14
      id   | username 
    -------+----------
     12345 | bob
     12345 | jane
     12345 | mary
     44455 | user1
     44455 | user2
     44455 | user3
    (6 rows)
    

    【讨论】:

    • 我的初稿只返回了核心行。 Q 要求所有行共享相同的 id,这反映在我的更新中。
    • 我刚开始的时候还没有看到你的,也不想删除我的。问题是关系划分的另一个极端情况。糟糕,现在我明白了,我忘记了常量 in(...)。 BRB...
    【解决方案5】:

    SQL Fiddle

    select id, "user"
    from
        (
            select id
            from t
            group by id
            having
                count(distinct "user") > 1
                and
                array['mary','bob','user2']::varchar(5)[] && array_agg("user")
        ) s
        inner join
        t using (id)
    order by id, user
    

    【讨论】:

    • (44455, 'user1'), (44455, 'user2') 失败,因为在子查询 s 中排除了 user1
    • @Erwin Fails 在这里这个词太强了,因为不清楚这是否确实是 OP 的意图。
    • 我从演示输出中得出我的解释,因此正确的措辞可能是“产生与显示的结果不同的结果”。
    【解决方案6】:

    Postgres 8.2 没有 窗口函数(在 8.4 版中引入)。
    由于您正在寻找行

    “id”由多个唯一的“用户”值共享。

    SELECT t2.id, t2.user
    FROM   tbl t1
    JOIN   tbl t2 USING (id)    -- retrieve all rows with same id
    WHERE  t1.user IN ('mary','bob','user2')
    AND    EXISTS (
       SELECT 1
       FROM   tbl
       WHERE  id = t1.id
       AND    user <> t1.user   -- at least one other user with same id
       )
    ORDER  BY t2.id, t2.user;
    

    名字是象征性的。不会使用reserved word user 作为标识符。

    这个变体可能更快:

    SELECT id, user
    FROM (
        SELECT id
        FROM   tbl t1
        WHERE  user IN ('mary','bob','user2')
        AND    EXISTS (
            SELECT 1
            FROM   tbl
            WHERE  id = t1.id
            AND    user <> t1.user
            )
        ) sub
    JOIN   tbl USING (id)
    ORDER  BY id, user;
    

    任一查询都根据您的请求返回 所有 行 - 包括完整的重复项。如果您只想要不同的行:

    SELECT DISTINCT id, user ...
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-04-02
      • 1970-01-01
      • 2019-05-04
      • 2018-11-22
      • 2020-12-12
      相关资源
      最近更新 更多