Postgresql 无法从临时表中进行选择

Postgresql 无法从临时表中进行选择,postgresql,temp-tables,postgresql-9.4,Postgresql,Temp Tables,Postgresql 9.4,我有3个表-T_USER、T_PRIVILEGE和T_USER_PRIVILEGE T_USER_PRIVILEGES是一个引用表,包含从T_USER行到T_PRIVILEGE行的引用。我想从T_USER中删除一行,首先需要从T_USER_权限中删除引用,还需要从T_权限中删除所有引用的行 我想创建一个临时表,其中包含来自T_特权的所有引用行,然后从T_用户特权中删除所有引用,最后从T_特权中删除存储在临时表中的所有行 我尝试创建一个存储过程来完成它: CREATE FUNCTION "SP_D

我有3个表-T_USER、T_PRIVILEGE和T_USER_PRIVILEGE

T_USER_PRIVILEGES是一个引用表,包含从T_USER行到T_PRIVILEGE行的引用。我想从T_USER中删除一行,首先需要从T_USER_权限中删除引用,还需要从T_权限中删除所有引用的行

我想创建一个临时表,其中包含来自T_特权的所有引用行,然后从T_用户特权中删除所有引用,最后从T_特权中删除存储在临时表中的所有行

我尝试创建一个存储过程来完成它:

CREATE FUNCTION "SP_DELETE_USER"(userid character varying) RETURNS void AS
$BODY$CREATE TEMP TABLE temp_privilege_ids
(
    privilege_id VARCHAR(100)
);    

SELECT "PRIVILEGE_ID"
INTO temp_privilege_ids
FROM 
(SELECT * FROM "T_USER_PRIVILEGES"
WHERE "USER_ID" = userid) as foo;

DELETE FROM "T_USER_PRIVILEGES"
WHERE "USER_ID" = userid;

DELETE FROM "T_PRIVILEGE"
WHERE "ID" IN
(SELECT privilege_id FROM temp_privilege_ids);$BODY$
LANGUAGE sql VOLATILE NOT LEAKPROOF;
ALTER FUNCTION public."SP_DELETE_USER"(character varying)
  OWNER TO postgres;
userid是SP的一个参数

当我尝试创建SP时,pgAdmin说:

我到处寻找解释,但没有找到答案。 有人有主意吗

这是参考表:

CREATE TABLE "T_USER_PRIVILEGES" (
  "USER_ID" character varying(100) NOT NULL,
  "PRIVILEGE_ID" character varying(100) NOT NULL,
  CONSTRAINT "PK_T_USER_PRIVILEGES" PRIMARY KEY ("USER_ID", "PRIVILEGE_ID"),
  CONSTRAINT "FK_T_USER_PRIVILEGES_PRIVILEGES" FOREIGN KEY ("PRIVILEGE_ID")
      REFERENCES "T_PRIVILEGE" ("ID") MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE NO ACTION,
  CONSTRAINT "FK_T_USER_PRIVILEGES_USER" FOREIGN KEY ("USER_ID")
      REFERENCES "T_USER" ("ID") MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE NO ACTION
);

CREATE INDEX "FKI_T_USER_PRIVILEGES_PRIVILEGES"
  ON "T_USER_PRIVILEGES" ("PRIVILEGE_ID" COLLATE pg_catalog."default");

语言SQL函数是一次规划的,因此您不能引用任何尚未存在的表。您可以手动创建temp表,以便在创建时通过表面语法检查并允许创建函数,但函数在执行时仍然会失败

您可以使用plpgsql函数来实现您正在尝试的功能,该函数应用了注释中已提供的一些建议@a_horse_,其名称为_no_):

CREATE OR REPLACE FUNCTION "SP_DELETE_USER"(_userid varchar) RETURNS void AS
$func$
BEGIN
   CREATE TEMP TABLE temp_privilege_ids ON COMMIT DROP AS
   SELECT "PRIVILEGE_ID"
   FROM   "T_USER_PRIVILEGES"
   WHERE  "USER_ID" = _userid;

   DELETE FROM "T_USER_PRIVILEGES"
   WHERE "USER_ID" = _userid;

   DELETE FROM "T_PRIVILEGE" t
   USING temp_privilege_ids tmp
   WHERE t."ID" = tmp."PRIVILEGE_ID";
END
$func$ LANGUAGE plpgsql;
但这仍然是不必要的复杂。只需使用:

撇开我对您的数据库设计和命名约定的疑虑不谈

,您的FK约束FK_T_USER_PRIVILEGES_PRIVILEGES_PRIVILEGES似乎指向了错误的方向:在这种情况下,多个用户可以链接到同一个权限,这是有意义的

如果是,请删除此约束并在T_PRIVILEGE.ID上创建一个约束:

然后,由于CASCADE子句,当您删除T_USER_privileges中的行时,特权将自动删除


还是一个奇怪的设计。特权通常是多个用户可以共享的…

不相关,但是:选择。。“插入临时权限”ID应插入“插入临时权限”ID选择。。。。或者更好:只使用一个创建表。。。正如select…在编译时检查表的存在,当您创建函数时,表确实不存在。编译器不知道该表在运行时是否存在。另外:临时表是完全无用的。这可以通过两个简单的delete语句完成,或者:使用on delete cascade在两个表之间创建一个FK约束。如果没有临时的,我就不能这样做,因为我有一个FK,必须先从引用表中删除。另外,这是否基本上意味着我不能在SPs中使用临时表?关于FK,我有两个FK从引用表进入其他两个表,但在这种情况下会删除级联工作?如果其他用户将特权用作well@a_horse_with_no_name:是的。我怀疑这种设置是否合理。只是演示正确的技术。我不能在这里修复损坏的设计。我也会使用不同的名称和数据类型。用户和特权之间的关系是一对多的,这意味着没有其他人会使用特权。@Elliko:如果是这样,那么,您的FK约束FK_T_USER_PRIVILEGES_PRIVILEGES_PRIVILEGES指向了错误的方向:在这种情况下,多个用户可以链接到同一个权限,这是有意义的。这是有意义的。然后我可以简单地使用DELETE CASCADE。谢谢
CREATE OR REPLACE FUNCTION "SP_DELETE_USER"(_userid varchar) RETURNS void AS
$func$
BEGIN
   CREATE TEMP TABLE temp_privilege_ids ON COMMIT DROP AS
   SELECT "PRIVILEGE_ID"
   FROM   "T_USER_PRIVILEGES"
   WHERE  "USER_ID" = _userid;

   DELETE FROM "T_USER_PRIVILEGES"
   WHERE "USER_ID" = _userid;

   DELETE FROM "T_PRIVILEGE" t
   USING temp_privilege_ids tmp
   WHERE t."ID" = tmp."PRIVILEGE_ID";
END
$func$ LANGUAGE plpgsql;
WITH del1 AS (
   DELETE FROM "T_USER_PRIVILEGES"
   WHERE  "USER_ID" = _userid  -- provide userid here
   RETURNING "PRIVILEGE_ID"
   )
DELETE FROM "T_PRIVILEGE" t
USING  del1
WHERE  t."ID" = del1."PRIVILEGE_ID";
ALTER TABLE "T_PRIVILEGE"
ADD CONSTRAINT "FK_T_PRIVILEGE_ID" FOREIGN KEY ("ID")
   REFERENCES "T_USER_PRIVILEGES"("PRIVILEGE_ID") 
   ON UPDATE CASCADE ON DELETE CASCADE;