SQL: Как бы вы разбили 100000 записей из таблицы Oracle на 5 частей?

Я пытаюсь разобраться, как разделить первые 100 000 записей из таблицы, в которой более 1 миллиона записей, на 5 (пять) 20 000 фрагментов записей, чтобы поместить в файл? Может быть, какой-нибудь SQL, который получит min и max rowid или первичный идентификатор для каждых 5 фрагментов 20000 записей, так что я могу поместить min и max значение в переменную и передать его в SQL и использовать BETWEEN в предложении where для SQL.

Можно ли это сделать?

Я в базе данных Oracle 11g.

Заранее спасибо.

4 ответа

Решение

Если вы просто хотите присвоить значения 1-5 группам одинакового размера, используйте ntile():

select t.*, ntile(5) over (order by NULL) as num
from (select t.*
      from t
      where rownum <= 100000
     ) t;

Если вы хотите вставить в 5 разных таблиц, то используйте insert all:

insert all
    when num = 1 then into t1
    when num = 2 then into t2
    when num = 3 then into t3
    when num = 4 then into t4
    when num = 5 then into t5
    select t.*, ntile(5) over (order by NULL) as num
    from (select t.*
          from t
          where rownum <= 100000
         ) t;

Немного грубовато голосую за очередной справедливый вопрос.

Во всяком случае, NTILE для меня новинка, поэтому я бы не узнал, если бы не твой вопрос.

Мой способ сделать это, по старой школьной схеме, был бы в MOD rownum, чтобы получить номер группы, например

select t.*, mod(rn,5) as num
from (select t.*, rownnum rn
      from t
     ) t;

Это решает часть SQL, точнее, как группировать строки в равные куски, но это только половина вашего вопроса. Следующая половина - как записать их в 5 отдельных файлов.

Вы можете иметь 5 отдельных запросов, каждый спулинг в отдельный файл, например:

spool f1.dat
    select t.*
    from (select t.*, rownnum rn
          from t
         ) t
    where mod(t.rn,5) = 0;
spool off

spool f2.dat
    select t.*
    from (select t.*, rownnum rn
          from t
         ) t
    where mod(t.rn,5) = 1;
spool off

и т.п.

Или, используя UTL_FILE. Вы можете попробовать что-нибудь умное с одним запросом и иметь массив типов UTL_FILE, где индекс массива соответствует MOD(rn,5), тогда вам не понадобится логика типа "IF rn = 0 THEN UTL_FILE.WRITELN(f0, ..".. ".

Итак, что-то вроде (не проверено, просто в грубой форме для руководства, никогда не пробовал это сам):

DECLARE
   TYPE fname IS VARRAY(5) OF VARCHAR2(100);
   TYPE fh    IS VARRAY(5) OF UTL_FILE.FILE_TYPE;
   CURSOR c1 IS 
    select t.*, mod(rn,5) as num
    from (select t.*, rownnum rn
          from t
         ) t;
   idx INTEGER;
BEGIN
  FOR idx IN 1..5 LOOP
      fname(idx) := 'data_' || idx || '.dat';
      fh(idx) := UTL_FILE.'THE_DIR', fname(idx), 'w');
  END LOOP;
  FOR r1 IN c1 LOOP
     UTL_FILE.PUT_LINE ( fh(r1.num+1), r1.{column value from C1} );
  END LOOP;
  FOR idx IN 1..5 LOOP
      UTL_FILE.FCLOSE (fh(idx));
  END LOOP;
END;

Вы даже можете попробовать с простой агрегацией:

create table test_chunk(val) as
(
    select floor(dbms_random.value(1, level * 10)) from dual
    connect by level <= 100
)

select min(val), max(val), floor((num+1)/2)
from (select rownum as num, val from test_chunk)
group by floor((num+1)/2)

Большое спасибо Гордону Линофу за то, что он дал мне стартовый код.

просто обновление о том, как получить минимальное и максимальное значения для 5 блоков.

select num, min(cre_surr_id), max(cre_surr_id)
from
(select p.cre_surr_id, ntile(5) over (order by NULL) as num
from (select p.*
      from productions p
      where rownum <= 100000
 ) p )
group by num
Другие вопросы по тегам