CASCADE DELETE just once
我有一个Postgresql数据库,我想在其上进行一些级联删除。 但是,表未使用ON DELETE CASCADE规则进行设置。 有什么方法可以执行删除并告诉Postgresql只将它级联一次吗? 相当于的东西
1 | DELETE FROM some_table CASCADE; |
这个旧问题的答案似乎没有这样的解决方案存在,但我想我明确地问这个问题只是为了
不能。只需编写一个想要级联的表的delete语句即可。
1 2 | DELETE FROM some_child_table WHERE some_fk_field IN (SELECT some_id FROM some_Table); DELETE FROM some_table; |
如果您确实需要
USE WITH CARE - 这将删除在
Postgres使用TRUNCATE命令支持
1 | TRUNCATE some_table CASCADE; |
这是事务性的(即可以回滚),尽管它与其他并发事务没有完全隔离,并且还有其他一些注意事项。阅读文档了解详细信息。
我写了一个(递归)函数来删除任何基于其主键的行。我写这个是因为我不想创建我的约束作为"删除级联"。我希望能够删除复杂的数据集(作为DBA),但不允许我的程序员能够在不考虑所有影响的情况下级联删除。
我还在测试这个功能,因此可能存在错误 - 但如果您的数据库具有多列主要(因此是外部)键,请不要尝试它。此外,键都必须能够以字符串形式表示,但它可以以没有该限制的方式编写。我无论如何都非常谨慎地使用这个函数,我过分重视我的数据以启用对所有内容的级联约束。
基本上,这个函数在模式,表名和主值(以字符串形式)中传递,它将通过在该表上查找任何外键并确保数据不存在来开始 - 如果存在,则以递归方式调用自己发现的数据。它使用已标记为删除的数据数组来防止无限循环。请测试一下,让我知道它对你有用。注意:它有点慢。
我称之为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 | CREATE OR REPLACE FUNCTION delete_cascade(p_schema VARCHAR, p_table VARCHAR, p_key VARCHAR, p_recursion VARCHAR[] DEFAULT NULL) RETURNS INTEGER AS $$ DECLARE rx record; rd record; v_sql VARCHAR; v_recursion_key VARCHAR; recnum INTEGER; v_primary_key VARCHAR; v_rows INTEGER; BEGIN recnum := 0; SELECT ccu.column_name INTO v_primary_key FROM information_schema.table_constraints tc JOIN information_schema.constraint_column_usage AS ccu ON ccu.constraint_name = tc.constraint_name AND ccu.constraint_schema=tc.constraint_schema AND tc.constraint_type='PRIMARY KEY' AND tc.table_name=p_table AND tc.table_schema=p_schema; FOR rx IN ( SELECT kcu.table_name AS foreign_table_name, kcu.column_name AS foreign_column_name, kcu.table_schema foreign_table_schema, kcu2.column_name AS foreign_table_primary_key FROM information_schema.constraint_column_usage ccu JOIN information_schema.table_constraints tc ON tc.constraint_name=ccu.constraint_name AND tc.constraint_catalog=ccu.constraint_catalog AND ccu.constraint_schema=ccu.constraint_schema JOIN information_schema.key_column_usage kcu ON kcu.constraint_name=ccu.constraint_name AND kcu.constraint_catalog=ccu.constraint_catalog AND kcu.constraint_schema=ccu.constraint_schema JOIN information_schema.table_constraints tc2 ON tc2.table_name=kcu.table_name AND tc2.table_schema=kcu.table_schema JOIN information_schema.key_column_usage kcu2 ON kcu2.constraint_name=tc2.constraint_name AND kcu2.constraint_catalog=tc2.constraint_catalog AND kcu2.constraint_schema=tc2.constraint_schema WHERE ccu.table_name=p_table AND ccu.table_schema=p_schema AND TC.CONSTRAINT_TYPE='FOREIGN KEY' AND tc2.constraint_type='PRIMARY KEY' ) loop v_sql := 'select '||rx.foreign_table_primary_key||' as key from '||rx.foreign_table_schema||'.'||rx.foreign_table_name||' where '||rx.foreign_column_name||'='||quote_literal(p_key)||' for update'; --raise notice '%',v_sql; --found a foreign key, now find the primary keys for any data that exists in any of those tables. FOR rd IN EXECUTE v_sql loop v_recursion_key=rx.foreign_table_schema||'.'||rx.foreign_table_name||'.'||rx.foreign_column_name||'='||rd.key; IF (v_recursion_key = any (p_recursion)) THEN --raise notice 'Avoiding infinite loop'; ELSE --raise notice 'Recursing to %,%',rx.foreign_table_name, rd.key; recnum:= recnum +delete_cascade(rx.foreign_table_schema::VARCHAR, rx.foreign_table_name::VARCHAR, rd.key::VARCHAR, p_recursion||v_recursion_key); END IF; END loop; END loop; BEGIN --actually delete original record. v_sql := 'delete from '||p_schema||'.'||p_table||' where '||v_primary_key||'='||quote_literal(p_key); EXECUTE v_sql; GET diagnostics v_rows= ROW_COUNT; --raise notice 'Deleting %.% %=%',p_schema,p_table,v_primary_key,p_key; recnum:= recnum +v_rows; exception WHEN others THEN recnum=0; END; RETURN recnum; END; $$ LANGUAGE PLPGSQL; |
如果我理解正确,你应该能够通过删除外键约束,添加一个新的(将级联),执行你的东西,并重新创建限制外键约束来做你想要的。
例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | testing=# CREATE TABLE a (id INTEGER PRIMARY KEY); NOTICE: CREATE TABLE / PRIMARY KEY will CREATE implicit INDEX"a_pkey" FOR TABLE"a" CREATE TABLE testing=# CREATE TABLE b (id INTEGER REFERENCES a); CREATE TABLE -- put some data in the table testing=# INSERT INTO a VALUES(1); INSERT 0 1 testing=# INSERT INTO a VALUES(2); INSERT 0 1 testing=# INSERT INTO b VALUES(2); INSERT 0 1 testing=# INSERT INTO b VALUES(1); INSERT 0 1 -- restricting works testing=# DELETE FROM a WHERE id=1; ERROR: UPDATE OR DELETE ON TABLE"a" violates FOREIGN KEY CONSTRAINT"b_id_fkey" ON TABLE"b" DETAIL: KEY (id)=(1) IS still referenced FROM TABLE"b". -- find the name of the constraint testing=# \d b; TABLE"public.b" COLUMN | TYPE | Modifiers --------+---------+----------- id | INTEGER | Foreign-KEY constraints: "b_id_fkey" FOREIGN KEY (id) REFERENCES a(id) -- drop the constraint testing=# ALTER TABLE b DROP CONSTRAINT b_a_id_fkey; ALTER TABLE -- create a cascading one testing=# ALTER TABLE b ADD FOREIGN KEY (id) REFERENCES a(id) ON DELETE cascade; ALTER TABLE testing=# DELETE FROM a WHERE id=1; DELETE 1 testing=# SELECT * FROM a; id ---- 2 (1 ROW) testing=# SELECT * FROM b; id ---- 2 (1 ROW) -- it works, do your stuff. -- [stuff] -- recreate the previous state testing=# \d b; TABLE"public.b" COLUMN | TYPE | Modifiers --------+---------+----------- id | INTEGER | Foreign-KEY constraints: "b_id_fkey" FOREIGN KEY (id) REFERENCES a(id) ON DELETE CASCADE testing=# ALTER TABLE b DROP CONSTRAINT b_id_fkey; ALTER TABLE testing=# ALTER TABLE b ADD FOREIGN KEY (id) REFERENCES a(id) ON DELETE RESTRICT; ALTER TABLE |
当然,为了您的心理健康,您应该将这样的东西抽象到程序中。
我无法评论苍白的答案,所以我添加了自己的答案。
Palehorse logick可以,但是大数据集的效率可能会很差。
1 2 3 | DELETE FROM some_child_table sct WHERE EXISTS (SELECT FROM some_Table st WHERE sct.some_fk_fiel=st.some_id ); DELETE FROM some_table; |
如果您在列上有索引并且数据集大于几个记录则会更快。
我接受了Joe Love的回答,并使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | CREATE OR REPLACE FUNCTION delete_cascade(p_schema VARCHAR, p_table VARCHAR, p_keys VARCHAR, p_subquery VARCHAR DEFAULT NULL, p_foreign_keys VARCHAR[] DEFAULT array[]::VARCHAR[]) RETURNS INTEGER AS $$ DECLARE rx record; rd record; v_sql VARCHAR; v_subquery VARCHAR; v_primary_key VARCHAR; v_foreign_key VARCHAR; v_rows INTEGER; recnum INTEGER; BEGIN recnum := 0; SELECT ccu.column_name INTO v_primary_key FROM information_schema.table_constraints tc JOIN information_schema.constraint_column_usage AS ccu ON ccu.constraint_name = tc.constraint_name AND ccu.constraint_schema=tc.constraint_schema AND tc.constraint_type='PRIMARY KEY' AND tc.table_name=p_table AND tc.table_schema=p_schema; FOR rx IN ( SELECT kcu.table_name AS foreign_table_name, kcu.column_name AS foreign_column_name, kcu.table_schema foreign_table_schema, kcu2.column_name AS foreign_table_primary_key FROM information_schema.constraint_column_usage ccu JOIN information_schema.table_constraints tc ON tc.constraint_name=ccu.constraint_name AND tc.constraint_catalog=ccu.constraint_catalog AND ccu.constraint_schema=ccu.constraint_schema JOIN information_schema.key_column_usage kcu ON kcu.constraint_name=ccu.constraint_name AND kcu.constraint_catalog=ccu.constraint_catalog AND kcu.constraint_schema=ccu.constraint_schema JOIN information_schema.table_constraints tc2 ON tc2.table_name=kcu.table_name AND tc2.table_schema=kcu.table_schema JOIN information_schema.key_column_usage kcu2 ON kcu2.constraint_name=tc2.constraint_name AND kcu2.constraint_catalog=tc2.constraint_catalog AND kcu2.constraint_schema=tc2.constraint_schema WHERE ccu.table_name=p_table AND ccu.table_schema=p_schema AND TC.CONSTRAINT_TYPE='FOREIGN KEY' AND tc2.constraint_type='PRIMARY KEY' ) loop v_foreign_key := rx.foreign_table_schema||'.'||rx.foreign_table_name||'.'||rx.foreign_column_name; v_subquery := 'select"'||rx.foreign_table_primary_key||'" as key from '||rx.foreign_table_schema||'."'||rx.foreign_table_name||'" where"'||rx.foreign_column_name||'"in('||COALESCE(p_keys, p_subquery)||') for update'; IF p_foreign_keys @> ARRAY[v_foreign_key] THEN --raise notice 'circular recursion detected'; ELSE p_foreign_keys := array_append(p_foreign_keys, v_foreign_key); recnum:= recnum + delete_cascade(rx.foreign_table_schema, rx.foreign_table_name, NULL, v_subquery, p_foreign_keys); p_foreign_keys := array_remove(p_foreign_keys, v_foreign_key); END IF; END loop; BEGIN IF (COALESCE(p_keys, p_subquery) <> '') THEN v_sql := 'delete from '||p_schema||'."'||p_table||'" where"'||v_primary_key||'"in('||COALESCE(p_keys, p_subquery)||')'; --raise notice '%',v_sql; EXECUTE v_sql; GET diagnostics v_rows = ROW_COUNT; recnum := recnum + v_rows; END IF; exception WHEN others THEN recnum=0; END; RETURN recnum; END; $$ LANGUAGE PLPGSQL; |
您可以使用它来自动执行此操作,您可以使用
我引用了外键约束的手册:
CASCADE specifies that when a referenced row is deleted, row(s)
referencing it should be automatically deleted as well.
带有级联选项的删除仅适用于定义了外键的表。如果你执行删除,并且它说你不能,因为它会违反外键约束,级联将导致它删除有问题的行。
如果要以这种方式删除关联的行,则需要首先定义外键。另外,请记住,除非您明确指示它开始事务,或者您更改了默认值,否则它将执行自动提交,这可能非常耗时。