3

我有这个函数可以在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.

我怎样才能做到这一点?

4

3 回答 3

3

您的功能可以进一步简化。更重要的是,您可以修复内置的竞态条件:

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 也没有错或坏。)

  • 不要滥用ALIAS FOR将名称附加到参数。手册中明确不鼓励这样做。使用正确的参数名称。手册:

    最好仅将其用于覆盖预定名称的目的。

于 2016-09-30T00:13:52.967 回答
1

为什么不像这样改变你的功能?:

将现有id_city插入id_city1. 如果一个不存在,它将是NULL。然后,您可以执行INSERTif it isNULL并分配新的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;
于 2016-09-29T20:45:13.330 回答
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)?

于 2016-10-01T17:34:56.300 回答