3

1 秒に 1 回記録された、特定の時間における過去のバス位置のテーブルがあります。スキーマは次のようになります。

BusID        int         not null,
BreadcrumbID int         not null identity (1, 1),
BusStopID    int         null,
Timestamp    datetime    not null

過去の旅行に基づいてバス停のスケジュールを生成したいと考えています。バスは、停車場に対応する場合は「停車中」であり、が nullBusStopIDの場合は「停車中」ではありません。BusStopID

バスが各停留所にいる平均時間を生成する必要があります。したがって、基本的には、次のことを行う必要があります。

  • バスが停車する時刻を特定する -- 簡単なwhere句でうまくいく
  • バスが停車している平均時間を特定します。私の目的のために、個別の「停止時間」をプラス/マイナス 10 分のウィンドウとして定義しています。バスが 1 日 10:04 ~ 10:08 に停車し、別の日が 10:06 ~ 10:08 に停車し、3 日目が 10:14 ~ 10:18 に停車する場合、それらは同じ停車地になりますが、停車する場合10:45 ~ 10:48 の場合、別の停留所が発生します。
  • 「ノイズ」を除外します。つまり、数回しか発生せず、二度と発生しない時間を停止します

2番目と3番目の弾丸をどのように達成するかについて、私は完全に途方に暮れています。助けてください!

4

4 に答える 4

2

私が見たこの投稿はあなたを助けるかもしれません。(Sql サーバー セントラル)

于 2010-12-07T14:59:58.873 に答える
2

何度か、私は似たようなことをしました。基本的に、複雑な順序付け内の分離に基づくグループ化。この問題に関して、私が使用するアプローチの基本は次のとおりです。

  1. 関心のあるすべての時間範囲のテーブルを作成します。
  2. 対象の時間範囲の各グループの開始時間を見つけます。
  3. 関心のある時間範囲の各グループの終了時間を見つけます。
  4. 開始時刻と終了時刻を時間範囲のリストに結合し、グループ化します。

または、より詳細には:(これらの各ステップは1つの大きなCTEの一部である可能性がありますが、読みやすくするために一時テーブルに分割しました...)

ステップ 1: 関心のあるすべての時間範囲のリストを見つけます (@Brad がリンクしている方法と同様の方法を使用しました)。 注:@Manfred Sorgが指摘したように、これはバスのデータに「欠落秒」がないことを前提としています。タイムスタンプに切れ目がある場合、このコードは単一の範囲を 2 つ (またはそれ以上) の異なる範囲として解釈します。

;with stopSeconds as (
  select BusID, BusStopID, TimeStamp,
         [date] = cast(datediff(dd,0,TimeStamp) as datetime),
         [grp] = dateadd(ss, -row_number() over(partition by BusID order by TimeStamp), TimeStamp)
  from #test
  where BusStopID is not null
)
select BusID, BusStopID, date,
       [sTime] = dateadd(ss,datediff(ss,date,min(TimeStamp)), 0),
       [eTime] = dateadd(ss,datediff(ss,date,max(TimeStamp)), 0),
       [secondsOfStop] = datediff(ss, min(TimeStamp), max(Timestamp)),
       [sOrd] = row_number() over(partition by BusID, BusStopID order by datediff(ss,date,min(TimeStamp))),
       [eOrd] = row_number() over(partition by BusID, BusStopID order by datediff(ss,date,max(TimeStamp)))
into #ranges
from stopSeconds
group by BusID, BusStopID, date, grp

ステップ 2: 各停留所の最も早い時間を見つける

select this.BusID, this.BusStopID, this.sTime minSTime,
       [stopOrder] = row_number() over(partition by this.BusID, this.BusStopID order by this.sTime)
into #starts
from #ranges this
  left join #ranges prev on this.BusID = prev.BusID
                        and this.BusStopID = prev.BusStopID
                        and this.sOrd = prev.sOrd+1
                        and this.sTime between dateadd(mi,-10,prev.sTime) and dateadd(mi,10,prev.sTime)
where prev.BusID is null

ステップ 3: 各停留所の最新時刻を検索する

select this.BusID, this.BusStopID, this.eTime maxETime,
       [stopOrder] = row_number() over(partition by this.BusID, this.BusStopID order by this.eTime)
into #ends
from #ranges this
  left join #ranges next on this.BusID = next.BusID
                        and this.BusStopID = next.BusStopID
                        and this.eOrd = next.eOrd-1
                        and this.eTime between dateadd(mi,-10,next.eTime) and dateadd(mi,10,next.eTime)
