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は、LOC_ZONEに自分の場所をpingするトレーラーのIDを表し、LOC_ZONEはその時点でLOC_DATEに格納されます。

私が見つけようとしているのは、特定のロケーションゾーンのすべてのトレーラーに費やされた平均時間です。たとえば、トレーラーxがlocゾーンPLANSUFVAで4時間過ごし、トレーラーyがlocゾーンPLANSUFVAで6時間過ごした場合、私は戻りたいと思います。

Loc Zone  Avg Time  
PLANSUFVA   5

カーソルなしでこれを行う方法はありますか?

本当にありがとうございました。

4

5 に答える 5

1

これにはSQL2012が必要です。

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 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

これは、同じ場所での複数の滞在を考慮しています。滞在をpingで開始するか終了するかは、どちらを選択するlagかによって異なります(つまり、1つのトレーラーがAからpingを送信し、x時間後にBからpingを送信した場合、それはAまたはBにカウントされます)。lead

于 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句を除いて-これらはとに置き換えることができますsysdate-next_date - loc_dateこれはSQLServer2005以降とOracle10g以降の両方で動作します。)

于 2013-02-15T18:57:11.493 に答える
0

この問題を解決するには、各場所で費やす時間が必要です。

これを行う1つの方法は、相関サブクエリを使用することです。隣接する値をグループ化する必要があります。アイデアは、シーケンス内の次の値を見つけることです。

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またはSQLServer2012を使用している場合、適切なクエリは次のとおりです。

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

これで、滞在ごとに1つの行があり、nextdateは次のゾーンの日付です。

于 2013-02-15T18:46:04.843 に答える
0

これにより、各ゾーンで費やされた平均分数で並べ替えられます。は、別のゾーンで次のCROSS APPLYpingを返します。

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 に答える