Есть ли оператор 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
Кажется, что некоторая комбинация аналитических функций и иерархических запросов может сделать это, не прибегая к процедурному коду. Заранее спасибо за ваши мысли и помощь.
Вы можете использовать рекурсивный 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<>рабочий пример.
@AlexBartsmon. . . Если вы знаете количество процессоров, у вас будут дополнительные столбцы для каждого процессора. Я хотел бы отметить, что это отвечает на вопрос, который вы задали здесь.
Вы можете создать конвейерную функцию, используя типы:
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<>рабочий пример здесь
Вау, спасибо. Даже не знал о рекурсивном разнообразии CTE. Как обобщить это на N процессоров? При использовании приведенного выше шаблона предполагается, что для создания cte с proc1, proc2, ..., procN и условиями case потребуется динамический SQL. Возможно, есть более чистое обобщение, чем динамический SQL?