视图如何取决于postgres中的主键约束

Iva*_*ila 5 postgresql constraints sql-view postgresql-9.5

有时,在批量数据加载时,建议临时删除表上的约束和索引.但是当我这样做时,我遇到了一些依赖问题.我的简化示例:

CREATE TABLE public.t_place_type
(
  id serial NOT NULL,
  c_name character varying(100),
  CONSTRAINT pk_t_place_type PRIMARY KEY (id)
);

CREATE TABLE public.t_place
(
  id serial NOT NULL,
  c_name character varying(50),
  id_place_type integer,
  CONSTRAINT pk_t_place PRIMARY KEY (id),
  CONSTRAINT fk_t_place_t_place_type FOREIGN KEY (id_place_type)
      REFERENCES public.t_place_type (id) MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE NO ACTION
);

CREATE OR REPLACE VIEW public.v_place AS 
 SELECT p.id,
    p.c_name,
    pt.c_name AS c_place_type
   FROM t_place p
     LEFT JOIN t_place_type pt ON pt.id = p.id_place_type
  GROUP BY p.id, pt.id, p.c_name;
Run Code Online (Sandbox Code Playgroud)

我的剧本:

ALTER TABLE public.t_place DROP CONSTRAINT fk_t_place_t_place_type;
ALTER TABLE public.t_place DROP CONSTRAINT pk_t_place;
ALTER TABLE public.t_place_type DROP CONSTRAINT pk_t_place_type;
Run Code Online (Sandbox Code Playgroud)

当我运行它时,我收到错误:

错误:不能在表t_place_type上删除约束pk_t_place_type,因为其他对象依赖于它DETAIL:view v_place依赖于表t_place_type上的约束pk_t_place_type

我很奇怪,视图可以取决于某些约束.AFAIK postgres不会缓存视图的执行计划.

当我以这种方式改变我的观点时:

CREATE OR REPLACE VIEW public.v_place AS 
 SELECT p.id,
    p.c_name AS c_name,
    pt.c_name AS c_place_type
   FROM t_place p
     LEFT JOIN t_place_type pt ON pt.id = p.id_place_type;
Run Code Online (Sandbox Code Playgroud)

依赖关系消失了,我的脚本成功执行了.

所以我的问题是:视图和约束之间存在这种依赖关系的原因是什么.

编辑

这里https://www.postgresql.org/docs/9.5/static/sql-select.html#SQL-GROUPBY postgres docs说:

当存在GROUP BY或存在任何聚合函数时,SELECT列表表达式无法引用未聚合的列,但聚合函数内或未分组的列在功能上依赖于分组列,因为否则会有更多比未归属列返回的一个可能值.如果分组列(或其子集)是包含未分组列的表的主键,则存在功能依赖性.

这是这种行为的原因吗?即使我的观点中没有未分组的列?

Łuk*_*ski 1

如果您将 PK 放在 上,您从视图中的查询将不起作用public.t_place_type

它会产生这个错误:

ERROR:  column "pt.c_name" must appear in the GROUP BY clause or be used in an aggregate function
LINE 3:     pt.c_name AS c_place_type
            ^
Run Code Online (Sandbox Code Playgroud)

这是因为,从您的文档引用来看,A functional dependency exists if the grouped columns (or a subset thereof) are the primary key of the table containing the ungrouped column.

Postgres 知道 PK 代表唯一的行,因此一旦您按它分组,您也可以按该表中的所有列进行分组并获得相同的结果。

这些给出相同的结果,但行未分组:

SELECT * FROM public.t_place;
SELECT * FROM public.t_place GROUP BY id;
SELECT * FROM public.t_place GROUP BY id, c_name;
SELECT * FROM public.t_place GROUP BY id, c_name, id_place_type;
SELECT * FROM public.t_place GROUP BY id, id_place_type;
Run Code Online (Sandbox Code Playgroud)

并且您在选择时使用此依赖项pt.c_name AS c_place_type,因为您按主键对该表进行了分组pt.id,一旦删除它,则 PK 不存在,因此按它分组使得pt.c_name两者都不会在聚合中使用,也不会在 中使用group by。这就是 Postgres 抱怨视图依赖性的原因 - 一旦你删除这个 PK,它的查询将不再起作用。

您可以使用问题中的修改示例自行尝试:

CREATE TABLE public.t_place_type
(
  id serial NOT NULL,
  c_name character varying(100)
);

CREATE TABLE public.t_place
(
  id serial NOT NULL,
  c_name character varying(50),
  id_place_type integer,
  CONSTRAINT pk_t_place PRIMARY KEY (id)
);

 SELECT p.id,
    p.c_name,
    pt.c_name AS c_place_type
   FROM t_place p
     LEFT JOIN t_place_type pt ON pt.id = p.id_place_type
  GROUP BY p.id, pt.id, p.c_name;

/* RESULT:
ERROR:  column "pt.c_name" must appear in the GROUP BY clause or be used in an aggregate function
LINE 3:     pt.c_name AS c_place_type
            ^
*/
Run Code Online (Sandbox Code Playgroud)