我有这个函数在city
表中插入一行而没有重复。它返回插入行的id:
CREATE OR REPLACE FUNCTION public.insert_city(
character varying,
character varying,
character varying,
character varying,
character varying,
character varying)
RETURNS integer AS
$BODY$
DECLARE
name_city1 ALIAS FOR $1;
country1 ALIAS FOR $2;
province1 ALIAS FOR $3;
region1 ALIAS FOR $4;
cap1 ALIAS FOR $5;
nationality1 ALIAS FOR $6;
id_city1 integer;
BEGIN
INSERT INTO city (name_city, country, province, region, cap, nationality)
SELECT name_city1, country1, province1, region1, cap1, nationality1
WHERE NOT EXISTS (SELECT id_city FROM city WHERE name_city = name_city1)
RETURNING id_city INTO id_city1;
-- xxx
END;
$BODY$
LANGUAGE plpgsql VOLATILE;
xxx
标志着我需要这样的地方:
IF is_number(id_city1) THEN
RETURN id_city1;
ELSE
RETURN query select id_city from city where name_city=name_city1;
END IF;
如果第一个查询没有插入新行而我没有从中获取id_city
,我想执行第二个查询以选择现有的id_city
。
我该怎么做?
答案 0 :(得分:2)
您的功能可以简化一些。更重要的是,您可以修复内置竞争条件:
CREATE OR REPLACE FUNCTION public.insert_city(name_city1 varchar
, country1 varchar
, province1 varchar
, region1 varchar
, cap1 varchar
, nationality1 varchar)
RETURNS integer AS
$func$
WITH ins AS (
INSERT INTO city
(name_city , country , province , region , cap , nationality )
VALUES(name_city1, country1, province1, region1, cap1, nationality1)
ON CONFLICT (name_city) DO UPDATE
SET name_city = NULL WHERE FALSE -- never executed, but locks the row!
RETURNING id_city
)
SELECT id_city FROM ins
UNION ALL
SELECT id_city FROM city WHERE name_city = name_city1 -- only executed if no INSERT
LIMIT 1;
$func$ LANGUAGE sql;
假设您运行Postgres 9.5 或更高版本,因为您没有声明它。
使用新的更快的UPSERT解决方案INSERT .. ON CONFLICT ...
详细解释:
您需要在UNIQUE
上使用 name_city
约束。
关于UNION ALL ... LIMIT 1
:
使用数据修改CTE的单个SQL命令可以实现。这最不容易受到锁争用或其他并发问题的影响。即使没有并发访问,它也是最短和最快的。
该函数可以是更简单的 SQL函数。 (但是plpgsql也没有错或坏。)
请勿滥用 将名称附加到参数。这在手册中明确表示不鼓励。使用正确的参数名称。 The manual: ALIAS FOR
最好只将其用于覆盖预定名称。
答案 1 :(得分:1)
为什么不改变你的功能?:
将现有id_city
插入id_city1
。如果不存在,则为NULL
。然后,您可以INSERT
执行NULL
并分配新的id_city1
。最后返回id_city1
。
SELECT id_city INTO id_city1 FROM city WHERE name_city = name_city1;
IF id_city1 IS NULL THEN
INSERT INTO city (name_city, country, province, region, cap, nationality)
VALUES (name_city1, country1, province1, region1, cap1, nationality1)
RETURNING id_city INTO id_city1;
END IF;
RETURN id_city1;
答案 2 :(得分:0)
这是plpgsql版本
CREATE OR REPLACE FUNCTION public.insert_city(name_city1 varchar
, country1 varchar
, province1 varchar
, region1 varchar
, zip1 varchar
, nationality1 varchar,
OUT id_city1 int)
AS
$func$
BEGIN
INSERT INTO city
(name_city , country , province , region , zip , nationality )
VALUES(name_city1, country1, province1, region1, zip1, nationality1)
ON CONFLICT (name_city,zip) DO UPDATE
SET name_city = NULL WHERE FALSE -- never executed, but locks the row!
RETURNING id_city
INTO id_city1;
IF NOT FOUND THEN
SELECT id_city
FROM city
WHERE name_city = name_city1
INTO id_city1;
END IF;
END $func$ LANGUAGE plpgsql;
有一种方法可以在行存在时不增加primary_key编号(在这种情况下为id_city)?