是否有一种自动方法可以从许多表中删除与给定条件匹配的许多行?
Posted
技术标签:
【中文标题】是否有一种自动方法可以从许多表中删除与给定条件匹配的许多行?【英文标题】:Is there an automatic way to delete many rows matching a given condition from many tables? 【发布时间】:2020-07-31 17:14:29 【问题描述】:我有一个 Postgres 数据库,其中包含许多表,每个表包含许多行,这些不同的表之间有许多各种类型的关系。
如果我在所有这些表中添加一个新列 should_delete
,是否有一些自动方法可以删除所有行 WHERE should_delete = true
,而无需手动指定应从中删除行的每个表,或手动排序查询,以便在引用它们的其他行之前删除行(从而避免由于约束失败而导致的错误,例如,通过在包含外键的行之前删除由外键引用的行,即使两行都包含 should_delete = true
并且应该被删除)?
这最终会导致所有标记为 should_delete = true
的行都被删除,并且没有标记为 should_delete = false
的行被删除,但只有在无法在不违反约束的情况下仅删除标记为 should_delete = true
的行时才会导致错误.
如果没有纯粹的 Postgres 方式来实现这一点,我也在使用 Python 和 Django 来访问这个数据库,所以如果有一个 Python 包可以用来做到这一点也是可以接受的。
【问题讨论】:
如果表之间有FOREIGN KEY
关系,为什么不使用ON DELETE CASCADE
?然后从父项中删除,子项记录也被删除。
@AdrianKlaver 我对此持谨慎态度的几个原因:我不想删除所有引用父项的记录,只删除带有should_delete = true
的记录;我不一定知道在每种情况下要从哪个父级开始,它可能是在不同表中相互引用的任意一组行;并且这种行为是我想为单个功能/在单个事务中执行的特殊情况,如果要删除某些***父记录,我会发现在所有其他情况下级联删除所有数据是非常危险的.
老实说,我认为这整件事是灾难的根源,也是计划不周的标志。您冒着忘记should_delete = true
的记录以及在条件发生变化时删除您不想删除的记录的巨大风险。我赞成通过在每个查询中选择它们来删除所需记录的显式方法,而不是依赖某些您可能不记得已设置的信息。
@AdrianKlaver 是对的,但让我们深入研究一下这个问题 :-) 这意味着如果您的 TableA 依赖于 TableB 但只有在 TableA 记录中有 should_delete = True ,这意味着您不应该删除它记录,因为 TableB 中的记录 should_delete 为 False(或为空或其他) - 这就是您要实现的目标吗?
感谢您的想法@AdrianKlaver - 我认为在不了解存在哪些约束、数据的重要性等详细信息的情况下,很难说特定的数据库设计有多好。这是对我正在尝试做的事情的有意最小化的描述,只是为了看看是否有一些简单的方法可以实现这一点,我也在考虑其他选择。
【参考方案1】:
尝试按照选择获取包含您的列的表列表
SELECT table_name
FROM information_schema.columns
WHERE column_name = 'should_delete'
ORDER BY table_schema, table_name;
循环遍历结果并继续为每个表执行删除语句
我从这篇文章中获得灵感:https://kb.objectrocket.com/postgresql/postgres-list-tables-with-python-1023
第二种可能性是当您从选择中获得结果时生成带有实际删除语句的 SQL 文件 - 如下所示:
with open("delete_statements.sql", "w") as myFile:
for table in resultFromPostgresqlSelect:
myFile.write("DELTE FROM WHERE should_delete = true;\n".format(table))
编辑 - 经过几次 cmet 和澄清
您想要的是使用 python 连接到 PostgreSQL 并运行以下查询
SELECT
tc.table_schema,
tc.constraint_name,
tc.table_name,
kcu.column_name,
ccu.table_schema AS foreign_table_schema,
ccu.table_name AS foreign_table_name,
ccu.column_name AS foreign_column_name
FROM
information_schema.table_constraints AS tc
JOIN information_schema.key_column_usage AS kcu
ON tc.constraint_name = kcu.constraint_name
AND tc.table_schema = kcu.table_schema
JOIN information_schema.constraint_column_usage AS ccu
ON ccu.constraint_name = tc.constraint_name
AND ccu.table_schema = tc.table_schema
WHERE constraint_type = 'FOREIGN KEY';
它将为您提供以下外键列表和它们所在的表列表 - 以及更重要的是 - 引用这些表的列名
table_schema | constraint_name | table_name | column_name | foreign_table_schema | foreign_table_name | foreign_column_name
--------------+-----------------+------------+----------------------+----------------------+--------------------+---------------------
public | fk_table_a_id | table_b | reference_table_a_id | public | table_a | id
从此,您将遍历每个foreign_table(在本例中为table_a)并选择所有应该删除=true 的ID,然后对该表的引用表执行相同的操作。 一旦你有了这些列表(在我的例子中是两个列表 - 一个来自 table_a 的 ID 列表和一个来自 table_b 的 ID 列表),你将加入它们并只找到两边的那些 - 你可以从两个表中删除的那些行
在我的例子中,我会生成这两个 SQL 并为它们获取结果
SELECT id FROM table_a WHERE should_delete = true;
SELECT reference_table_a_id FROM table_b WHERE should_delete = true;
然后我可以匹配这些 ID,并且我有从 table_a 和 table_b 中删除的列表
或者甚至更好 - 您生成删除查询,该查询将在表之间连接 - 有关详细信息,请参阅以下帖子PostgreSQL delete with inner join
这是解决您问题的pythonic/动态方式。如果您有任何问题/建议,请告诉我
在下面添加这两个表的定义
CREATE TABLE table_a (id INT GENERATED ALWAYS AS IDENTITY, something int, should_delete int DEFAULT 0,
PRIMARY KEY(id));
CREATE TABLE table_b (id INT GENERATED ALWAYS AS IDENTITY, something_else int, should_delete int DEFAULT 0, reference_table_a_id INT,
PRIMARY KEY(ID),
CONSTRAINT fk_table_a_id
FOREIGN KEY(reference_table_a_id)
REFERENCES table_a(id)
);
【讨论】:
感谢您的建议 - 我认为这个问题有 2 个棘手的部分:动态查找应该删除的表,然后以正确的顺序从这些表中删除,这样就不会违反过程(如果可能的话)。这回答了第一部分,但我认为如果以错误的顺序删除内容,我仍然会遇到第二部分的问题,因为没有什么可以确保语句以最佳顺序运行。以上是关于是否有一种自动方法可以从许多表中删除与给定条件匹配的许多行?的主要内容,如果未能解决你的问题,请参考以下文章