Як я можу генерувати випадковий байт


18

Я хотів би мати можливість генерувати випадкові byteaполя довільної довжини (<1Gb) для заповнення даних тесту.

Який найкращий спосіб зробити це?

Відповіді:


20

Покращуючи відповідь Джека Дугласа, щоб уникнути необхідності циклічного циклу PL / PgSQL та об'єднання байтів, ви можете використовувати:

CREATE OR REPLACE FUNCTION random_bytea(bytea_length integer)
RETURNS bytea AS $body$
    SELECT decode(string_agg(lpad(to_hex(width_bucket(random(), 0, 1, 256)-1),2,'0') ,''), 'hex')
    FROM generate_series(1, $1);
$body$
LANGUAGE 'sql'
VOLATILE
SET search_path = 'pg_catalog';

Це проста SQLфункція, яку дешевше викликати, ніж PL / PgSQL.

Різниця в продуктивності завдяки зміненому методу агрегації величезна для більших byteaзначень. Хоча оригінальна функція насправді до 3 разів швидша для розмірів <50 байт, ця масштабується набагато краще для більших значень.

Або скористайтеся функцією розширення C :

Я реалізував генератор випадкових байтів як просту функцію розширення C. Це в моєму сховищі скрап-коду на GitHub . Дивіться README там.

Це вражає продуктивністю вищевказаної версії SQL:

regress=# \a
regress=# \o /dev/null
regress=# \timing on
regress=# select random_bytea(2000000);
Time: 895.972 ms
regress=# drop function random_bytea(integer);
regress=# create extension random_bytea;
regress=# select random_bytea(2000000);
Time: 24.126 ms

1
Ну, я придумав майже те саме рішення, але протестував лише на нижчі значення. Рішення @ Джека було очевидним переможцем. +1 для вас, що ви не зупиняєтесь тут :)
dezso

Дякую - це чудово і думка провокує. Я думаю, що FROM generate_series(0, $1);треба FROM generate_series(1, $1);. Ви пробували рекурсію? Моє обмежене тестування передбачає, що цей масштаб краще:
Джек Дуглас

2
Я спробував символічної посилання /dev/urandomв /var/lib/pgsql/dataі читати його з pg_read_file()бонусним божевільними очками, але , до жаль , pg_read_file()читаю textвведення з допомогою перетворення кодування, тому він не може читати BYTEA. Якщо вам дуже потрібна максимальна швидкість, напишіть функцію Cрозширення, яка використовує швидкий генератор псевдовипадкових чисел для отримання бінарних даних і оберне байтову дату навколо буфера :-)
Крейг Рінгер

1
@JackDouglas Я не міг у цьому допомогти. C розширення версії random_bytea. github.com/ringerc/scrapcode/tree/master/postgresql/…
Крейг Рінгер

1
Ще одна відмінна відповідь! Насправді одне з найкращих, що я бачив досі. Я не тестував розширення, але я вірю, що він працює як оголошено.
Erwin Brandstetter

5

Я хотів би мати можливість генерувати випадкові байтові поля довільної довжини

Ця функція буде виконувати цю функцію, але 1 Гб знадобиться тривалий час, оскільки вона не масштабується лінійно з вихідною довжиною:

create function random_bytea(p_length in integer) returns bytea language plpgsql as $$
declare
  o bytea := '';
begin 
  for i in 1..p_length loop
    o := o||decode(lpad(to_hex(width_bucket(random(), 0, 1, 256)-1),2,'0'), 'hex');
  end loop;
  return o;
end;$$;

вихідний тест:

select random_bytea(2);

/*
|random_bytea|
|:-----------|
|\xcf99      |
*/

select random_bytea(10);

/*
|random_bytea          |
|:---------------------|
|\x781b462c3158db229b3c|
*/

select length(random_bytea(100000))
     , clock_timestamp()-statement_timestamp() time_taken;

/*
|length|time_taken     |
|-----:|:--------------|
|100000|00:00:00.654008|
*/

dbfiddle тут


Непогане використання width_bucket. Зручно.
Крейг Рінгер

1
Я вдосконалив ваш підхід, щоб уникнути PL / PgSQL і дорогого циклу конкатенації; див нову відповідь. Використовуючи string_agg над create_series замість циклу конкатенації PL / PgSQL на байті, я бачу в 150 разів покращення продуктивності.
Крейг Рінгер
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.