sql - 根据多个条件从表中删除重复项并保留到其他表

标签 sql database postgresql duplicates postgresql-9.2

我有一个 taccounts 表,其中包含 account_id(PK)login_namepassword 等列>最后登录。现在我必须根据新的业务逻辑删除一些重复的条目。 因此,重复的帐户将具有相同的电子邮件 相同(login_namepassword)。必须保留最近一次登录的帐户。

这是我的尝试(一些电子邮件值为空)

DELETE
FROM taccounts
WHERE email is not null and char_length(trim(both ' ' from email))>0 and last_login NOT IN
(
SELECT MAX(last_login)
FROM taccounts
WHERE email is not null and char_length(trim(both ' ' from email))>0 
GROUP BY lower(trim(both ' ' from email)))

login_namepassword类似

DELETE
FROM taccounts
WHERE last_login NOT IN
(
SELECT MAX(last_login)
FROM taccounts
GROUP BY login_name, password)

是否有更好的方法或任何方式来组合这两个单独的查询?

还有一些其他表将 account_id 作为外键。 如何更新这些表的此更改?` 我使用的是 PostgreSQL 9.2.1

编辑:部分电子邮件值为空,部分为空白 ('')。因此,如果两个帐户具有不同的登录名和密码,并且他们的电子邮件为空或空白,那么它们必须被视为两个不同的帐户。

最佳答案

如果大多数行都被删除(大部分是重复的)并且表适合 RAM,请考虑以下路线:

  1. SELECT 将幸存行放入临时表中。
  2. 重新路由 FK 对幸存者的引用
  3. 删除基表中的所有行。
  4. 重新插入幸存者。

1a。提取幸存行

CREATE TEMP TABLE tmp AS
SELECT DISTINCT ON (login_name, password) *
FROM  (
   SELECT DISTINCT ON (email) *
   FROM   taccounts
   ORDER  BY email, last_login DESC
   ) sub
ORDER  BY login_name, password, last_login DESC;

关于DISTINCT ON:

要识别两个不同条件的重复项,请使用子查询依次应用这两个规则。第一步保留具有最新 last_login 的帐户,因此这是“可序列化的”。

检查结果并测试合理性。

SELECT * FROM tmp;

临时表会在 session 结束时自动删除。在 pgAdmin(您似乎正在使用)中,只要编辑器窗口打开, session 就会存在。

1b。 “重复项”更新定义的替代查询

SELECT *
FROM   taccounts t
WHERE  NOT EXISTS (
   SELECT  FROM taccounts t1
   WHERE  ( NULLIF(t1.email, '') = t.email
        OR (NULLIF(t1.login_name, ''), NULLIF(t1.password, '')) = (t.login_name, t.password))
   AND   (t1.last_login, t1.account_id) > (t.last_login, t.account_id)
   );

这不会将任何“重复”列中的 NULL 或空字符串 ('') 视为相同。

行表达式(t1.last_login, t1.account_id)负责处理两个傻瓜可能共享相同last_login的可能性。在这种情况下,选择具有较大 account_id 的账户 - 这是唯一的,因为它是 PK。

2a。如何识别所有传入的 FK

SELECT c.confrelid::regclass::text AS referenced_table
     , c.conname AS fk_name
     , pg_get_constraintdef(c.oid) AS fk_definition
FROM   pg_attribute a 
JOIN   pg_constraint c ON (c.conrelid, c.conkey[1]) = (a.attrelid, a.attnum)
WHERE  c.confrelid = 'taccounts'::regclass   -- (schema-qualified) table name
AND    c.contype  = 'f'
ORDER  BY 1, contype DESC;

仅在外键的第一列上构建。更多相关内容:

或者在选择表taccounts后,在pgAdmin对象浏览器的右侧窗口中检查Dependents骑手。

2b。重新路由到新的主服务器

如果您有引用taccounts的表(传入外键taccounts),您将需要更新所有在删除重复项之前这些字段。
将它们全部重新路由到新的主行:

UPDATE referencing_tbl r
SET    referencing_column = tmp.reference_column
FROM   tmp
JOIN   taccounts t1 USING (email)
WHERE  r.referencing_column = t1.referencing_column
AND    referencing_column IS DISTINCT FROM tmp.reference_column;

UPDATE referencing_tbl r
SET    referencing_column = tmp.reference_column
FROM   tmp
JOIN   taccounts t2 USING (login_name, password)
WHERE  r.referencing_column = t1.referencing_column
AND    referencing_column IS DISTINCT FROM tmp.reference_column;

3。 & 4. 全力以赴

现在,不再引用愚人了。去杀人吧。

ALTER TABLE taccounts DISABLE TRIGGER ALL;
DELETE FROM taccounts;
VACUUM taccounts;
INSERT INTO taccounts
SELECT * FROM tmp;
ALTER TABLE taccounts ENABLE TRIGGER ALL;

在操作期间禁用所有触发器。这避免了在操作期间检查引用完整性。重新激活触发器后,一切都会好起来。我们处理了上述所有传入 FK。 传出 FK 保证是健全的,因为您没有并发写入访问并且所有值之前都已存在。

关于sql - 根据多个条件从表中删除重复项并保留到其他表,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15717674/

相关文章:

Mysql:查找列值以特定子字符串结尾的行

mysql - 对具有 2 个字段的 1 个表进行缓慢的 MySQL 查询

mysql - 忘记添加主键

sql - 所有 SQL 查询都可以在关系代数、域和元组关系演算中表示吗

sql - INSERT INTO 插入一行全是空值

sql - 如何从sql列中提取文本

SQLite 查询以获取最近的日期时间

mysql - INT 列值乱序超过 10 个字符

java - 使用 PostgreSQL 和 Spring Boot 时是否有规则禁止将其实体类命名为 "User"?

python - SQLAlchemy + PostgreSQL + PG 正则表达式