SQL Oracle — многопроцессорное планирование: жадное числовое разбиение

Есть ли оператор SQL для выполнения жадного разделения чисел? (Оракул 19с)

Я хочу разделить работу между N процессорами.

Пример,

Учитывая следующий набор данных о рабочей нагрузке:

job
---
4
60
50
1
100
6

Ожидаемый набор результатов (предположим, что только N=2, где связи идут к процессору с наименьшим количеством назначенных ему заданий):

job  processor
---  ---------
100  1
 60  2
 50  2
  6  1
  4  1
  1  2 

Следующая таблица может помочь прояснить, как эти процессоры были назначены.

job  processor  length  count
---  ---------  ------  -----
100  1          100     1
 60  2           60     1
 50  2          110     2
  6  1          106     2
  4  1          110     3
  1  2          111     3

Кажется, что некоторая комбинация аналитических функций и иерархических запросов может сделать это, не прибегая к процедурному коду. Заранее спасибо за ваши мысли и помощь.

ReactJs | Supabase | Добавление данных в базу данных
ReactJs | Supabase | Добавление данных в базу данных
Это и есть ваш редактор таблиц в supabase.👇
Понимание Python и переход к SQL
Понимание Python и переход к SQL
Перед нами лабораторная работа по BloodOath:
0
0
74
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Вы можете использовать рекурсивный CTE:

with tt as (
      select job, row_number() over (order by job desc) as seqnum
      from t
     ),
     cte(job, seqnum, processor, proc1, proc2, lev) as (
      select job, seqnum, 1, job as proc1, 0 as proc2, 1
      from tt
      where seqnum = 1
      union all
      select tt.job, tt.seqnum,
             (case when cte.proc1 > cte.proc2 then 2 else 1 end),
             (case when cte.proc1 > cte.proc2 then cte.proc1 else cte.proc1 + tt.job end),
             (case when cte.proc1 > cte.proc2 then cte.proc2 + tt.job else cte.proc2 end),
             lev + 1
      from cte join
           tt
           on tt.seqnum = cte.seqnum + 1
     )
select *
from cte
order by seqnum;

Здесь db<>рабочий пример.

Вау, спасибо. Даже не знал о рекурсивном разнообразии CTE. Как обобщить это на N процессоров? При использовании приведенного выше шаблона предполагается, что для создания cte с proc1, proc2, ..., procN и условиями case потребуется динамический SQL. Возможно, есть более чистое обобщение, чем динамический SQL?

Alex Bartsmon 13.12.2020 16:43

@AlexBartsmon. . . Если вы знаете количество процессоров, у вас будут дополнительные столбцы для каждого процессора. Я хотел бы отметить, что это отвечает на вопрос, который вы задали здесь.

Gordon Linoff 14.12.2020 01:35

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

CREATE TYPE job_processor AS OBJECT(
  job       NUMBER,
  processor NUMBER
);

CREATE TYPE job_processor_list AS TABLE OF job_processor;

Тогда функция:

CREATE FUNCTION partition_jobs (
  num_processors IN PLS_INTEGER
) RETURN job_processor_list PIPELINED
IS
  processor_time     SYS.ODCINUMBERLIST := SYS.ODCINUMBERLIST();
  processor_id       PLS_INTEGER;
  min_processor_time TABLE_NAME.JOB%TYPE;
BEGIN
  processor_time.EXTEND( num_processors );

  FOR i IN 1 .. num_processors LOOP
    processor_time(i) := 0;
  END LOOP;

  FOR j IN ( SELECT job FROM table_name ORDER BY job DESC ) LOOP
    processor_id       := 1;
    min_processor_time := processor_time( processor_id );
    
    FOR i IN 2 .. num_processors LOOP
      IF processor_time(i) < min_processor_time THEN
        processor_id       := i;
        min_processor_time := processor_time( processor_id );
      END IF;
    END LOOP;
    
    PIPE ROW ( job_processor( j.job, processor_id ) );
    processor_time( processor_id ) := processor_time( processor_id ) + j.job;
  END LOOP;
END;
/

Что для примера данных:

CREATE TABLE TABLE_NAME ( job ) AS
SELECT   4 FROM DUAL UNION ALL
SELECT  60 FROM DUAL UNION ALL
SELECT  50 FROM DUAL UNION ALL
SELECT   1 FROM DUAL UNION ALL
SELECT 100 FROM DUAL UNION ALL
SELECT   6 FROM DUAL;

Затем:

SELECT *
FROM   TABLE( partition_jobs( 2 ) );

Выходы:

JOB | PROCESSOR
--: | --------:
100 |         1
 60 |         2
 50 |         2
  6 |         1
  4 |         1
  1 |         1

и:

SELECT *
FROM   TABLE( partition_jobs( 3 ) );

Выходы:

JOB | PROCESSOR
--: | --------:
100 |         1
 60 |         2
 50 |         3
  6 |         3
  4 |         3
  1 |         2

db<>рабочий пример здесь

Другие вопросы по теме