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”匹配