您好,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 |
限制:
感谢您可能提供的任何建议/反馈。
答案 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;