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,该拖车将其位置 ping 到 LOC_ZONE,然后将其存储在当时的 LOC_DATE 中。

我试图找到的是特定位置区域中所有预告片花费的平均时间。例如,如果拖车 x 在本地区域 PLANSUFVA 中呆了 4 小时,而拖车 y 在本地区域 PLANSUFVA 中呆了 6 小时,我想返回

Loc Zone  Avg Time  
PLANSUFVA   5

无论如何,没有游标可以做到这一点吗?

我真的很感谢你的帮助。

4

5 回答 5

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
于 2013-02-15T18:47:05.740 回答
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(下一个位置的第一次减去该位置的第一次)。

此外,这将为 each 的最后一个位置返回 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 是下一个区域的日期。

于 2013-02-15T18:46:04.843 回答
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
于 2013-02-15T19:02:57.933 回答
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

这说明了在同一地点的多次住宿。lag之间的选择lead取决于逗留应该以 ping 开始还是结束(即,如果一个拖车从 A 发送 ping,然后 x 小时后从 B 发送 ping,这对 A 或 B 算吗)。

于 2013-02-15T19:54:29.093 回答
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 在这里。

(由于 SQLServer 计算时间差的方式,最好以秒为单位计算平均时间,然后除以 60*60。除了 getdate() 和 datediff 子句 - 可以替换为sysdateand next_date - loc_date- 这应该可在 SQLServer 2005 及更高版本和 Oracle 10g 及更高版本中工作。)

于 2013-02-15T18:57:11.493 回答