在 PostgreSQL 视图上插入或更新

INSERT or UPDATE on PostgreSQL views

我从 PostgreSQL 视图开始,因为它们对我的用例很有用,并且会提供比函数更好的性能。

(这无关紧要,但我在 Heroku Postgres 上使用 Django 1.7 — 以防万一)。

我已经创建了一个视图,可以很好地查询它。我想围绕视图编写一个 Django 包装器,这样我就可以像 table 一样对待它,并相应地查询和写入它。我一直在审查 Postgres docs on INSERT and UPDATE for views 但老实说,我发现他们的文档很难读,我几乎无法解析他们在说什么。

假设我有以下观点:

CREATE OR REPLACE VIEW links AS
  SELECT
    listing.id                                                     AS listing_id,
    CONCAT('/i-', industry.slug, '-j-', listing.slug, '/') AS link,
    'https://www.example.com' || CONCAT(industry.slug, '-SEP-', listing.slug, '/') AS full_link,
    listing.slug AS listing_slug,
    industry.slug AS industry_slug
  FROM listing
    INNER JOIN company ON company.id = listing.company_id
    INNER JOIN industry ON industry.id = company.industry_id

在这里,我使用 industry.sluglisting.slug 来建立链接。我希望能够从此视图中更新这两个字段,例如:

UPDATE links
SET listing_slug = 'my-new-slug'
WHERE listing_id = 5;

如何创建规则以正确执行此操作?

您视图中唯一可以更新的列是 listing_slug。 更新其他列是不可能或毫无意义的(例如,更新 industry_slug 没有意义,因为视图中没有 industry 的主键)。 在这种情况下,您应该使用排除更新其他列的能力的条件规则。

the documentation 中所述,必须 是您希望在视图上允许的每个操作的无条件 INSTEAD 规则。因此,您应该创建 dummy INSTEAD 更新规则和条件 ALSO 规则:

CREATE RULE update_links_default
AS ON UPDATE TO links DO INSTEAD NOTHING;

CREATE RULE update_links_listing
AS ON UPDATE TO links 
WHERE NEW.listing_slug <> OLD.listing_slug
DO ALSO
    UPDATE listing 
    SET slug = NEW.listing_slug
    WHERE id = OLD.listing_id;

如果您要将列 industry.id as industry_id 添加到视图中,您可以为 table 定义适当的规则 industry:

CREATE RULE update_links_industry
AS ON UPDATE TO links 
WHERE NEW.industry_slug <> OLD.industry_slug
DO ALSO
    UPDATE industry 
    SET slug = NEW.industry_slug
    WHERE id = OLD.industry_id;

通常,table.

需要一个 ALSO 规则

因为是double join,所以最好使用触发程序。要更新行业 table,您首先需要使用外键 listing-company-industry 找到 industry.id。 程序可能如下所示:

CREATE OR REPLACE FUNCTION update_listing_and_industry() RETURNS TRIGGER AS
  $$
  DECLARE _company_id int; _industry_id int;
  BEGIN
    _company_id = (SELECT company_id FROM listing WHERE id = OLD.listing_id);
    _industry_id = (SELECT industry_id FROM company WHERE id = _company_id);

    UPDATE listing SET slug = NEW.listing_slug WHERE id = OLD.listing_id;
    UPDATE industry SET slug = NEW.industry_slug WHERE id = _industry_id;
    RETURN NEW;
  END;
  $$
LANGUAGE plpgsql;

注意:触发程序是一个正常程序,return是一个触发程序。根据触发器的作用,过程必须 return NEW 或 OLD(在本例中为 NEW)。

以及带有 INSTEAD OF UPDATE 子句的触发器:

CREATE trigger update_view INSTEAD OF UPDATE ON links 
FOR EACH ROW EXECUTE PROCEDURE update_listing_and_industry();