Postgres - 降低值后查找重复值
Postgres - Find duplicate values after lowering the values
你好 Whosebug 用户...我有一个棘手的情况,我还没有找到答案。也许你能帮帮我。
数据库:PostgreSQL 8.4(无法升级)
在这个数据库中,有一个userstable。遗憾的是,用户在创建用户配置文件时可以提供的用户名区分大小写,因此用户名 Alex 与用户名 alex[=40 不同=].
新系统即将推出,用户名不再区分大小写。我试图找到所有在旧系统中被视为重复的用户名。这样我们就可以联系他们,让他们手动更新用户名,然后将他们的用户迁移到更新的系统(没有用户名冲突)。
我有以下查询,它将显示每个用户名与 "lower()" 函数匹配的计数。
select count(*), lower(username)
from users
where deleted = false
group by lower(username) having count(*) > 1
这个 returns 结果如下:
|count|lower |
|-----+--------+
|3 |alex |
|2 |george |
我需要做的是将这些数据放入一个临时文件 table 并显示所有这些重复的用户和其他详细信息,以便我们有一个列表来处理。
我找到了部分临时 table,但我的主要问题是:如何获得所有这些重复项的不同值?所以在长 运行 中,我得到的结果如下所示(如果可能,甚至可能没有临时 table):
|lower |username|
|-------+--------+
|alex |Alex |
|alex |alex |
|george |georGe |
|george |George |
限制:
- 我无法从 8.4 更改 postgres 的版本
- 有些重复的结果会超过 2 次(我目前看到的最多是 3 次)
- 由于必须通知用户,因此除了事先联系他们之外没有其他方法可以更改数据(这就是需要列表的原因)
感谢您提供的任何 suggestions/feedback。
我通常会使用 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 获取 "earliest" 用户(假设有一个不是 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;
这个怎么样。只需将上面的列表生成为 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)
你好 Whosebug 用户...我有一个棘手的情况,我还没有找到答案。也许你能帮帮我。
数据库:PostgreSQL 8.4(无法升级)
在这个数据库中,有一个userstable。遗憾的是,用户在创建用户配置文件时可以提供的用户名区分大小写,因此用户名 Alex 与用户名 alex[=40 不同=].
新系统即将推出,用户名不再区分大小写。我试图找到所有在旧系统中被视为重复的用户名。这样我们就可以联系他们,让他们手动更新用户名,然后将他们的用户迁移到更新的系统(没有用户名冲突)。
我有以下查询,它将显示每个用户名与 "lower()" 函数匹配的计数。
select count(*), lower(username)
from users
where deleted = false
group by lower(username) having count(*) > 1
这个 returns 结果如下:
|count|lower |
|-----+--------+
|3 |alex |
|2 |george |
我需要做的是将这些数据放入一个临时文件 table 并显示所有这些重复的用户和其他详细信息,以便我们有一个列表来处理。
我找到了部分临时 table,但我的主要问题是:如何获得所有这些重复项的不同值?所以在长 运行 中,我得到的结果如下所示(如果可能,甚至可能没有临时 table):
|lower |username|
|-------+--------+
|alex |Alex |
|alex |alex |
|george |georGe |
|george |George |
限制:
- 我无法从 8.4 更改 postgres 的版本
- 有些重复的结果会超过 2 次(我目前看到的最多是 3 次)
- 由于必须通知用户,因此除了事先联系他们之外没有其他方法可以更改数据(这就是需要列表的原因)
感谢您提供的任何 suggestions/feedback。
我通常会使用 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 获取 "earliest" 用户(假设有一个不是 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;
这个怎么样。只需将上面的列表生成为 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)