如何从函数 return setof 记录?

How to return setof records from function?

我正在尝试创建一个将 return 记录集的函数。我想按如下方式使用函数:

SELECT city_name FROM set_city(1, 1, 'ExampleName');

我的函数:

CREATE OR REPLACE FUNCTION set_city(_city_id integer, _country_id integer, _city_name varchar)
RETURNS SETOF RECORD
LANGUAGE plpgsql
as $$

DECLARE
        result record;

BEGIN
        IF EXISTS (SELECT 1 FROM geo_cities gc WHERE gc.id = _city_id)
        THEN
                UPDATE geo_cities
                SET country_id = _country_id, city_name = _city_name
                WHERE id = _city_id
                RETURNING * INTO result;
        ELSE
                INSERT INTO geo_cities(id, country_id, city_name)
                VALUES (_city_id, _country_id, _city_name)
                RETURNING * INTO result;
        END IF;
        -- It's wrong
        RETURN QUERY SELECT result;
END;
$$

我应该改变什么?

您可以更改 return 语句:

...
        -- It's wrong
        -- RETURN QUERY SELECT result;
        RETURN NEXT result; -- that's good
...

但是,函数 returning "record" 需要列定义列表,因此您必须在每个查询中添加它:

SELECT city_name FROM set_city(1, 1, 'ExampleName') 
    AS (id int, country_id int, city_name text);

事实上,函数 return 是类型 geo_cities 的单行,您不需要 setof:

DROP FUNCTION set_city(_city_id integer, _country_id integer, _city_name varchar);

CREATE OR REPLACE FUNCTION set_city(_city_id integer, _country_id integer, _city_name varchar)
RETURNS geo_cities
LANGUAGE plpgsql
as $$
DECLARE
        result geo_cities;
BEGIN
        IF EXISTS (SELECT 1 FROM geo_cities gc WHERE gc.id = _city_id)
        THEN
                UPDATE geo_cities
                SET country_id = _country_id, city_name = _city_name
                WHERE id = _city_id
                RETURNING * INTO result;
        ELSE
                INSERT INTO geo_cities(id, country_id, city_name)
                VALUES (_city_id, _country_id, _city_name)
                RETURNING * INTO result;
        END IF;
        RETURN result;
END;
$$;

SELECT city_name FROM set_city(1, 1, 'ExampleName');

请注意,您可以在单个 SQL 语句中获得相同的功能:

INSERT INTO geo_cities(id, country_id, city_name)
VALUES (1, 1, 'ExampleName')
ON CONFLICT (id) DO UPDATE SET 
    country_id = excluded.country_id, 
    city_name = excluded.city_name
RETURNING *;