where next.BusID is null

ステップ 4: すべてを結合する

select r.BusID, r.BusStopID,
       [avgLengthOfStop] = avg(datediff(ss,r.sTime,r.eTime)),
       [earliestStop] = min(r.sTime),
       [latestDepart] = max(r.eTime)
from #starts s
  join #ends e on s.BusID=e.BusID
              and s.BusStopID=e.BusStopID
              and s.stopOrder=e.stopOrder
  join #ranges r on r.BusID=s.BusID
                and r.BusStopID=s.BusStopID
                and r.sTime between s.minSTime and e.maxETime
                and r.eTime between s.minSTime and e.maxETime
group by r.BusID, r.BusStopID, s.stopOrder
having count(distinct r.date) > 1 --filters out the "noise"

最後に、完成させるために整理します。

drop table #ends
drop table #starts
drop table #ranges
于 2010-12-07T19:42:18.377 に答える
0

よくあることですが、これらの種類の問題は、一口サイズの断片に分割することで、解決と維持が容易になります。

-- Split into Date and minutes-since-midnight
WITH observed(dates,arrival,busstop,bus) AS (
    SELECT
        CONVERT(CHAR(8), TimeStamp, 112),
        DATEPART(HOUR,TimeStamp) * 60 + DATEPART(MINUTE,TimeStamp),
        busstopid,
        busid
    FROM
        History
),
-- Identify times at stop subsequent to arrival at that stop
atstop(dates,stoptime,busstop,bus) AS (
    SELECT
        a.dates,
        a.arrival,
        a.busstop,
        a.bus
    FROM
        observed a 
    WHERE
        EXISTS (
            SELECT 
                *
            FROM
                observed b
            WHERE
                a.dates = b.dates AND
                a.busstop = b.busstop AND
                a.bus = b.bus AND
                a.arrival - b.arrival BETWEEN 1 AND 10
        )
),
-- Isolate actual arrivals at stops, excluding waiting at stops
dailyhalts(dates,arrival,busstop,bus) AS (
    SELECT
        a.dates,a.arrival,a.busstop,a.bus
    FROM
        observed a 
    WHERE
        arrival NOT IN (
            SELECT
                stoptime
            FROM 
                atstop b 
            WHERE
                a.dates = b.dates AND
                a.busstop = b.busstop AND
                a.bus = b.bus 
    )
),
-- Merge arrivals across all dates
timetable(busstop,bus,arrival) AS (
    SELECT
        a.busstop, a.bus, a.arrival
    FROM
        dailyhalts a 
    WHERE
        NOT EXISTS (
            SELECT  
                *
            FROM
                dailyhalts h 
            WHERE
                a.busstop = h.busstop AND
                a.bus = h.bus AND
                a.arrival - h.arrival BETWEEN 1 AND 10
        )
    GROUP BY
        a.busstop, a.bus, a.arrival
)
-- Print timetable for a given day
SELECT
    a.busstop, a.bus, a.arrival, DATEADD(minute,AVG(b.arrival),'2010/01/01')
FROM
    timetable a INNER JOIN
    observed b ON
        a.busstop = b.busstop AND
        a.bus = b.bus AND
        b.arrival BETWEEN a.arrival AND a.arrival + 10
GROUP BY
    a.busstop, a.bus, a.arrival

入力:

ID  BusID   BusStopID   TimeStamp
1   1   1   2010-01-01 10:00:00.000
2   1   1   2010-01-01 10:01:00.000
3   1   1   2010-01-01 10:02:00.000
4   1   2   2010-01-01 11:00:00.000
5   1   3   2010-01-01 12:00:00.000
6   1   3   2010-01-01 12:01:00.000
7   1   3   2010-01-01 12:02:00.000
8   1   3   2010-01-01 12:03:00.000
9   1   1   2010-01-02 11:00:00.000
10  1   1   2010-01-02 11:03:00.000
11  1   1   2010-01-02 11:07:00.000
12  1   2   2010-01-02 12:00:00.000
13  1   3   2010-01-02 13:00:00.000
14  1   3   2010-01-02 13:01:00.000
15  1   1   2010-01-03 10:03:00.000
16  1   1   2010-01-03 10:05:00.000

出力:

