如何使用 SQL 减去不同列中两行的值
How can I subtract two row's values within DIFFERENT columns using SQL
我需要帮助计算进程之间的停机时间。它需要按 IMPORTID 分组,然后按 IMPORTREQUESTID。 IMPORTREQUESTID 对应于 IMPORTID 请求中的不同阶段。因此,例如在下面的数据集中,我需要我的 SQL 查询在 IMPORTREQUESTID 中找到 highest 或 Latest ENDDATE,然后减去 STARTDATE它的下一行的(最低)或者下一个 IMPORTREQUESTID 集群的 min(STARTDATE)。我已经对我的查询进行了排序,以按降序对 IMPORT ID 和 IMPORTREQUEST ID 进行排序。我应该使用 "CONNECT BY" 吗?在 IMPORTREQUESTID 的行中分组并找到最高的 ENDDATE,然后在 IMPORTREQUESTID 的下一个直接行或簇中从中减去最低的 STARTDATE 的最佳方法是什么?基本上,我试图计算一个进程完成时间和下一个进程开始时间之间的间隔时间。示例数据见下文 table:
IMPORTID IMPORTREQUESTID STARTDATE ENDDATE
1156 63833 4/23/2017 18:18 4/23/2017 18:18
1156 63833 4/23/2017 18:18 4/23/2017 18:18
1156 63832 4/23/2017 17:56 4/23/2017 17:57
1156 63832 4/23/2017 17:56 4/23/2017 17:57
1156 63832 4/23/2017 17:56 4/23/2017 17:57
预期结果:上面的查询将return对我来说是为整组行获得的所有差异的总和,并最终显示每个 IMPORTID:
ImportID TOTAL Downtime
1156 21 mins
或者更好:
一个详细的细分,每个唯一的 IMPORTREQUESTID(上一行的结束日期和下一行的开始日期)之间的间隔秒数解释为 return 以下唯一行(一个阶段的最大 ENDDATE 和进行的最小下一阶段的(开始日期):
IMPORTID IMPORTREQUESTID STARTDATE ENDDATE DIFF
1156 63833 4/23/2017 18:18 4/23/2017 18:18 21 mins
1156 63832 4/23/2017 17:56 4/23/2017 17:57
是这样的吗?我还没有完全理解这个问题.. 特别是 61 秒来自哪里我得到 21 分钟。我也不知道你为什么在你的例子中重复数据....所以我使用不同的方式删除它。
这里有两个关键点。
1) LEAD 是一个 window 函数,可以让我们按照定义的顺序向前看下一条记录。我们还可以 "PARTITION" 这样每个系列都不会向前看,除非 ImportId 和 ImportRequestID 匹配所以
Round((EndDate-LEAD(EndDate) over (order by ImportID, ImportRequestID DESC))*60*24)
会变成
Round((EndDate-LEAD(EndDate) over (PARTITION BY IMPORTID order by ImportRequestID DESC))*60*24)
2) 我使用 distinct 来消除看似重复的记录;但我怀疑您的数据集是否真的有重复项,因此可能不需要;或者您的连接不完整导致重复。
With CTE (IMPORTID, ImportRequestID, StartDate, EndDate) as (
SELECT 1156, 63833, to_date('4/23/2017 18:18','MM/DD/YYYY HH24:MI'), to_date('4/23/2017 18:18','MM/DD/YYYY HH24:MI') FROM DUAL UNION ALL
SELECT 1156, 63833, to_date('4/23/2017 18:18','MM/DD/YYYY HH24:MI'), to_date('4/23/2017 18:18','MM/DD/YYYY HH24:MI') FROM DUAL UNION ALL
SELECT 1156, 63832, to_date('4/23/2017 17:56','MM/DD/YYYY HH24:MI'), to_date('4/23/2017 17:57','MM/DD/YYYY HH24:MI') FROM DUAL UNION ALL
SELECT 1156, 63832, to_date('4/23/2017 17:56','MM/DD/YYYY HH24:MI'), to_date('4/23/2017 17:57','MM/DD/YYYY HH24:MI') FROM DUAL UNION ALL
SELECT 1156, 63832, to_date('4/23/2017 17:56','MM/DD/YYYY HH24:MI'), to_date('4/23/2017 17:57','MM/DD/YYYY HH24:MI') FROM DUAL)
SELECT ImportID
, ImportRequestID
, to_char(StartDate,'MM/DD/YYYY HH24:MI') StartDate
, to_char(EndDate,'MM/DD/YYYY HH24:MI') EndDate
, Round((EndDate-LEAD(EndDate) over (order by ImportID, ImportRequestID DESC))*60*24) as Minutediff
FROM (SELECT DISTINCT ImportID
, ImportRequestID
, StartDate
, EndDate
From CTE) B
您可以按 importid 和 importrequestid 对数据进行排序,以确保它们按正确的时间顺序排列。然后如 xQbert 所述,使用 lead() 或 lag() 为下一个过程的结束日期时间和开始日期时间制作列。
lag(enddate, 1) over (order by importrequestid) as priorend
然后使用类似于下面的函数来求差。
create or replace function timestamp_diff_in_seconds (ts1 in timestamp, ts2 in timestamp)
return number is total_secs number;
diff interval day(9) to second(6);
begin
diff := ts2 - ts1;
total_secs := abs(extract(second from diff) + extract(minute from diff)*60 + extract(hour from diff)*60*60 + extract(day from diff)*24*60*60);
return total_secs;
end timestamp_diff_in_seconds;
然后调用函数示例...
select timestamp_diff_in_seconds(priorend, startdate) as downtime.
我需要帮助计算进程之间的停机时间。它需要按 IMPORTID 分组,然后按 IMPORTREQUESTID。 IMPORTREQUESTID 对应于 IMPORTID 请求中的不同阶段。因此,例如在下面的数据集中,我需要我的 SQL 查询在 IMPORTREQUESTID 中找到 highest 或 Latest ENDDATE,然后减去 STARTDATE它的下一行的(最低)或者下一个 IMPORTREQUESTID 集群的 min(STARTDATE)。我已经对我的查询进行了排序,以按降序对 IMPORT ID 和 IMPORTREQUEST ID 进行排序。我应该使用 "CONNECT BY" 吗?在 IMPORTREQUESTID 的行中分组并找到最高的 ENDDATE,然后在 IMPORTREQUESTID 的下一个直接行或簇中从中减去最低的 STARTDATE 的最佳方法是什么?基本上,我试图计算一个进程完成时间和下一个进程开始时间之间的间隔时间。示例数据见下文 table:
IMPORTID IMPORTREQUESTID STARTDATE ENDDATE
1156 63833 4/23/2017 18:18 4/23/2017 18:18
1156 63833 4/23/2017 18:18 4/23/2017 18:18
1156 63832 4/23/2017 17:56 4/23/2017 17:57
1156 63832 4/23/2017 17:56 4/23/2017 17:57
1156 63832 4/23/2017 17:56 4/23/2017 17:57
预期结果:上面的查询将return对我来说是为整组行获得的所有差异的总和,并最终显示每个 IMPORTID:
ImportID TOTAL Downtime
1156 21 mins
或者更好:
一个详细的细分,每个唯一的 IMPORTREQUESTID(上一行的结束日期和下一行的开始日期)之间的间隔秒数解释为 return 以下唯一行(一个阶段的最大 ENDDATE 和进行的最小下一阶段的(开始日期):
IMPORTID IMPORTREQUESTID STARTDATE ENDDATE DIFF
1156 63833 4/23/2017 18:18 4/23/2017 18:18 21 mins
1156 63832 4/23/2017 17:56 4/23/2017 17:57
是这样的吗?我还没有完全理解这个问题.. 特别是 61 秒来自哪里我得到 21 分钟。我也不知道你为什么在你的例子中重复数据....所以我使用不同的方式删除它。
这里有两个关键点。
1) LEAD 是一个 window 函数,可以让我们按照定义的顺序向前看下一条记录。我们还可以 "PARTITION" 这样每个系列都不会向前看,除非 ImportId 和 ImportRequestID 匹配所以
Round((EndDate-LEAD(EndDate) over (order by ImportID, ImportRequestID DESC))*60*24)
会变成
Round((EndDate-LEAD(EndDate) over (PARTITION BY IMPORTID order by ImportRequestID DESC))*60*24)
2) 我使用 distinct 来消除看似重复的记录;但我怀疑您的数据集是否真的有重复项,因此可能不需要;或者您的连接不完整导致重复。
With CTE (IMPORTID, ImportRequestID, StartDate, EndDate) as (
SELECT 1156, 63833, to_date('4/23/2017 18:18','MM/DD/YYYY HH24:MI'), to_date('4/23/2017 18:18','MM/DD/YYYY HH24:MI') FROM DUAL UNION ALL
SELECT 1156, 63833, to_date('4/23/2017 18:18','MM/DD/YYYY HH24:MI'), to_date('4/23/2017 18:18','MM/DD/YYYY HH24:MI') FROM DUAL UNION ALL
SELECT 1156, 63832, to_date('4/23/2017 17:56','MM/DD/YYYY HH24:MI'), to_date('4/23/2017 17:57','MM/DD/YYYY HH24:MI') FROM DUAL UNION ALL
SELECT 1156, 63832, to_date('4/23/2017 17:56','MM/DD/YYYY HH24:MI'), to_date('4/23/2017 17:57','MM/DD/YYYY HH24:MI') FROM DUAL UNION ALL
SELECT 1156, 63832, to_date('4/23/2017 17:56','MM/DD/YYYY HH24:MI'), to_date('4/23/2017 17:57','MM/DD/YYYY HH24:MI') FROM DUAL)
SELECT ImportID
, ImportRequestID
, to_char(StartDate,'MM/DD/YYYY HH24:MI') StartDate
, to_char(EndDate,'MM/DD/YYYY HH24:MI') EndDate
, Round((EndDate-LEAD(EndDate) over (order by ImportID, ImportRequestID DESC))*60*24) as Minutediff
FROM (SELECT DISTINCT ImportID
, ImportRequestID
, StartDate
, EndDate
From CTE) B
您可以按 importid 和 importrequestid 对数据进行排序,以确保它们按正确的时间顺序排列。然后如 xQbert 所述,使用 lead() 或 lag() 为下一个过程的结束日期时间和开始日期时间制作列。
lag(enddate, 1) over (order by importrequestid) as priorend
然后使用类似于下面的函数来求差。
create or replace function timestamp_diff_in_seconds (ts1 in timestamp, ts2 in timestamp)
return number is total_secs number;
diff interval day(9) to second(6);
begin
diff := ts2 - ts1;
total_secs := abs(extract(second from diff) + extract(minute from diff)*60 + extract(hour from diff)*60*60 + extract(day from diff)*24*60*60);
return total_secs;
end timestamp_diff_in_seconds;
然后调用函数示例...
select timestamp_diff_in_seconds(priorend, startdate) as downtime.