Гость
Целевая тема:
Создать новую тему:
Автор:
Форумы / Firebird, InterBase [игнор отключен] [закрыт для гостей] / Простановка в FK-поле дочерней таблицы случайного значения из родителя / 9 сообщений из 9, страница 1 из 1
09.03.2014, 14:25:17
    #38582073
Таблоид
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Простановка в FK-поле дочерней таблицы случайного значения из родителя
Пфф... родилось-таки одним запросом, хоть и в муках...
Запишу сюда на память, но может и кому еще пригодится.

DDL : depts = таблица участков (родительская) и users = таблица сотрудников. Таблица сотрудников имеет поле-ссылку на участки, но оно в первоначальном скрипте = null.
Требуется заполнить это поле случайными id'шниками из родительской таблицы, используя rand():
Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
recreate table depts(id int primary key, name not null unique); commit;
insert into depts values(11, 'sales');
insert into depts values(101, 'logistics');
insert into depts values(1001, 'IT');
insert into depts values(101001, 'administration');
insert into depts values(1010101, 'HR');
commit;
recreate table users(
    id int primary key,
    dept_id int,
    name unique,
    constraint users_fk foreign key (id) references depts(id) );
commit;
insert into users(id, name) select row_number()over(),'user #'||row_number()over()
from rdb$types;
commit;


update-команда получилась вот такой:
Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
update users
set dept_id =
(
    select d.id
    from ( select id, row_number()over() rn from depts ) d
    join( select cast(round((0.5+rand()*(select count(*) from depts)),0) as int) rx from depts ) b
    on d.rn = b.rx
    rows 1
);

Проверочный запрос для показа перекосов (если они есть):
Код: sql
1.
select u.dept_id, count(*) c, 1.0000*count(*)/count(*)over() d  from users u group by 1

(во всех строках значения в столбце 'd' должны быть близкими друг к другу).

PS. Без материализации рандомов как-то тоскливо... :'(
...
Рейтинг: 0 / 0
09.03.2014, 17:27:45
    #38582129
Гхостик
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Простановка в FK-поле дочерней таблицы случайного значения из родителя
Напиши один раз и используй повторно.

create global temporary table gtt_random (value integer not nul primary key);
create procedure fill_randoms(max_value number; count integer);

Еще можно процедуру для нормального распределения сделать.
...
Рейтинг: 0 / 0
09.03.2014, 18:47:18
    #38582158
Таблоид
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Простановка в FK-поле дочерней таблицы случайного значения из родителя
ГхостикНапиши один раз и используй повторно.

create global temporary table gtt_random (value integer not nul primary key);
create procedure fill_randoms(max_value number; count integer);

Еще можно процедуру для нормального распределения сделать.Не интересно, ибо процедурно. Да и под каждый случай одной такой gtt_random + fill_randoms не напасёшься.
Кстати, зачем делать процедуру для нормального распр-я, когда rand() как раз и даёт его ?
...
Рейтинг: 0 / 0
09.03.2014, 19:01:30
    #38582163
Гхостик
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Простановка в FK-поле дочерней таблицы случайного значения из родителя
Зачем под каждый случай свою? Задаешь в параметрах процедуры количество записей и джойнишь gtt к результату по номеру записи из нужной таблицы.

Нормальное в математическом смысле, rand дает равномерное (тоже в математическом ).
...
Рейтинг: 0 / 0
09.03.2014, 19:42:57
    #38582174
Таблоид
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Простановка в FK-поле дочерней таблицы случайного значения из родителя
Гхостикджойнишь gtt к результату по номеру записи из нужной таблицы.на примере покажи, плз. Что-то " номер записи" заинтересовал (если это не row_number()over(), конечно же).
...
Рейтинг: 0 / 0
09.03.2014, 21:00:53
    #38582195
Гхостик
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Простановка в FK-поле дочерней таблицы случайного значения из родителя
Таблоидесли это не row_number()over(), конечно жеОн самый и есть. Но вообще, конечно, выгода мизерная получается.

Лучше, наверное, вот этот кусок переделать на динамический sql в процедуре по названию таблицы.

Код: sql
1.
2.
3.
4.
5.
select d.id
    from ( select id, row_number()over() rn from depts ) d
    join( select cast(round((0.5+rand()*(select count(*) from depts)),0) as int) rx from depts ) b
    on d.rn = b.rx
    rows 1

Кстати, зачем в "b" из depts запрашивается? Нужна же только одна запись, можно и из rdb$database.
...
Рейтинг: 0 / 0
09.03.2014, 21:45:22
    #38582207
Таблоид
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Простановка в FK-поле дочерней таблицы случайного значения из родителя
ГхостикКстати, зачем в "b" из depts запрашивается? Нужна же только одна запись, можно и из rdb$database.да, ты прав. Это я в пылу "борьбы за идею" упустил :-)
Вот так правильнее:
Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
update users
set dept_id =
(
    select d.id
    from ( select id, row_number()over() rn from depts ) d
    join( select cast(round((0.5+rand()*(select count(*) from depts)),0) as int) rx from rdb$database ) b
    on d.rn = b.rx
);
...
Рейтинг: 0 / 0
14.03.2014, 09:56:49
    #38585912
afgm
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Простановка в FK-поле дочерней таблицы случайного значения из родителя
Таблоид,

Через GTT будет работать неплохо. Особенно если ID-шники это чаще всего integer.
Есть вариант использовать скроллируемые курсоры. Они довольно шустры. Не одним запросом, но зато без GTT и реально быстро.

Записей в таблицах
users: 63001
depts: 5

Предлагаемый update:
Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
update users
set dept_id =
(
    select d.id
    from ( select id, row_number()over() rn from depts ) d
    join( select cast(round((0.5+rand()*(select count(*) from depts)),0) as int) rx from rdb$database ) b
    on d.rn = b.rx
);
-- Execute time = 3s 432ms


Просто update:
Код: sql
1.
2.
3.
update users
set dept_id = 11
--Execute time = 1s 185ms


Через курсоры:
Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
execute block as
declare cur scroll cursor for (select d.id from depts d);
declare depts_cnt integer;
declare id integer;
declare dept_id integer;
begin
  select count(*) from depts d into :depts_cnt;
  open cur;
  for select u.id from users u
  into :id
  as cursor tmp_cursor
  do
  begin
    fetch absolute round(0.5+rand()*:depts_cnt) from cur into :dept_id;
    update users set dept_id = :dept_id where current of tmp_cursor;
  end
 close cur;
end
-- Execute time = 1s 404ms


При количестве записей в depts 10000: Execute time = 1s 591ms

Чуть более корректный запрос оценки распределения:
Код: sql
1.
2.
3.
select d.id as dept_id, count(u.dept_id) as cnt_all, 1.0000*count(u.dept_id)/count(*)over() as cnt_dept from depts d
    left join users u on u.dept_id = d.id
group by d.id
...
Рейтинг: 0 / 0
14.03.2014, 13:43:35
    #38586256
Таблоид
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Простановка в FK-поле дочерней таблицы случайного значения из родителя
afgmЧерез курсорыЗаинтриговал. Проверю попозже, спасибо за идею.
...
Рейтинг: 0 / 0
Форумы / Firebird, InterBase [игнор отключен] [закрыт для гостей] / Простановка в FK-поле дочерней таблицы случайного значения из родителя / 9 сообщений из 9, страница 1 из 1
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]