在 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.slug
和 listing.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();
我从 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.slug
和 listing.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();