многопоточность - запрос, показывающий общее время, прошедшее с работы над набором процессов. - PullRequest
1 голос
/ 15 июля 2011

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

Вот тупая версия таблицы (назовем ее processes), с которой я работаю:

| id  | start_date          | end_date            |
---------------------------------------------------
|  1  | 07/15/2011 12:00:00 | 07/15/2011 12:01:00 |
|  2  | 07/15/2011 12:00:00 | 07/15/2011 12:02:00 |
|  3  | 07/15/2011 12:00:00 | 07/15/2011 12:03:00 |
|  4  | 07/15/2011 12:01:00 | 07/15/2011 12:05:00 |
|  5  | 07/15/2011 12:01:00 | 07/15/2011 12:03:00 |
|  6  | 07/15/2011 12:03:00 | 07/15/2011 12:04:00 |
|  7  | 07/15/2011 12:03:00 | 07/15/2011 12:07:00 |
|  8  | 07/15/2011 12:03:00 | 07/15/2011 12:06:00 |
|  9  | 07/15/2011 12:04:00 | 07/15/2011 12:05:00 |
| 10  | 07/15/2011 12:05:00 | 07/15/2011 12:07:00 |
| 11  | 07/15/2011 12:08:00 | 07/15/2011 12:09:00 |

При такой небольшой выборке данных это достаточно просто визуализировать (я предполагаю, что поток может завершить процесс и мгновенно выбрать следующий без дополнительных затрат для целей этого вопроса)

12:XX: | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
Thread1: 1---4---------------10------]   11--]
Thread2: 2-------]   6---9---]
Thread3: 3-----------7---------------]
Thread4:     5-------8-----------]

И оттуда вы можете легко сказать, что общее время, потраченное на работу над 11 процессами, составило 8 минут.

Проблема возникает из-за того, что я имею дело с тысячами записей, и есть некоторые периоды времени, когда обработка вообще не происходит.

Как получить этот результат, используя запрос PL / SQL, выбранный из таблицы?

Ответы [ 3 ]

3 голосов
/ 16 июля 2011

Это один из подходов, который вы можете использовать.Создайте список возможных времен (в вашем случае, вплоть до секунд) и используйте «существует» или присоедините его к таблице, чтобы получить секунды для каждого идентификатора.

*Изменить: Новый пример с использованием существует вместо Join .. больше в соответствии с вопросом, что запрос пытается ответить .. (На сколько секунд работал хотя бы один активный процесс ..?).Установите данные под запросом ... * '

select count(time1)  from 
(
  /* All possible seconds in the time frame you want to track */ 
select trunc(sysdate) + level/(24*60*60) time1
        from dual
        connect by level <= (trunc(sysdate)-trunc(sysdate-1))*24*60*60
) all_times
where exists (
  /* Atleast one process is running at that second...*/
  select 1 
    from t1 
    where t1.start_time < all_times.time1
      and t1.end_time >= all_times.time1
)

Count(time1)

11

Для простоты понимания я взял только 4 случая / записи.Первые три записи пересекаются, а последняя представляет собой полную непересекающуюся запись.

create table t1(
   id number,
   start_time date,
   end_time   date
);

SQL>  insert into t1 values ( 1,
  2      to_date('07/15/2011 12:00:00','MM/DD/YYYY HH24:MI:SS'),
  3      to_date('07/15/2011 12:00:05','MM/DD/YYYY HH24:MI:SS'));

1 row created.

SQL> insert into t1 values ( 1,
  2      to_date('07/15/2011 12:00:02','MM/DD/YYYY HH24:MI:SS'),
  3      to_date('07/15/2011 12:00:04','MM/DD/YYYY HH24:MI:SS'));

1 row created.

SQL> insert into t1 values ( 1,
  2      to_date('07/15/2011 12:00:03','MM/DD/YYYY HH24:MI:SS'),
  3      to_date('07/15/2011 12:00:06','MM/DD/YYYY HH24:MI:SS'));

1 row created.

SQL> insert into t1 values ( 1,
  2      to_date('07/15/2011 12:00:15','MM/DD/YYYY HH24:MI:SS'),
  3      to_date('07/15/2011 12:00:20','MM/DD/YYYY HH24:MI:SS'));

1 row created.

SQL> commit;

alter session set NLS_DATE_FORMAT = "MM/DD/YYYY HH24:MI:SS";

SQL> select * from t1;

        ID START_TIME          END_TIME
---------- ------------------- -------------------
         1 07/15/2011 12:00:00 07/15/2011 12:00:05
         1 07/15/2011 12:00:02 07/15/2011 12:00:04
         1 07/15/2011 12:00:03 07/15/2011 12:00:06
         1 07/15/2011 12:00:15 07/15/2011 12:00:20

Эта часть возвращает вам список всех времен (до секунд) 15 июля ...

select trunc(sysdate) + level/(24*60*60) time1
from dual
connect by level <= (trunc(sysdate)-trunc(sysdate-1))*24*60*60

Присоедините это к основному столу, и вы получите это ... (каждую минуту, в которую запущен процесс ...)

SQL > break on id on start_time on end_time

select t1.id,
       t1.start_time,
       t1.end_time,
       all_seconds.time1
  from t1,
       (select trunc(sysdate) + level/(24*60*60) time1
        from dual
        connect by level <= (trunc(sysdate)-trunc(sysdate-1))*24*60*60
       ) all_seconds
  where all_seconds.time1 > t1.start_time
    and all_seconds.time1 <= t1.end_time 

       ID START_TIME          END_TIME            TIME1
