Nik*_*dun 5 sql postgresql database-design plpgsql sql-delete
假设我们有一个像这样的PostgreSQL表:
CREATE TABLE master (
id INT PRIMARY KEY,
...
);
Run Code Online (Sandbox Code Playgroud)
以及使用外键引用它的许多其他表:
CREATE TABLE other (
id INT PRIMARY KEY,
id_master INT NOT NULL,
...
CONSTRAINT other_id_master_fkey FOREIGN KEY (id_master)
REFERENCES master (id) ON DELETE RESTRICT
);
Run Code Online (Sandbox Code Playgroud)
有没有办法检查(从触发器功能内)主行是否可删除而不实际尝试删除它?显而易见的方法是逐个对所有引用表执行SELECT,但我想知道是否有更简单的方法.
我需要这个的原因是我有一个包含分层数据的表,其中任何行都可以有子行,并且只有层次结构中最低的子行可以被其他表引用.因此,当一行即将成为父行时,我需要检查它是否已在任何地方引用.如果是,则它不能成为父行,并且拒绝插入新的子行.
您可以尝试删除行并回滚效果.您不希望在触发器函数中执行此操作,因为任何异常都会取消对数据库的所有持久更改.请考虑手册中的引用:
当
EXCEPTION
子句捕获错误时,PL/pgSQL函数的局部变量保持与发生错误时一样,但是回滚对块中持久数据库状态的所有更改.
大胆强调我的.
但是您可以将其包装到单独的块或单独的plpgsql函数中并捕获异常,以防止对main(触发器)函数产生影响.
CREATE OR REPLACE FUNCTION f_can_del(_id int)
RETURNS boolean AS
$func$
BEGIN
DELETE FROM master WHERE master_id = _id; -- DELETE is always rolled back
IF NOT FOUND THEN
RETURN NULL; -- ID not found, return NULL
END IF;
RAISE SQLSTATE 'MYERR'; -- If DELETE, raise custom exception
EXCEPTION
WHEN FOREIGN_KEY_VIOLATION THEN
RETURN FALSE;
WHEN SQLSTATE 'MYERR' THEN
RETURN TRUE;
-- other exceptions are propagated as usual
END
$func$ LANGUAGE plpgsql;
Run Code Online (Sandbox Code Playgroud)
返回TRUE
/ FALSE
/ NULL
表示该行可以删除/不可删除/不存在.
可以轻松地使此函数动态化以测试任何表/列/值.
在PostgreSQL 9.2中,您还可以报告哪个表被阻止.
在目前的PostgreSQL 9.3提供了更详细的信息.
为什么在评论中发布的动态函数尝试失败?手册中的引用应该给出一个线索:
请特别注意,
EXECUTE
更改输出GET DIAGNOSTICS
,但不会更改FOUND
.
它适用于GET DIAGNOSTICS
:
CREATE OR REPLACE FUNCTION f_can_del(_tbl regclass, _col text, _id int)
RETURNS boolean AS
$func$
DECLARE
_ct int; -- to receive count of deleted rows
BEGIN
EXECUTE format('DELETE FROM %s WHERE %I = $1', _tbl, _col)
USING _id; -- exception if other rows depend
GET DIAGNOSTICS _ct = ROW_COUNT;
IF _ct > 0 THEN
RAISE SQLSTATE 'MYERR'; -- If DELETE, raise custom exception
ELSE
RETURN NULL; -- ID not found, return NULL
END IF;
EXCEPTION
WHEN FOREIGN_KEY_VIOLATION THEN
RETURN FALSE;
WHEN SQLSTATE 'MYERR' THEN
RETURN TRUE;
-- other exceptions are propagated as usual
END
$func$ LANGUAGE plpgsql;
Run Code Online (Sandbox Code Playgroud)
在它的同时,我使它完全动态,包括列的数据类型(当然,它必须匹配给定的列).我正在为此目的使用多态类型anyelement
.在这个相关答案中有更多解释:
如何编写一个返回文本或整数值的函数?
我还使用format()
和类型参数regclass
来防范SQLi.关于dba.SE的相关答案的详细解释.
归档时间: |
|
查看次数: |
1977 次 |
最近记录: |