busstop bus arrival (No column name)
1   1   600 2010-01-01 10:02:00.000
1   1   660 2010-01-01 11:03:00.000
2   1   660 2010-01-01 11:00:00.000
2   1   720 2010-01-01 12:00:00.000
3   1   720 2010-01-01 12:01:00.000
3   1   780 2010-01-01 13:00:00.000
于 2010-12-08T12:08:16.673 に答える
0

新鮮な答え...

これを試してください:

DECLARE @stopWindowMinutes INT
SET @stopWindowMinutes = 10

--
;
WITH    test_data
          AS ( SELECT   1 [BusStopId]
                       ,'2010-01-01 10:00:04' [Timestamp]
               UNION SELECT   1,'2010-01-01 10:00:05'
               UNION SELECT   1,'2010-01-01 10:00:06'
               UNION SELECT   1,'2010-01-01 10:00:07'
               UNION SELECT   1,'2010-01-01 10:00:08'
               UNION SELECT   1,'2010-01-02 10:00:06'
               UNION SELECT   1,'2010-01-02 10:00:07'
               UNION SELECT   1,'2010-01-02 10:00:08'
               UNION SELECT   2,'2010-01-01 10:00:06'
               UNION SELECT   2,'2010-01-01 10:00:07'
               UNION SELECT   2,'2010-01-01 10:00:08'
               UNION SELECT   2,'2010-01-01 10:00:09'
               UNION SELECT   2,'2010-01-01 10:00:10'
               UNION SELECT   2,'2010-01-01 10:00:09'
               UNION SELECT   2,'2010-01-01 10:00:10'
               UNION SELECT   2,'2010-01-01 10:00:11'
               UNION SELECT   1,'2010-01-02 10:33:43'
               UNION SELECT   1,'2010-01-02 10:33:44'
               UNION SELECT   1,'2010-01-02 10:33:45'
               UNION SELECT   1,'2010-01-02 10:33:46'
             )
    SELECT DISTINCT
            [BusStopId]
           ,[AvgStop]
    FROM    ( SELECT    [a].[BusStopId]
                       ,( SELECT    MIN([b].[Timestamp])
                          FROM      [test_data] b
                          WHERE     [a].[BusStopId] = [b].[BusStopId]
                                    AND CONVERT(VARCHAR(10), [a].[Timestamp], 120) = CONVERT(VARCHAR(10), [b].[Timestamp], 120)
                                    AND [b].[Timestamp] BETWEEN DATEADD(SECOND, -@stopWindowMinutes * 60,
                                                                        [a].[Timestamp])
                                                        AND     DATEADD(SECOND, @stopWindowMinutes * 60, [a].[Timestamp]) -- w/i X minutes

                        ) [MinStop]
                       ,( SELECT    MAX([b].[Timestamp])
                          FROM      [test_data] b
                          WHERE     [a].[BusStopId] = [b].[BusStopId]
                                    AND CONVERT(VARCHAR(10), [a].[Timestamp], 120) = CONVERT(VARCHAR(10), [b].[Timestamp], 120)
                                    AND [b].[Timestamp] BETWEEN DATEADD(SECOND, -@stopWindowMinutes * 60,
                                                                        [a].[Timestamp])
                                                        AND     DATEADD(SECOND, @stopWindowMinutes * 60, [a].[Timestamp]) -- w/i X minutes

                        ) [MaxStop]
                       ,( SELECT    DATEADD(second,
                                            AVG(DATEDIFF(second, CONVERT(VARCHAR(10), [b].[Timestamp], 120),
                                                         [b].[Timestamp])),
                                            CONVERT(VARCHAR(10), MIN([b].[Timestamp]), 120))
                          FROM      [test_data] b
                          WHERE     [a].[BusStopId] = [b].[BusStopId]
                                    AND CONVERT(VARCHAR(10), [a].[Timestamp], 120) = CONVERT(VARCHAR(10), [b].[Timestamp], 120)
                                    AND [b].[Timestamp] BETWEEN DATEADD(SECOND, -@stopWindowMinutes * 60,
                                                                        [a].[Timestamp])
                                                        AND     DATEADD(SECOND, @stopWindowMinutes * 60, [a].[Timestamp]) -- w/i X minutes

                        ) [AvgStop]
              FROM      [test_data] a
              WHERE     CONVERT(VARCHAR(10), [Timestamp], 120) = CONVERT(VARCHAR(10), [Timestamp], 120)
              GROUP BY  [a].[BusStopId]
                       ,[a].[Timestamp]
            ) subset1
于 2010-12-07T13:29:30.613 に答える