Снова аналитические функцииИсточник: ln
Балансировка нагрузки на сотрудников - снова аналитические функции Том, У меня возникла следующая проблема с оператором update: Есть таблица кредитов (loan): state_cd, user_id, status_cd, ... И таблица user_state_served: user_id, state_cd, last_asgnmt_dt Хотелось бы распределить кредиты равномерно среди сотрудников, имеющих право работать в соответствующем штате. Для этого я изменяю last_asgnmt_dt = sysdate и беру в качестве следующего ответственного за кредит user_id с min(last_asgnmt_dt). Однако после первоначального распределения нагрузка сотрудников может оказаться разной, в зависимости от количества кредитов, выданных в штате. Как можно было бы взять среднее количество кредитов в штате и поровну распределить их между сотрудниками? Например, в штате TX выдано 400 кредитов. Этим штатом сейчас занимается 4 сотрудника, нагрузка среди которых распределена так: emp1: 150 emp2: 50 emp3: 75 emp4: 0 нераспределенные: 125. Я хочу поровну поделить все активные кредиты между 4 сотрудниками. Ответ Тома КайтаКак вам такое решение: ops$tkyte@ORA920LAP> create table user_state_served 2 as 3 select rownum user_id, 'TX' state_cd from all_objects where rownum <= 4 4 union all 5 select rownum+4 user_id, 'VA' state_cd from all_objects where rownum <= 3; Table created. ops$tkyte@ORA920LAP> create table loan_table 2 as 3 select 'TX' state_cd, 1 user_id from all_objects where rownum <= 150 4 union all 5 select 'TX', 2 from all_objects where rownum <= 50 6 union all 7 select 'TX', 3 from all_objects where rownum <= 75 8 union all 9 select 'TX', null from all_objects where rownum <= 125; Table created. ops$tkyte@ORA920LAP> insert into loan_table 2 select 'VA', 5 from loan_table 3 / 400 rows created. ops$tkyte@ORA920LAP> select user_id, count(*) from loan_table group by user_id 2 / USER_ID COUNT(*) ---------- ---------- 1 150 2 50 3 75 5 400 125 ops$tkyte@ORA920LAP> create or replace procedure redistribute_state(p_state_cd in varchar2) 2 as 3 type rowidArray is table of rowid INDEX BY BINARY_INTEGER; 4 5 l_user_id dbms_sql.number_table; 6 l_rowids rowidArray; 7 begin 8 9 select b.user_id, a.rid BULK COLLECT into l_user_id, l_rowids 10 from ( 11 select state_cd, 12 ntile(buckets) over ( partition by buckets, state_cd order by state_cd ) idx, 13 loan_table.rowid rid 14 from loan_table, 15 (select count(*) BUCKETS 16 from user_state_served 17 where state_cd = p_state_cd) 18 where state_cd = p_state_cd 19 ) A, 20 (select user_id, row_number() over ( order by user_id ) rn 21 from user_state_served 22 where state_cd = p_state_cd ) b 23 where a.idx = b.rn; 24 25 forall i in 1 .. l_rowids.count 26 update loan_table set user_id = l_user_id(i) where rowid = l_rowids(i); 27 end; 28 / Procedure created. ops$tkyte@ORA920LAP> exec redistribute_state( 'TX' ); PL/SQL procedure successfully completed. ops$tkyte@ORA920LAP> exec redistribute_state( 'VA' ); PL/SQL procedure successfully completed. ops$tkyte@ORA920LAP> select user_id, count(*) from loan_table group by user_id 2 / USER_ID COUNT(*) ---------- ---------- 1 100 2 100 3 100 4 100 5 134 6 133 7 133 7 rows selected. Поскольку в версии 8i функцию ntile в plsql использовать нельзя, вот как можно изменить это решение для версии Oracle 8i: ops$tkyte@ORA920LAP> rollback; Rollback complete. ops$tkyte@ORA920LAP> select user_id, count(*) from loan_table group by user_id 2 / USER_ID COUNT(*) ---------- ---------- 1 150 2 50 3 75 5 400 125 ops$tkyte@ORA920LAP> create or replace view V 2 as 3 select b.user_id, a.rid 4 from ( 5 select state_cd, 6 ntile(buckets) over ( partition by buckets, state_cd order by state_cd ) idx, 7 loan_table.rowid rid 8 from loan_table, 9 (select count(*) BUCKETS 10 from user_state_served 11 where state_cd = userenv('client_info')) 12 where state_cd = userenv('client_info') 13 ) A, 14 (select user_id, row_number() over ( order by user_id ) rn 15 from user_state_served 16 where state_cd = userenv('client_info') ) b 17 where a.idx = b.rn; View created. ops$tkyte@ORA920LAP> create or replace procedure redistribute_state(p_state_cd in varchar2) 2 as 3 type rowidArray is table of rowid index by binary_integer; 4 5 l_user_id dbms_sql.number_table; 6 l_rowids rowidArray; 7 begin 8 dbms_application_info.set_client_info(p_state_cd); 9 select user_id, rid BULK COLLECT into l_user_id, l_rowids 10 from v; 11 12 forall i in 1 .. l_rowids.count 13 update loan_table set user_id = l_user_id(i) where rowid = l_rowids(i); 14 end; 15 / Procedure created. ops$tkyte@ORA920LAP> exec redistribute_state( 'TX' ); PL/SQL procedure successfully completed. ops$tkyte@ORA920LAP> exec redistribute_state( 'VA' ); PL/SQL procedure successfully completed. ops$tkyte@ORA920LAP> select user_id, count(*) from loan_table group by user_id 2 / USER_ID COUNT(*) ---------- ---------- 1 100 2 100 3 100 4 100 5 134 6 133 7 133 7 rows selected. Комментарий читателя от 24 июня 2003 годаТом! Хранимая процедура работает не совсем так, как мне хотелось бы. Например, у меня есть 6 кредитов для в штате TX: user_id loan_id 2 1 3 2 3 3 3 4 3 5 6 При выполнении твоей хранимой процедуры, она равномерно распределяет 6 кредитов среди 2 пользователей в группе, но меняет уже выделенные сотруднику задания. Например, в данном случае она может взять кредит с идентификатором 1 и передать его для работы сотруднику с user_id 3. Хотя этого не надо делать - надо только забрать кредиты у перегруженных сотрудников и передать для управления менее загруженным, не отбирая у них те кредиты, с которыми они уже работают. Ответ Тома КайтаНу, идею вы поняли, так почему бы не попытаться решить самому? |