Postgres-降低值后查找重复值

时间:2018-07-20 18:24:41

标签: postgresql duplicates postgresql-8.4

您好,StackOverflow用户...我遇到了一个棘手的情况,但尚未找到答案。也许你可以帮我。

数据库:PostgreSQL 8.4(无法升级)

在此数据库中,有一个用户表。遗憾的是,用户创建用户个人资料时可以提供的用户名区分大小写,因此 Alex 用户名与 alex 用户名不同。

有一个新系统退出,用户名不再区分大小写。我正在尝试查找在旧系统中将被视为重复的所有用户名。这样,我们可以伸出援手,让他们手动更新用户名,然后将其用户迁移到更新的系统(不会与用户名发生冲突)。

我有以下查询,它将通过“ lower()”函数显示与另一个用户名匹配的计数。

select count(*), lower(username)
  from users
  where deleted = false
  group by lower(username) having count(*) > 1

这将返回如下结果:

|count|lower   |
|-----+--------+
|3    |alex    |
|2    |george  |

我需要做的是将这些数据放入临时表中,并显示所有这些重复的用户和其他详细信息,以便我们有一个清单可以通过。

我已经找到了临时表的一部分,但是我的主要问题是:如何获得所有这些重复项的不同值?因此,从长远来看,我得到的结果如下所示(甚至可能没有临时表):

|lower  |username|
|-------+--------+
|alex   |Alex    |
|alex   |alex    |
|george |georGe  |
|george |George  |

限制:

  • 我无法从8.4更改postgres的版本
  • 某些重复项将有2个以上的匹配(到目前为止,我看到的最多是3个)
  • 由于必须通知用户,因此除事先联系他们外,其他任何方式都无法更改数据(这就是为什么需要此列表)

感谢您可能提供的任何建议/反馈。

2 个答案:

答案 0 :(得分:2)

如何?只需将上面的列表生成为CTE,然后将其加入主查询中即可:

WITH dups AS (
    SELECT lower(username) uname, count(*) ucount 
    FROM users 
    WHERE deleted = false 
    GROUP BY lower(username) HAVING count(*) > 1)
SELECT username, uname, ucount 
FROM users INNER JOIN dups ON lower(username) = uname 
WHERE deleted = false
ORDER BY ucount DESC, uname ASC;

 username | uname  | ucount
----------+--------+--------
 Alex     | alex   |      3
 alex     | alex   |      3
 ALEX     | alex   |      3
 GeorGe   | george |      2
 george   | george |      2
(5 rows)

或者,如果您只希望列出受影响的用户的清单,甚至更简单:

SELECT username
FROM users 
WHERE deleted = false AND lower(username) IN (
    SELECT lower(username)
    FROM users
    WHERE deleted = false
    GROUP BY lower(username) HAVING count(*) > 1)
ORDER BY lower(username) ASC;

 username
----------
 Alex
 alex
 ALEX
 GeorGe
 george
(5 rows)

答案 1 :(得分:0)

我通常会使用string_agg,但8.4中似乎不支持。似乎有一种解决方法,但是请注意,由于没有8.4的本地副本,因此我尚未进行测试。这样的事情应该起作用:

select
  (max(u1.username)),
  array_to_string(array_agg(u2.username), ',') as duplicates
  from users u1
         inner join users u2 on u1.id < u2.id
         and lower(u1.username) = lower(u2.username)
         left join users u3 on u1.id > u3.id
         and lower(u1.username) = lower(u3.username)
         and u3.deleted = false
 where u1.deleted = false
   and u2.deleted = false
   and u3.id is null
 group by u1.id;

这将通过ID获得“最早的”用户(假设主键不是username。可以对其进行修改以显示实际的小写用户名,然后在重复项中显示其余用户名)。

编辑:为每个重复项显示一行:

select
  lower(u1.username),
  u2.username
  from users u1
         inner join users u2 on u1.id < u2.id
         and lower(u1.username) = lower(u2.username)
         left join users u3 on u1.id > u3.id
         and lower(u1.username) = lower(u3.username)
         and u3.deleted = false
 where u1.deleted = false
   and u2.deleted = false
   and u3.id is null
order by u1.username;