---------- ------------------- ------------------- -------------------
         1 07/15/2011 12:00:00 07/15/2011 12:00:05 07/15/2011 12:00:01
                                                   07/15/2011 12:00:02
                                                   07/15/2011 12:00:03
                                                   07/15/2011 12:00:04
                                                   07/15/2011 12:00:05
         2 07/15/2011 12:00:02 07/15/2011 12:00:04 07/15/2011 12:00:03
                                                   07/15/2011 12:00:04
         3 07/15/2011 12:00:03 07/15/2011 12:00:06 07/15/2011 12:00:04
                                                   07/15/2011 12:00:05
                                                   07/15/2011 12:00:06
         4 07/15/2011 12:00:15 07/15/2011 12:00:20 07/15/2011 12:00:16
                                                   07/15/2011 12:00:17
                                                   07/15/2011 12:00:18
                                                   07/15/2011 12:00:19
                                                   07/15/2011 12:00:20

Отсюда ... Все, что вам нужно, это отличноеотсчет минут.Если у вас есть другой столбец для группировки, вам нужно будет изменить этот запрос в конце.(пример ... Проекты, даты начала и даты окончания ... для которых вы хотите общее количество рабочих дней .., вам нужно сгруппировать по project_id (??))

select count(distinct(time1)) total_minutes
  from (
select t1.id,
       t1.start_time,
       t1.end_time,
       all_seconds.time1
  from t1,
       (select trunc(sysdate) + level/(24*60*60) time1
        from dual
        connect by level <= (trunc(sysdate)-trunc(sysdate-1))*24*60*60
       ) all_seconds
  where all_seconds.time1 > t1.start_time
    and all_seconds.time1 <= t1.end_time
 )
/

TOTAL_MINUTES
-------------
           11

Проблема сэто решение состоит в том, что это (более или менее) грубая сила.Вы должны создать список всех возможных времен (если у вас есть данные, охватывающие несколько дат, и вам нужно время обработки с точностью до секунд, сгенерированные данные будут огромными).В зависимости от того, насколько велика ваша фактическая таблица, соединение может быть неэффективным.

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

1 голос
/ 15 июля 2011

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

SELECT (MAX(end_date) - MIN(start_time))*24*60 AS elapsed_time
FROM   processes;

Это вернет время, прошедшее в минутах.


Судя по комментарию, кажется, что вы хотите узнать, сколько времени прошло для каждого процесса? Если это так, то это всего лишь небольшой вариант предыдущего ответа:

SELECT sum(end_date - start_time)*24*60 AS elapsed_time
FROM   processes;

Очевидно, что этот ответ не имеет отношения к определению, для каких процессов получить итоговую сумму, но это вообще не рассматривается в вопросе, поэтому я предполагаю, что это не проблема.


Теперь я вижу, что это не дает того ответа, который вы ищете, потому что он подсчитывает время, отработанное несколькими процессами несколько раз. Я считаю, что следующее будет работать. Это основано на данных примера @ Раджеша из другого ответа.

SELECT SUM (new_end - new_start) * 24 * 60 * 60
FROM   (SELECT   DISTINCT
                 MIN (LEAST (a.start_time, b.start_time)) AS new_start, 
                 MAX (GREATEST (a.end_time, b.end_time)) AS new_end
        FROM     (SELECT   ROWNUM AS rnum, t1.* FROM t1) a
                 INNER JOIN t1 b
                 ON a.start_time <= b.end_time AND a.end_time >= b.start_time
        GROUP BY rnum)

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

Я считаю, что здесь есть еще один недостаток, касающийся каскадных периодов времени: период A перекрывается с периодом B, а период B перекрывается с периодом C, но период C не перекрывается с периодом A. Я думаю, что эта проблема разрешима, я просто еще не совсем понял.


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

SELECT SUM (new_end - new_start) * 24 * 60 * 60
FROM   (SELECT   DISTINCT MIN (start_time) AS new_start, 
                          MAX (end_time) AS new_end
        FROM     (SELECT  start_time, 
                          end_time, 
                          CONNECT_BY_ROOT rnum AS rnum
                  FROM    (SELECT     ROWNUM AS rnum, t1.* FROM t1) a
                  CONNECT BY NOCYCLE 
                         a.start_time <= PRIOR end_time 
                         AND a.end_time >= PRIOR start_time)
        GROUP BY rnum);
0 голосов
/ 05 августа 2011

Хотя ответ @ Аллана действительно решает мою проблему, ужасно медленно на больших наборах данных.Хотя это моя вина, так как я специально спросил запрос, а не просто решение.

Вот сценарий, который я в итоге использовал.

DECLARE
    start_time DATE;
    end_time DATE;
    first_loop boolean := TRUE;
    duration NUMBER := 0;
    CURSOR client_data IS
      SELECT start_date s, end_date e
      FROM table_name;
BEGIN
    FOR rec IN client_data LOOP
        IF ( first_loop ) THEN
            first_loop := FALSE;
            start_time := rec.s;
            end_time := rec.e;
        ELSE
            IF ( rec.s > end_time ) THEN
                duration := duration + (end_time - start_time);
                start_time := rec.s;
                end_time := rec.e;
            ELSIF ( rec.e > end_time ) THEN
                end_time := rec.e;
            END IF;
        END IF;  
    END LOOP;
    duration := duration + (end_time - start_time);
    dbms_output.put_line(duration*24*60*60 || ' seconds');
END;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...