2013-02-15 69 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