为 PostgreSQL 表创建别名
Posted
技术标签:
【中文标题】为 PostgreSQL 表创建别名【英文标题】:Create Alias for PostgreSQL Table 【发布时间】:2014-06-11 13:47:03 【问题描述】:我有一个名为 assignments 的表。我希望能够使用 assignments.column 或 homework.column 读取/写入此表中的所有列,我该怎么做?
我知道这不是你通常会做的事情。我需要能够做到这一点,以便在短时间内提供向后兼容性。
我们有一个 ios 应用程序,目前对数据库执行直接 postgresql 查询。我们正在更新我们所有的应用程序以使用 API。在构建 API 的过程中,开发人员决定更改表的名称,因为我们(愚蠢地)认为我们不需要向后兼容。
现在,V1.0 和 API 都需要能够写入这个表,所以我以后不必做一些巫术来传输/合并数据...... 我们将 Ruby on Rails 用于 API。
【问题讨论】:
Postgres 没有同义词,例如 Oracle 有。你能确切地说明你的用途是什么吗?也许视图可以解决问题。 在编辑中添加了详细信息。 @Mureinik:视图是要走的路。你应该这样回答。 你能在视图上插入/更新/删除吗? @PhillipBoushy:是的 - 有规则或触发器。 Here is a recent related question (and answer) on dba.SE demonstrating INSTEAD triggers.。 Another more answer with more explanation. 【参考方案1】:对于 Postgres 9.3,以下内容就足够了:
CREATE VIEW homework AS SELECT * FROM assignments;
之所以有效,是因为简单的视图可以自动更新 (see docs)。
【讨论】:
不幸的是,我需要兼容 PostgreSQL 9.1.9 和 9.2.4(OS X 10.8 和 9 中的内置 PostgreSQL。 在这种情况下,您注定要使用触发器或规则,但是升级可能比实施这些更容易。 我很想升级到 9.3,但这是所有 OS X 服务都使用的内置 PostgreSQL 数据库......所以,这可能有点危险。【参考方案2】:在 Postgres 9.3 或更高版本中,简单的 VIEW
是自动“可更新”的。 The manual:
简单视图可自动更新:系统将允许
INSERT
、UPDATE
和DELETE
语句将在视图中使用 与在普通桌子上的方式相同。视图可自动更新 如果满足以下所有条件:视图在其
FROM
列表中必须有一个条目,该条目必须是表或另一个可更新视图。视图定义不得在顶层包含
WITH
、DISTINCT
、GROUP BY
、HAVING
、LIMIT
或OFFSET
子句。视图定义不得在顶层包含集合操作(
UNION
、INTERSECT
或EXCEPT
)。视图的选择列表不得包含任何聚合、窗口函数或集合返回函数。
如果不满足这些条件之一(或者对于现在已经过时的 Postgres 9.2 或更早版本),手动设置可能会完成这项工作。
以您正在进行的工作为基础:
触发功能
CREATE OR REPLACE FUNCTION trg_ia_insupdel()
RETURNS trigger
LANGUAGE plpgsql AS
$func$
DECLARE
_tbl CONSTANT regclass := 'iassignments_assignments';
_cols text;
_vals text;
BEGIN
CASE TG_OP
WHEN 'INSERT' THEN
INSERT INTO iassignments_assignments
VALUES (NEW.*);
RETURN NEW;
WHEN 'UPDATE' THEN
SELECT INTO _cols, _vals
string_agg(quote_ident(attname), ', ') -- incl. pk col!
, string_agg('n.' || quote_ident(attname), ', ')
FROM pg_attribute
WHERE attrelid = _tbl -- _tbl converted to oid automatically
AND attnum > 0 -- no system columns
AND NOT attisdropped; -- no dropped (dead) columns
EXECUTE format('
UPDATE %s t
SET (%s) = (%s)
FROM (SELECT ($1).*) n
WHERE t.published_assignment_id
= ($2).published_assignment_id' -- match to OLD value of pk
, _tbl, _cols, _vals) -- _tbl converted to text automatically
USING NEW, OLD;
RETURN NEW;
WHEN 'DELETE' THEN
DELETE FROM iassignments_assignments
WHERE published_assignment_id = OLD.published_assignment_id;
RETURN OLD;
END CASE;
RETURN NULL; -- control should never reach this
END
$func$;
触发器
CREATE TRIGGER insupbef
INSTEAD OF INSERT OR UPDATE OR DELETE ON assignments_published
FOR EACH ROW EXECUTE PROCEDURE trg_ia_insupdel();
注意事项
assignments_published
必须是 VIEW
,INSTEAD OF
触发器只允许用于视图。
动态 SQL(在UPDATE
部分中)并不是绝对必要的,只是为了自动涵盖对表布局的未来更改。 table 和 PK 的名称仍然是硬编码的。
没有sub-block(就像你曾经拥有的那样)更简单,可能更便宜。
使用(SELECT ($1).*)
而不是较短的VALUES ($1.*)
来保留列名。
我的命名约定:我在 触发函数 前面加上 trg_
,然后是表示目标表的缩写,最后是 ins
、up
和 @987654350 中的一个或多个标记@ 分别代表 INSERT
、UPDATE
和 DELETE
。触发器的名称是函数名称的副本,去掉了前两部分。这纯粹是惯例和品味的问题,但事实证明对我很有用,因为名称说明了目的并且仍然很短。
已经提到的相关答案中的更多解释:
Update multiple columns in a trigger function in plpgsql
【讨论】:
函数/触发器名称是否有方法?它们对我来说似乎相当神秘。我将在我的测试数据库上对此进行测试,看看它是如何工作的。 @PhillipBoushy:嗯,对,我为此添加了一些解释。 我相信这确实回答了它,不幸的是,我没有机会完全测试它。这周我会尝试这样做,并将您的答案标记为正确。 @ErwinBrandstetter。我试图复制你的结果。但。我相信你现在应该INSERT INTO iassignments_assignments VALUES (NEW.*);
。另一点是,当我尝试将assignments_published
设为表时,触发器会出错。 ERROR: "assignments_published" is a table DETAIL: Tables cannot have INSTEAD OF triggers.
虽然我还没有理解函数的意图....
@Mark:是的,VALUES (NEW.*)
。 INSTEAD OF
触发器只允许用于视图,不能用于表。无论如何,这个答案是旧的。如果可能,只需使用自动更新的视图(自 Postgres 9.3 起)。我只使 UPDATE
部分动态化,因为 OP 在他的回答中提出了它。【参考方案3】:
到目前为止,这是我使用触发功能的地方,任何反馈都将不胜感激。它是http://vibhorkumar.wordpress.com/2011/10/28/instead-of-trigger/ 和Update multiple columns in a trigger function in plpgsql 的组合
表:iassignments_assignments
列:
published_assignment_id
name
filepath
filename
link
teacher
due date
description
published
clas-s-rooms
查看:assignments_published - SELECT * FROM iassignments_assignments
assignments_published 的触发函数
CREATE OR REPLACE FUNCTION assignments_published_trigger_func()
RETURNS TRIGGER
LANGUAGE plpgsql
AS $function$
BEGIN
IF TG_OP = 'INSERT' THEN
EXECUTE format('INSERT INTO %s SELECT ($1).*', 'iassignments_assignments')
USING NEW;
RETURN NEW;
ELSIF TG_OP = 'UPDATE' THEN
DECLARE
tbl = 'iassignments_assignments';
cols text;
vals text;
BEGIN
SELECT INTO cols, vals
string_agg(quote_ident(attname), ', ')
,string_agg('x.' || quote_ident(attname), ', ')
FROM pg_attribute
WHERE attrelid = tbl
AND NOT attisdropped -- no dropped (dead) columns
AND attnum > 0; -- no system columns
EXECUTE format('
UPDATE %s t
SET (%s) = (%s)
FROM (SELECT ($1).*) x
WHERE t.published_assignment_id = ($2).published_assignment_id'
, tbl, cols, vals)
USING NEW, OLD;
RETURN NEW;
END
ELSIF TG_OP = 'DELETE' THEN
DELETE FROM iassignments_assignments WHERE published_assignment_id=OLD.published_assignment_id;
RETURN NULL;
END IF;
RETURN NEW;
END;
$function$;
触发器
CREATE TRIGGER assignments_published_trigger
INSTEAD OF INSERT OR UPDATE OR DELETE ON
assignments_published FOR EACH ROW EXECUTE PROCEDURE assignments_published_trigger_func();
表:iassignments_classes
列:
class_assignment_id
guid
assignment_published_id
查看:assignments_class - SELECT * FROM assignments_classes
assignments_class 的触发函数
**一旦我收到关于另一个函数的反馈并知道它已创建,我将创建此函数,因此我(希望)对这个函数只需要很少的更改。
触发器
CREATE TRIGGER assignments_class_trigger
INSTEAD OF INSERT OR UPDATE OR DELETE ON
assignments_class FOR EACH ROW EXECUTE PROCEDURE assignments_class_trigger_func();
【讨论】:
为什么是动态报表?您想涵盖未来对表格的更改吗? (更简单和更快的)静态版本是否足够好?如果您稍后应该更改表格,您将更改触发器... 我无法控制需要对其进行的查询或数据库结构,我只是想提出一个解决方案来使其向后兼容,因此,我怀疑它需要是动态的。只要将来易于更新,我就可以让它更加静态。以上是关于为 PostgreSQL 表创建别名的主要内容,如果未能解决你的问题,请参考以下文章