从 INSERT 或 SELECT 获取 id
Get id from INSERT or SELECT
我有这个函数可以在 city
table 中插入一行而不重复。它returns插入行的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 ;
country1 ALIAS FOR ;
province1 ALIAS FOR ;
region1 ALIAS FOR ;
cap1 ALIAS FOR ;
nationality1 ALIAS FOR ;
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
,我想执行第二个查询 select 一个现有的 id_city
.
我该怎么做?
为什么不这样改变你的函数呢?:
将现有的 id_city
插入 id_city1
。如果一个不存在,它将是 NULL
。如果它是 NULL
,然后您可以执行 INSERT
并分配新的 id_city1
。最后 return 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;
你的函数可以再简化一些。更重要的是,您可以修复内置的竞争条件:
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 ...
详细解释:
- Is SELECT or INSERT in a function prone to race conditions?
为此,您需要 UNIQUE
name_city
约束。
关于UNION ALL ... LIMIT 1
:
- Way to try multiple SELECTs till a result is available?
可以通过使用数据修改 CTE 的单个 SQL 命令来实现。这最不容易受到锁争用或其他并发问题的影响。即使没有并发访问也是最短最快的。
函数可以是更简单的SQL函数。 (但是 plpgsql 也不错。)
不要滥用 ALIAS FOR
为参数附加名称。手册中明确不鼓励这样做。使用正确的参数名称。 The manual:
It's best to use it only for the purpose of overriding predetermined names.
这是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)?
我有这个函数可以在 city
table 中插入一行而不重复。它returns插入行的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 ;
country1 ALIAS FOR ;
province1 ALIAS FOR ;
region1 ALIAS FOR ;
cap1 ALIAS FOR ;
nationality1 ALIAS FOR ;
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
,我想执行第二个查询 select 一个现有的 id_city
.
我该怎么做?
为什么不这样改变你的函数呢?:
将现有的 id_city
插入 id_city1
。如果一个不存在,它将是 NULL
。如果它是 NULL
,然后您可以执行 INSERT
并分配新的 id_city1
。最后 return 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;
你的函数可以再简化一些。更重要的是,您可以修复内置的竞争条件:
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 ...
详细解释:- Is SELECT or INSERT in a function prone to race conditions?
为此,您需要
UNIQUE
name_city
约束。关于
UNION ALL ... LIMIT 1
:- Way to try multiple SELECTs till a result is available?
可以通过使用数据修改 CTE 的单个 SQL 命令来实现。这最不容易受到锁争用或其他并发问题的影响。即使没有并发访问也是最短最快的。
函数可以是更简单的SQL函数。 (但是 plpgsql 也不错。)
不要滥用
为参数附加名称。手册中明确不鼓励这样做。使用正确的参数名称。 The manual:ALIAS FOR
It's best to use it only for the purpose of overriding predetermined names.
这是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)?