Ste*_*e D 7 postgresql foreign-key referential-integrity postgresql-9.4
我使用 PostgreSQL 中的以下两个表来保存我读过的书籍的数据库:
CREATE TABLE authors (
id SERIAL PRIMARY KEY,
name text
);
CREATE TABLE books (
id SERIAL PRIMARY KEY,
title text,
author_id integer REFERENCES authors(id) ON UPDATE CASCADE ON DELETE CASCADE,
UNIQUE(title, author_id)
);
Run Code Online (Sandbox Code Playgroud)
现在,在浏览我的作者列表时,我发现了以下两个条目:
id | name
----------
1 | Mark Twain
2 | Samuel Clemens
Run Code Online (Sandbox Code Playgroud)
我想要做的是删除“马克吐温”条目,并有效地更新所有引用“马克吐温”的书籍以引用“塞缪尔克莱门斯”。我知道我可以手动执行此操作,但我想要一个有效的解决方案,无论哪些表正在引用authors(id)
我想过这样做(在交易中):
id
为 2,让我们UPDATE CASCADE
负责更改引用。但这会遇到一些问题,主要是:
DELETE CASCADE
让我担心的第二个步骤还有一个更微妙的问题,可以用我(精心策划的)books
表格的一部分来说明:
id | title | author_id
------------------------------------
1 | "Huckleberry Finn" | 1
2 | "Huckleberry Finn" | 2
Run Code Online (Sandbox Code Playgroud)
在这里,即使我的两个步骤成功了,我就违反了UNIQUE
上contstraint books
。
有没有办法做到这一点,并解决大部分/所有这些问题?使用 Postgres 9.4。
假设您只想books
在合并重复作者后删除重复项。
BEGIN;
LOCK books, authors;
CREATE TEMP TABLE dupes ON COMMIT DROP AS (SELECT 2 AS dupe, 1 AS org);
DELETE FROM books b -- delete duplicate books
USING dupes d
WHERE b.author_id = d.dupe
AND EXISTS (
SELECT 1
FROM books
WHERE title = b.title
AND author_id = d.org
);
UPDATE books b -- now we relink all remaining books
SET author_id = d.org
FROM dupes d
WHERE b.author_id = d.dupe;
DELETE FROM authors a -- now we can delete all dupes
USING dupes d
WHERE a.id = d.dupe;
COMMIT;
Run Code Online (Sandbox Code Playgroud)
临时表可以容纳许多行以一次删除许多重复项。
对每个引用的表重复前两个步骤authors.id
。如果有很多我会动态创建和执行语句......
我明确锁定表以避免并发干扰。
一个基本函数可能如下所示:
CREATE OR REPLACE FUNCTION f_remove_dupe(_tbl text, _col text, _dupe int, _org int)
RETURNS void AS
$func$
DECLARE
_ftbl text;
_fcol text;
BEGIN
FOR _ftbl, _fcol IN
-- table and column name behind all referencing FKs
SELECT c.conrelid::regclass::text, f.attname
FROM pg_attribute a
JOIN pg_constraint c ON a.attrelid = c.confrelid AND a.attnum = c.confkey[1]
JOIN pg_attribute f ON f.attrelid = c.conrelid AND f.attnum = c.conkey[1]
WHERE a.attrelid = _tbl::regclass
AND a.attname = _col
AND c.contype = 'f'
LOOP
EXIT WHEN _ftbl IS NULL; -- skip if not found
EXECUTE format('
UPDATE %1$s
SET %2$I = $2
WHERE %2$I = $1'
, _ftbl, _fcol)
USING _dupe, _org;
END LOOP;
EXECUTE format('
DELETE FROM %I WHERE %I = $1'
, _tbl, _col)
USING _dupe;
END
$func$ LANGUAGE plpgsql;
Run Code Online (Sandbox Code Playgroud)
称呼:
SELECT f_remove_dupe('authors', 'id', 2, 1);
Run Code Online (Sandbox Code Playgroud)
这个简单的版本...
UNIQUE
引用表中的约束。适应您的要求。
有关的:
归档时间: |
|
查看次数: |
4247 次 |
最近记录: |