我正在尝试确定完成多线程应用程序的一组进程所花费的总时间,该应用程序跟踪表中的开始和结束时间。描述我的问题最简单的方法是举个例子。
这是我正在使用的表格(我们称之为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查询获得此结果?
答案 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;