Разделение данных для равной группы (PL / SQL, SQL)

#sql #oracle #plsql #cursor #bulk

#sql #Oracle #plsql #курсор #массовый

Вопрос:

У меня есть несколько идентификаторов:

 Select count(*) Into count_id From table_I;--4
  

Я знаю, что у меня есть общее total_user = 109 (количество записей). Итак, я хочу разделить его на равную группу:

 Select round(count(*)/count_user,0) Into mapUser From table_U;
  

Итак, у меня есть 4 group . В first three will be 27 и last should be 28 пользователях.

Теперь я хочу for each group присвоить уникальный идентификатор.

 set serveroutput on 
declare 
      CURSOR cur IS Select * From table_U FOR UPDATE OF USER_ID;
      mapUser NUMBER;
      l_rec table_U%rowtype;
      x_mapUser Number := 0;--number between 0-27
      c_obj_id NUMBER := 1;
      count_id NUMBER := 0;

      type T1 is table of number(10) index by binary_integer;
      V1 T1;    

begin
     Select count(*) Into count_id From table_I;--count_id = 4
     Select round(count(*)/count_id,0) Into mapUser From table_U; --mapUser = 27

     SELECT id BULK COLLECT INTO V1 FROM table_I;--it's 4 id (id_1, id_2, id_3, id_4)

    OPEN cur;
        LOOP FETCH cur INTO l_rec;
         EXIT WHEN cur%notfound;

           IF x_mapUser > mapUser Then --0-27 > 27
                x_mapUser := 1;                   
                c_obj_id := c_obj_id  1;--next value from V1
           End if;

          UPDATE table_U SET USER_ID = V1(c_obj_id) WHERE CURRENT OF cur;

         x_mapUser := x_mapUser  1;

        END LOOP;
       CLOSE cur;        
end;
  

Но я также не знаю, как изменить мое IF и последнее значение из cur assign id_4 . Я делаю что-то не так здесь :/

Комментарии:

1. Образцы данных и желаемые результаты действительно очень помогают.

Ответ №1:

Это то, что сработало для меня:

 merge into table_u a
using (select rd, i.id
         from (select u.rowid rd, cnt - mod(rownum-1, cnt) rn 
                 from table_u u, (select count(1) cnt from table_i) ) u
         join (select row_number() over( order by id) rn, id from table_i) i using (rn)) b
on (a.rowid = b.rd)         
when matched then update set a.user_id = b.id
  

Мои тестовые таблицы:

 create table table_i as (
  select level*10 id from dual connect by level <= 4);

create table table_u as (
  select cast(null as number(3)) user_id, level id from dual connect by level <= 109);
  

Наибольшее значение из второй таблицы присваивалось 28 раз, остальным — 27 раз. Это потому, что я использовал

 cnt - mod(rownum-1, cnt) rn
  

для подсчета объединяющего столбца. Хотя я не знаю, важно ли это для вас. 🙂 Основой этого решения является mod() которое позволяет нам переключаться между 1 и cnt (в данном случае 4).

Вы можете сделать это в PLSQL, как вы показали, но решения SQL обычно быстрее и предпочтительнее, когда это возможно.

Комментарии:

1. Вдумайтесь — я только что протестировал ваш код, и на моем сайте все работает, отлично, спасибо за помощь!

Ответ №2:

Я бы использовал ntile() :

 select u.*, ntile(4) over (order by user_id) as grp
from table_u u;
  

Я не знаю, какой порядок, если таковой имеется, вы хотите. Вы можете использовать случайное число, если хотите.

Если вы хотите перечислить значения в каждой группе, используйте подзапрос:

 select u.*, row_number() over (partition by grp order by grp) as seqnum
from (select u.*, ntile(4) over (order by user_id) as grp
      from table_u u
     ) u;
  

Комментарии:

1. хорошо, а теперь как обновить столбец (col1) из table_u? например, у group_1 есть id_1, у group_2 есть id_2 ….. (идентификатор из table_I)

Ответ №3:

Я нашел ошибку и сделал это с моим кодом PL / SQL:

 ...
x_mapUser Number := 0
...
OPEN cur;
 LOOP FETCH cur INTO l_rec;
  EXIT WHEN cur%notfound;
     UPDATE table_U SET USER_ID = V1(c_obj_id) WHERE CURRENT OF cur;

     IF x_mapUser > mapUser-1 AND c_obj_id < count_id Then
            x_mapUser := 0;
            c_obj_id := c_obj_id  1;
     End if;

 x_mapUser := x_mapUser  1;

 END LOOP;
CLOSE cur;