Sql 如何以编程方式检查行是否可删除?

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)

假设我们有这样一个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)
                                    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。见:


另一种方法是从目录表中动态创建SELECT语句。考虑相关的答案和.@ NIKSABALDUN:因为<代码>删除<代码>从来没有过,它应该适合。但我不知道你会从中得到什么。基本上,它告诉查询计划器,如果函数被多次调用,它可以在单个查询中重用结果。这种类型的函数可能只调用一次。对于更多的问题,请开始另一个问题。显然,它不能被声明为稳定的,即使所有更改都将回滚。PostgreSQL似乎不允许在稳定函数中使用任何UPDATE或DELETE语句。