Ответы:
Хорошая идея. Я предлагаю два небольших упрощения:
('{Foo,Bar,Poo}'::text[])[ceil(random()*3)]
Простой синтаксис с использованием массива literal ( '{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;