Postgresql 从INSERT或SELECT获取id
我有一个函数,它在Postgresql 从INSERT或SELECT获取id,postgresql,plpgsql,insert-select,Postgresql,Plpgsql,Insert Select,我有一个函数,它在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
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\u city
,我希望执行第二个查询以选择现有的id\u city
我怎样才能做到这一点呢?为什么不这样修改你的函数呢 将现有的
id\u city
插入id\u city1
。如果一个不存在,它将是NULL
。然后,如果INSERT
为NULL
,则可以执行INSERT
,并分配新的id\u city1
。最后返回id\u 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;
您的功能可以进一步简化。更重要的是,您可以修复内置的竞争条件:
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;
要点
- 假设您运行Postgres9.5或更高版本,因为您没有声明它
- 使用新的快速插入解决方案
INSERT。。关于冲突…
详细说明: - 为此,您需要
限制唯一的
城市名称
- 关于
:联合所有。。。限制1
- 可以通过使用数据修改CTE的单个SQL命令来实现。这是最不容易受到锁争用或其他并发问题影响的。即使没有并发访问,它也是最短、最快的
- 该函数可以是更简单的SQL函数。(但plpgsql也没有错或坏。)
- 不要滥用的别名将名称附加到参数。手册中明确不鼓励这样做。使用正确的参数名 最好仅将其用于覆盖预定名称的目的
- 这是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;
有一种方法,当行存在时,不增加主键号(在这种情况下为id\u city)?存在竞争条件。当并发事务同时提交时,即使第一次选择没有找到行,也可能会遇到唯一的冲突。有一些更快的解决方案没有显示此问题。错误:没有唯一或排除约束与启动期间的冲突规范SQL状态:42P10上下文:SQL函数“insert_city”匹配