Установить случайное значение из набора

11

Мне нужно поместить несколько случайных значений в базу данных, но я не хочу, чтобы в конечном итоге был полностью рандомизированный текст (например, 7hfg43d3). Вместо этого я хотел бы случайно выбрать одно из значений, предоставленных мной.

Корда
источник

Ответы:

26

Хорошая идея. Я предлагаю два небольших упрощения:

('{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;

-> SQLfiddle

Однако, чтобы быть в полной безопасности, вы можете использовать пользовательские подписки на массивы Postgres и по-прежнему избегать дополнительного добавления:

('[0:2]={Foo,Bar,Poo}'::text[])[floor(random()*3)]

Подробности по этому связанному вопросу на SO.

Или еще лучше, используйте trunc(), это немного быстрее.

('[0:2]={Foo,Bar,Poo}'::text[])[trunc(random()*3)]
Эрвин Брандштеттер
источник
ceil (0) == этаж (0) + 1?
Корда
@korda: я добавил еще, обращаясь к этому.
Эрвин Брандштеттер
@ErwinBrandstetter разве вы не думаете, что ceil(random())::intэто всегда даст вам 1, так что вы не сможете проверить, вернется ли он когда-нибудь 0?
aki92
@ aki92: ceil(0.0)нет, в этом все дело. Ото: для целей этого теста мы могли бы упростить: WHERE random() = 0.0.
Эрвин Брандштеттер
@ErwinBrandstetter о, извините, просто пропустил эту вещь.
aki92
8

Я пришел с идеей использовать массивы для достижения этой цели:

(ARRAY['Foo','Bar','Poo'])[floor(random()*3)+1]
Корда
источник
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;

juanra
источник
Вы можете преобразовать эту функцию в функцию с переменным параметром, который лично мне кажется более удобным для пользователя.
Sahap Asci