多线程 - 查询以显示处理一组进程所用的总时间

时间:2011-07-15 19:34:20

标签: multithreading algorithm oracle plsql

我正在尝试确定完成多线程应用程序的一组进程所花费的总时间,该应用程序跟踪表中的开始和结束时间。描述我的问题最简单的方法是举个例子。

这是我正在使用的表格(我们称之为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 个答案:

答案 0 :(得分:3)

这是您可以采取的一种方法。生成可能的时间列表(在您的情况下,一直到秒)并使用“exists”或将其加入到您的表中以获取每个ID的秒数。

* 编辑:使用exists而不是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

这部分将为您提供7月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 :(得分:1)

除非我遗漏了某些内容,否则您需要的是最低开始日期和最高结束日期之间的差异:

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;

显然,这个答案并不涉及确定哪些进程可以获得总数,但这根本没有解决,所以我认为这不是问题。


我现在看到,这并没有提供您正在寻找的答案,因为它会多次计算多个进程的工作时间。我相信以下内容会奏效。这是基于@ Rajesh的另一个答案的样本数据。

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);

答案 2 :(得分:0)

虽然@ Allan的答案确实解决了我的问题,但是在大型数据集上可怕慢。这是我的错,因为我专门询问了一个查询,而不仅仅是一个解决方案。

这是我最终使用的脚本。

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;