Відповіді:
Хороша ідея. Пропоную два незначні спрощення:
('{Foo,Bar,Poo}'::text[])[ceil(random()*3)]
Простіший синтаксис з використанням літералу масиву ( '{Foo,Bar,Poo}'::text[]
) Скорочує рядок для більш довгих списків. Додаткова вигода: явна декларація типу працює для будь-якого типу, а не лише для text
. Ваша оригінальна ідея трапляється для виведення text
, тому що це тип за замовчуванням для рядкових літералів.
Використовуйте ceil()
замість floor() + 1
. Той самий результат.
Добре, теоретично нижня межа може бути 0 точно, як натякає у вашому коментарі , оскільки random()
виробляє ( цитуючи тут посібник ):
випадкове значення в діапазоні 0,0 <= x <1,0
Однак я ніколи не бачив, щоб це сталося. Виконайте пару мільйонів тестів:
SELECT count(*)
FROM generate_series(1,1000000)
WHERE ceil(random())::int = 0;
Щоб бути абсолютно безпечним, ви можете використовувати підписи на власні масиви Postgres і все ж уникати додаткових доповнень:
('[0:2]={Foo,Bar,Poo}'::text[])[floor(random()*3)]
Деталі під цим пов’язаним запитанням про SO.
Або ще краще, використовуйте trunc()
, це трохи швидше.
('[0:2]={Foo,Bar,Poo}'::text[])[trunc(random()*3)]
ceil(random())::int
завжди дасть вам 1, так що ви не зможете перевірити, чи коли-небудь поверне 0?
ceil(0.0)
ні, не в цьому справа. Ото: для цілей цього тесту ми могли б спростити: WHERE random() = 0.0
.
На основі цієї ідеї я створив функцію, яка була мені дуже корисною:
CREATE OR REPLACE FUNCTION random_choice(
choices text[]
)
RETURNS text AS $$
DECLARE
size_ int;
BEGIN
size_ = array_length(choices, 1);
RETURN (choices)[floor(random()*size_)+1];
END
$$ LANGUAGE plpgsql;
Приклади використання:
SELECT random_choice(array['h', 'i', 'j', 'k', 'l']) as random_char;
SELECT random_choice((SELECT array_agg(name) FROM pets)) AS pet_name;