Sql 如何以编程方式检查行是否可删除?
假设我们有这样一个PostgreSQL表:Sql 如何以编程方式检查行是否可删除?,sql,postgresql,database-design,plpgsql,sql-delete,Sql,Postgresql,Database Design,Plpgsql,Sql Delete,假设我们有这样一个PostgreSQL表: CREATE TABLE master ( id INT PRIMARY KEY, ... ); 以及许多其他使用外键引用它的表: CREATE TABLE other ( id INT PRIMARY KEY, id_master INT NOT NULL, ... CONSTRAINT other_id_master_fkey FOREIGN KEY (id_master)
CREATE TABLE master (
id INT PRIMARY KEY,
...
);
以及许多其他使用外键引用它的表:
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
);
是否有一种方法可以检查(从触发器函数中)主行是否可删除,而不实际尝试删除它?显而易见的方法是逐个选择所有引用表,但我想知道是否有更简单的方法
我需要这样做的原因是,我有一个具有层次结构数据的表,其中任何行都可以有子行,并且只有层次结构中最低的子行可以被其他表引用。因此,当一行即将成为父行时,我需要检查它是否已经在任何地方被引用。如果是,则不能成为父行,并且拒绝插入新的子行。您可以尝试删除该行并回滚效果。您不希望在触发器函数中执行此操作,因为任何异常都会取消对数据库的所有持久化更改 当
异常
子句捕获错误时
PL/pgSQL函数保持错误发生时的状态,但是
块内对持久数据库状态的所有更改都将回滚
粗体强调我的
但您可以将其包装到单独的块或单独的plpgsql函数中,并在其中捕获异常,以防止对主(触发器)函数产生影响
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;
这将返回TRUE
/FALSE
/NULL
,指示该行可以删除/不可以删除/不存在
dbfiddle旧的 可以很容易地将此函数动态化,以测试任何表/列/值 因为您还可以报告阻塞的表。
或者稍后提供更详细的信息 用于任意表、列和类型的泛型函数 为什么你在评论中发布的内容失败了?应该给出一个线索: 请特别注意,
EXECUTE
会更改GET DIAGNOSTICS
的输出,但不会更改FOUND
它与获取诊断功能一起工作:
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;
dbfiddle旧的 在编写时,我将其完全动态化,包括列的数据类型(当然,它必须与给定的列匹配)。我正在为此目的使用。见:
format()
和类型为regclass
的参数来保护SQLi。见: