2013-02-15 63 views
0

改变我必须计算平均花费的时间基础上的定位区

create table LOCHIST 
(
    RES_ID VARCHAR(10) NOT NULL, 
    LOC_DATE TIMESTAMP NOT NULL, 
    LOC_ZONE VARCHAR(10) 
) 

类似的表值,如

insert into LOCHIST values(0911,2015-09-23 12:27:00.000000,SYLVSYLGA); 
insert into LOCHIST values(5468,2013-02-15 13:13:24.000000,30726); 
insert into LOCHIST values(23894,2013-02-15 13:12:13.000000,BECTFOUNC); 
insert into LOCHIST values(24119,2013-02-15 13:12:09.000000,30363); 
insert into LOCHIST values(7101,2013-02-15 13:11:37.000000,37711); 
insert into LOCHIST values(26083,2013-02-15 13:11:36.000000,SHAWANDAL); 
insert into LOCHIST values(24978,2013-02-15 13:11:36.000000,38132); 
insert into LOCHIST values(26696,2013-02-15 13:11:27.000000,29583); 
insert into LOCHIST values(5468,2013-02-15 13:11:00.000000,37760); 
insert into LOCHIST values(5552,2013-02-15 13:10:55.000000,30090); 
insert into LOCHIST values(24932,2013-02-15 13:10:48.000000,JBTTLITGA); 
insert into LOCHIST values(23894,2013-02-15 13:10:42.000000,47263); 
insert into LOCHIST values(26803,2013-02-15 13:10:25.000000,32534); 
insert into LOCHIST values(24434,2013-02-15 13:10:03.000000,PLANSUFVA); 
insert into LOCHIST values(26696,2013-02-15 13:10:00.000000,GEORALBGA); 
insert into LOCHIST values(5468,2013-02-15 13:09:54.000000,19507); 
insert into LOCHIST values(23894,2013-02-15 13:09:48.000000,37725); 

此表字面上去上百万条记录。

每个RES_ID代表一个拖车的ID,他将他们的位置压入LOC_ZONE,然后在LOC_DATE存储该LOC_ZONE。

我试图找到的是所有拖车在特定位置区域花费的平均时间。例如,如果拖车X在禄区PLANSUFVA花了4小时,拖车Y美元在禄区PLANSUFVA6小时我希望回到

Loc Zone Avg Time 
PLANSUFVA 5 

反正有这样做没有游标?

我真的很感谢你的帮助。

+0

鉴于您发布的示例数据,您希望得到的预期输出是什么,为什么?数字“LOC_ZONE”值和非数字“LOC_ZONE”值有区别吗?如果您修改了“INSERT”语句,以便它们在语法上也是有效的,那将会非常有帮助。 – 2013-02-15 18:42:48

+1

为什么这个标签sql-server和Oracle? – 2013-02-15 18:46:19

+0

对于我的目的,数字和非数字区域可以被同等对待。 – azoorob 2013-02-15 19:28:55

回答

0

要解决此问题,您需要在每个位置花费的时间量。

要做到这一点的一种方法是使用相关的子查询。您需要对相邻的值进行分组。这个想法是找到序列中的下一个值:

select resid, min(loc_zone) as loc_zone, min(loc_date) as StartTime, 
     max(loc_date) as EndTime, 
     nextdate as NextStartTime 
from (select lh.*, 
      (select min(loc_date) from lochist lh2 
       where lh2.res_id = lh.res_id and lh2.loc_zone <> lh.loc_zone and 
        lh2.loc_date > lh.loc_date 
      ) as nextdate 
     from lochist lh 
    ) lh 
group by lh.res_id, nextdate 

有了这些数据,您就可以获得所需的平均值。

我不清楚时间是否应该基于EndTime - StartTime(上次记录时间减去第一个记录时间)或NextStartTime - startTime(第一次在下一个位置减去第一次在此位置)。

此外,这将返回NULL为每个res_id的最后一个位置。你不会说这个序列中的最后一个怎么办。

如果您在res_id, loc_date, loc_zone上构建索引,则运行速度可能会更快。

如果你有Oracle或SQL Server 2012中,正确的查询是:

select lh.*, 
     lead(loc_date) over (partition by res_id order by loc_date) as nextdate 
from (select lh.*, 
      lag(loc_zone) over (partition by res_id order by loc_date) as prevzone 
     from lochist lh 
    ) lh 
where prevzone is null or prevzone <> loc_zone 

现在,你必须每次住宿一行,nextdate在下一区的日期。

+0

嗨,感谢您的帮助。时间应该基于第一次在下一个位置减去第一次在这个位置。当我尝试运行你的查询时,它试图执行大约5分钟,但从来没有,我认为我的表包含了太多的数据。 – azoorob 2013-02-15 19:36:28

+0

@ user2076550。 。 。首先尝试一部分数据,如一天或一个月的价值。 – 2013-02-15 20:02:55

+0

非常感谢您的帮助。您发布的第二个查询效果很好,但我有一个问题。如果一个拖车在同一位置连续打5次(这个错误发生很多),那么从另一个位置ping,这个查询不会给出从第5次ping到第6次而不是第1次到第6次的时间? – azoorob 2013-02-15 20:16:12

1

这需要SQL 2012:

with data 
as (
     select *, (case when LOC_ZONE != PREVIOUS_LOC_ZONE or PREVIOUS_LOC_ZONE is null then ROW_ID else null end) as STAY_START, (case when LOC_ZONE != NEXT_LOC_ZONE or NEXT_LOC_ZONE is null then ROW_ID else null end) as STAY_END 
     from (
      select RES_ID, LOC_ZONE, LOC_DATE, lead(LOC_DATE, 1) over (partition by RES_ID, LOC_ZONE order by LOC_DATE) as NEXT_LOC_DATE, lag(LOC_ZONE, 1) over (partition by RES_ID order by LOC_DATE) as PREVIOUS_LOC_ZONE, lead(LOC_ZONE, 1) over (partition by RES_ID order by LOC_DATE) as NEXT_LOC_ZONE, ROW_NUMBER() over (order by RES_ID, LOC_ZONE, LOC_DATE) as ROW_ID 
      from LOCHIST 
    ) t 
), stays as (
     select * from (
      select RES_ID, LOC_ZONE, STAY_START, lead(STAY_END, 1) over (order by ROWID) as STAY_END 
      from (
        select RES_ID, LOC_ZONE, STAY_START, STAY_END, ROW_NUMBER() over (order by RES_ID, LOC_ZONE, STAY_START desc) as ROWID 
        from data 
        where STAY_START is not null or STAY_END is not null 
      ) t 
    ) t 
     where STAY_START is not null and STAY_END is not null 
) 
select s.LOC_ZONE, avg(datediff(second, LOC_DATE, NEXT_LOC_DATE))/60/60 as AVG_IN_HOURS 
from data d 
inner join stays s on d.RES_ID = s.RES_ID and d.LOC_ZONE = s.LOC_ZONE and d.ROW_ID >= s.STAY_START and d.ROW_ID < s.STAY_END 
group by s.LOC_ZONE 
+0

我不认为你想通过LOC_DATE分区。另外,我认为你通过为每个分区添加一个零长度的行(通过'isnull(NEXT_LOCDATE,LOC_DATE)')来歪曲平均值) – Martin 2013-02-15 19:28:46

+0

LOC_ZONE!更新。另外,如果预告片要重新访问地点,你应该小心谨慎。 – muhmud 2013-02-15 19:30:50

+0

关于平均值的事情也是如此 - 更新。好点 – muhmud 2013-02-15 19:43:36

0

要做到这一点,而无需使用游标或相关子查询,请尝试:

with rl as 
(select l.*, rank() over (partition by res_id order by loc_date) rn 
from lochist l), 
fdr as 
(select rc.*, coalesce(rn.loc_date, getdate()) next_date 
from rl rc 
left join rl rn on rc.res_id = rn.res_id and rc.rn + 1 = rn.rn) 
select loc_zone, avg(datediff(second, loc_date, next_date))/3600 avg_time 
from fdr 
group by loc_zone 
(的

SQLFiddle here.

因为这SQLServer的方式计算时差,计算平均时间以秒为单位然后除以60 * 60可能会更好,除了getdate()和datedif f子句 - 可以用sysdatenext_date - loc_date来代替 - 这应该可以在SQLServer 2005以及Oracle 10g之后运行。)

0

这应该让您按照花费的平均分钟数排序每个区域。 CROSS APPLY返回另一个区域中的下一个ping。

SELECT 
    loc.LOC_ZONE 
    ,AVG(DATEDIFF(mi,loc.LOC_DATE,nextPing.LOC_DATE)) AS avgMinutes 
FROM LOCHIST loc 
CROSS APPLY(
    SELECT TOP 1 loc2.LOC_DATE 
    FROM LOCHIST loc2 
    WHERE loc2.RES_ID = loc.RES_ID 
    AND loc2.LOC_DATE > loc.LOC_DATE 
    AND loc2.LOC_ZONE <> loc.LOC_ZONE 
    ORDER BY loc2.LOC_DATE ASC 
) AS nextPing 
GROUP BY loc.LOC_ZONE 
ORDER BY avgMinutes DESC 
+0

感谢您的帮助。当我尝试运行此操作时,出现以下错误:在“OM LOCHIST loc CROSS”后面找到意外标记“APPLY” 。预计 令牌可能包括:“JOIN”。 SQLSTATE = 42601 错误代码:-104 – azoorob 2013-02-15 19:31:05

+0

您正在使用什么版本的SQL Server? 'CROSS APPLY'只能在2005年以及更新的版本中使用。 – supergrady 2013-02-15 20:05:35

+0

2000,谢谢你让我知道。我在这个 – azoorob 2013-02-15 20:25:40

0

我的解决方案的变化:

select LOC_ZONE, avg(TOTAL_TIME) AVG_TIME from (
    select RES_ID, LOC_ZONE, sum(TIME_SPENT) TOTAL_TIME 
    from (
     select RES_ID, LOC_ZONE, datediff(mi, lag(LOC_DATE, 1) over (
      partition by RES_ID order by LOC_DATE), LOC_DATE) TIME_SPENT 
     from LOCHIST 
    ) t 
    where TIME_SPENT is not null 
    group by RES_ID, LOC_ZONE) f 
group by LOC_ZONE 

这说明了在同一地点多次停留。 laglead之间的选择取决于停留是否应以ping开始或结束(即,如果一个预告片从A发送ping,然后从B发送x小时,那么这是否指向A或B)。

+0

尼斯还是很新的。关于边界值的位虽然。我了解它的方式,停留开始/结束与位置变化。 – muhmud 2013-02-15 21:29:19