我有一个 taccounts
表,其中包含 account_id(PK)
、login_name
、password
、 等列>最后登录
。现在我必须根据新的业务逻辑删除一些重复的条目。
因此,重复的帐户将具有相同的电子邮件
或相同(login_name
和 password
)。必须保留最近一次登录的帐户。
这是我的尝试(一些电子邮件值为空)
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_name
和password
类似
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,请考虑以下路线:
SELECT
将幸存行放入临时表中。- 重新路由 FK 对幸存者的引用
删除
基表中的所有行。- 重新
插入
幸存者。
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/