Заполнить переменную только при первом вызове функции postgresql

Есть функция, возвращающая таблицу. При вызове функции создается переменная-массив, которую необходимо заполнить случайными числами. Затем возвращается таблица с числами из переменной. Так вот, как сделать так, чтобы заполнение происходило только при первом обращении к функции? При последующих должны возвращаться те же самые значения, а не каждый раз генерироваться новые.

CREATE OR REPLACE FUNCTION fnc_generate_goods()
RETURNS TABLE (good VARCHAR, price INT) AS $$
BEGIN
    FOR i IN 1..20 LOOP
        RETURN QUERY(SELECT ('товар' || to_char(i, 'FM00'))::VARCHAR, i);
    END LOOP;
END;
$$ LANGUAGE plpgsql;

Хочу рандомно сгенерировать цену для товаров, но чтобы она не менялась каждый раз при обращении к этой функции. Конечно, это можно сделать просто создав новую таблицу, но по заданию так нельзя...


Ответы (1 шт):

Автор решения: Alexander Pavlov

Если инициализировать random каждый раз одни и тем же числом, то цены будут случайные, но воспроизводимые между вызовами, если передавать seed в параметрах (захардкодить в функции тоже можно).

Но этот подход имеет серьёзный недостаток - все вызовы random() после вызова этой функции тоже будут воспроизводиться, поэтому использовать стоит только для учебных заданий или в сценариях тестирования. Добавил восстановление случайной последовательности.

CREATE OR REPLACE FUNCTION fnc_generate_goods(price_seed float)
    RETURNS TABLE (good VARCHAR, price INT) AS $$
declare
    previous_seed float;    
BEGIN
    select random() into previous_seed; -- remember current sequence
    
    perform setseed(price_seed); -- generate expected sequence
    
    FOR i IN 1..5 LOOP
            RETURN QUERY(SELECT ('товар' || to_char(i, 'FM00'))::VARCHAR, (random()*1000)::int);
        END LOOP;

    perform setseed(previous_seed*2 - 1.0); -- restore sequence
    
END;
$$ LANGUAGE plpgsql;

Ниже вызовы fnc_generate_goods(0.43) и fnc_generate_goods(0.44) генерируют воспроизводимые цены, при этом вызовы random() за пределами fnc_generate_goods продолжают давать случайные числа.

postgres=# select * from fnc_generate_goods(0.43);
  good   | price 
---------+-------
 B>20@01 |   155
 B>20@02 |    16
 B>20@03 |   853
 B>20@04 |   745
 B>20@05 |   753
(5 rows)

postgres=# select random();
       random       
--------------------
 0.4382084077869095
(1 row)

postgres=# select * from fnc_generate_goods(0.44);
  good   | price 
---------+-------
 B>20@01 |   740
 B>20@02 |   736
 B>20@03 |   845
 B>20@04 |   614
 B>20@05 |   977
(5 rows)

postgres=# select random();
      random       
-------------------
 0.918940566300332
(1 row)

postgres=# select * from fnc_generate_goods(0.43);
  good   | price 
---------+-------
 B>20@01 |   155
 B>20@02 |    16
 B>20@03 |   853
 B>20@04 |   745
 B>20@05 |   753
(5 rows)

postgres=# select random();
       random       
--------------------
 0.7218863495340173
(1 row)

postgres=# select * from fnc_generate_goods(0.44);
  good   | price 
---------+-------
 B>20@01 |   740
 B>20@02 |   736
 B>20@03 |   845
 B>20@04 |   614
 B>20@05 |   977
(5 rows)

postgres=# select random();
       random       
--------------------
 0.5467171552940719
(1 row)
→ Ссылка