19

SSRS / SQLプロジェクトに取り組んでいて、日付間のギャップを取得するためのクエリを作成しようとしていますが、これを作成する方法が完全にわかりません。基本的に、使用をスケジュールできるデバイスがいくつかあり、次のレポートが必要です。それらが使用されていないときに表示します。

デバイスID、EventStart、およびEventEndの時刻を含むテーブルがあります。クエリを実行して、各デバイスのこれらのイベント間の時刻を取得する必要がありますが、これを行う方法がよくわかりません。

例えば:

Device 1 Event A runs from `01/01/2012 08:00 - 01/01/2012 10:00`
Device 1 Event B runs from `01/01/2012 18:00 - 01/01/2012 20:00`    
Device 1 Event C runs from `02/01/2012 18:00 - 02/01/2012 20:00`    
Device 2 Event A runs from `01/01/2012 08:00 - 01/01/2012 10:00`
Device 2 Event B runs from `01/01/2012 18:00 - 01/01/2012 20:00`

私のクエリはその結果として持つべきです

`Device 1 01/01/2012 10:00 - 01/01/2012 18:00`
`Device 1 01/01/2012 20:00 - 02/01/2012 18:00`
`Device 2 01/01/2012 10:00 - 01/01/2012 18:00`

この表には、平均して約4〜5台のデバイスがあり、おそらく200〜300以上のイベントがあります。

更新:

わかりました。これを更新して、もう少し情報を提供してみます。これについてはあまりよく説明していないようです(申し訳ありません!)

私が扱っているのは、イベントの詳細が記載されたテーブルです。各イベントはフライトシミュレーターの予約です。多数のフライトシミュレーション(テーブルではデバイスと呼ばれます)があり、SSRSレポートを生成しようとしています。各シムが利用可能な日/時間を示すために顧客に提供することができます。

したがって、開始日/終了日パラメーターを渡し、それらの日付の間のすべての可用性を選択します。結果は次のように表示されます。

Device   Available_From       Available_To
 1       01/01/2012 10:00    01/01/2012 18:00`
 1       01/01/2012 20:00    02/01/2012 18:00`
 2       01/01/2012 10:00    01/01/2012 18:00`

また、イベントが重複することもありますが、これは非常にまれであり、データが不良であるため、各デバイスの可用性を個別に知る必要があるため、あるデバイスのイベントが別のデバイスのイベントと重複することは問題ではありません。

4

5 に答える 5

25

クエリ:

間隔を含むフィールドにとという名前が付けStartられ、Finishテーブルに名前が付けられているとするとYOUR_TABLE、クエリ...

SELECT Finish, Start
FROM
    (
        SELECT DISTINCT Start, ROW_NUMBER() OVER (ORDER BY Start) RN
        FROM YOUR_TABLE T1
        WHERE
            NOT EXISTS (
                SELECT *
                FROM YOUR_TABLE T2
                WHERE T1.Start > T2.Start AND T1.Start < T2.Finish
            )
        ) T1
    JOIN (
        SELECT DISTINCT Finish, ROW_NUMBER() OVER (ORDER BY Finish) RN
        FROM YOUR_TABLE T1
        WHERE
            NOT EXISTS (
                SELECT *
                FROM YOUR_TABLE T2
                WHERE T1.Finish > T2.Start AND T1.Finish < T2.Finish
            )
    ) T2
    ON T1.RN - 1 = T2.RN
WHERE
    Finish < Start

...テストデータに次の結果が表示されます。

Finish                      Start
2012-01-01 10:00:00.000     2012-01-01 18:00:00.000

このクエリの重要な特性は、重複する間隔でも機能することです。


アルゴリズム:

1.重複する間隔をマージする

サブクエリは、他の間隔の外側T1にある間隔の開始のみを受け入れます。サブクエリは、間隔の終了に対して同じことを行います。これは、重複を削除するものです。T2

両方とも他の間隔の外側DISTINCTにある2つの同一の間隔の開始(または終了)がある場合に重要です。単に空の間隔(つまり、期間0)を削除します。WHERE Finish < Start

また、次のステップで必要になる時間的順序に関連する行番号を添付します。

T1収量:

Start                       RN
2012-01-01 08:00:00.000     1
2012-01-01 18:00:00.000     2

T2収量:

Finish                      RN
2012-01-01 10:00:00.000     1
2012-01-01 20:00:00.000     2

2.結果を再構築します

これで、「アクティブ」または「非アクティブ」の間隔を再構築できます。

アクティブな間隔は、の間隔の終わりと次の間隔の始まりを組み合わせることによって再構築さ- 1れますON。事実上、私たちは...

Finish                      RN
2012-01-01 10:00:00.000     1

...と...

Start                       RN
2012-01-01 18:00:00.000     2

...一緒に、結果として:

Finish                      Start
2012-01-01 10:00:00.000     2012-01-01 18:00:00.000

T1(アクティブな間隔は、からの行をからの行と並べて配置しT2、を使用JOIN ... ON T1.RN = T2.RNして元に戻すことで再構築できますWHERE。)


例:

これはもう少し現実的な例です。次のテストデータ:

Device      Event      Start                      Finish
Device 1    Event A    2012-01-01 08:00:00.000    2012-01-01 10:00:00.000
Device 2    Event B    2012-01-01 18:00:00.000    2012-01-01 20:00:00.000
Device 3    Event C    2012-01-02 11:00:00.000    2012-01-02 15:00:00.000
Device 4    Event D    2012-01-02 10:00:00.000    2012-01-02 12:00:00.000
Device 5    Event E    2012-01-02 10:00:00.000    2012-01-02 15:00:00.000
Device 6    Event F    2012-01-03 09:00:00.000    2012-01-03 10:00:00.000

次の結果が得られます。

Finish                      Start
2012-01-01 10:00:00.000     2012-01-01 18:00:00.000
2012-01-01 20:00:00.000     2012-01-02 10:00:00.000
2012-01-02 15:00:00.000     2012-01-03 09:00:00.000
于 2012-03-07T18:02:06.197 に答える
5

最初の回答-ただし、OPによって追加の制約が追加された最後の回答については、以下を参照してください。

-最新のendTimeの後に次のstartTimeを取得し、重複を回避したい場合は、次のようにします。

select
    distinct
    e1.deviceId,
    e1.EventEnd,
    e3.EventStart
from Events e1 
join Events e3 on e1.eventEnd < e3.eventStart     /* Finds the next start Time */
and e3.eventStart = (select min(eventStart) from Events e5
                     where e5.eventStart > e1.eventEnd)
and not exists (select *                          /* Eliminates an e1 rows if it is overlapped */
                from Events e5 
                where e5.eventStart < e1.eventEnd
                    and e5.eventEnd > e1.eventEnd)

3行の場合:

INSERT INTO Events VALUES (1, '01/01/2012 08:00', '01/01/2012 10:00')
INSERT INTO Events VALUES (2, '01/01/2012 18:00', '01/01/2012 20:00')
insert into Events values (2, '01/01/2012 09:00', '01/01/2012 11:00')

これにより、1つの結果が得られます。

January, 01 2012 11:00:00-0800  January, 01 2012 18:00:00-0800

ただし、おそらくDeviceIdでも一致させたいと思います。その場合、結合で、追加e1.DeviceId = e3.DeviceIdし、e1.deviceId = e5.deviceId

SQLフィドルはこちら: http ://sqlfiddle.com/#!3/3899c/8

-

OK、最終編集。これは、deviceIdsを追加し、同時に終了するイベントを説明するために個別に追加するクエリです。

SELECT distinct
    e1.DeviceID,
    e1.EventEnd as LastEndTime,
    e3.EventStart as NextStartTime
FROM Events e1 
join Events e3 on e1.eventEnd < e3.eventStart
     and e3.deviceId = e1.deviceId
     and e3.eventStart = (select min(eventStart) from Events e5
                     where e5.eventStart > e1.eventEnd
                    and e5.deviceId = e3.deviceId)
where not exists (select * from Events e7 
                    where e7.eventStart < e1.eventEnd
                      and e7.eventEnd > e1.eventEnd
                      and e7.deviceId = e1.deviceId)
order by e1.deviceId, e1.eventEnd

e3への参加は次のスタートを見つけます。e5に参加すると、これが現在の終了時刻の後の最も早い開始時刻であることが保証されます。考慮される行の終了時刻が別の行と重複している場合、e7への結合によって行が削除されます。

このデータの場合:

INSERT INTO Events VALUES (1, '01/01/2012 08:00', '01/01/2012 10:00')
INSERT INTO Events VALUES (2, '01/01/2012 18:00', '01/01/2012 20:00')
insert into Events values (2, '01/01/2012 09:00', '01/01/2012 11:00')
insert into Events values (2, '01/02/2012 11:00', '01/02/2012 15:00')
insert into Events values (1, '01/02/2012 10:00', '01/02/2012 12:00')
insert into Events values (2, '01/02/2012 10:00', '01/02/2012 15:00')
insert into Events values (2, '01/03/2012 09:00', '01/03/2012 10:00')

次の結果が得られます。

1   January, 01 2012 10:00:00-0800  January, 02 2012 10:00:00-0800
2   January, 01 2012 11:00:00-0800  January, 01 2012 18:00:00-0800
2   January, 01 2012 20:00:00-0800  January, 02 2012 10:00:00-0800
2   January, 02 2012 15:00:00-0800  January, 03 2012 09:00:00-0800

SQLフィドルはこちら: http ://sqlfiddle.com/#!3/db0fa/3

于 2012-03-07T16:41:54.673 に答える
3

これはそれほど単純ではないと思いますが、現在のシナリオの理解に基づいたクエリを次に示します。

DECLARE @Events TABLE (
    DeviceID INT,
    EventStart DATETIME,
    EventEnd DATETIME
)

INSERT INTO @Events VALUES (1, '01/01/2012 08:00', '01/01/2012 10:00')
INSERT INTO @Events VALUES (2, '01/01/2012 18:00', '01/01/2012 20:00')

SELECT
    e1.DeviceID,
    e1.EventEnd,
    e2.EventStart
FROM 
    @Events e1 
    JOIN @Events e2 
        ON e2.EventStart = (
            SELECT MIN(EventStart)
            FROM @Events
            WHERE EventStart > e1.EventEnd
        )
于 2012-03-07T15:49:19.917 に答える
2

これで問題は解決しますか?

2番目のものはより適切なようです

'テーブルがあり、2つの列がDateFromとDateToです。両方の列に日付と時刻の値が含まれています。欠落している日付範囲、つまり、テーブルのどのエントリでもカバーされていないすべての日付範囲をどのように見つけますか?

于 2012-03-07T15:42:29.447 に答える
2

これが私が今行ったPostgresソリューションであり、ストアドプロシージャは含まれていません。

SELECT minute, sum(case when dp.id is null then 0 else 1 end) as s 
FROM generate_series( 
   '2017-12-28'::timestamp,
   '2017-12-30'::timestamp,
   '1 minute'::interval
) minute 
left outer join device_periods as dp
on minute >= dp.start_date and minute < dp.end_date 
group by minute order by minute

generate_series関数は、日付範囲の1分ごとに1つの行を持つテーブルを生成します。より正確には、間隔を1秒に変更できます。これはpostgres固有の機能ですが、おそらく他のエンジンにも同様の機能があります。

このクエリは、埋められたすべての分と空白のすべてを提供します。このクエリを外部クエリでラップすることができます。外部クエリは、時間、日ごとにグループ化するか、いくつかのウィンドウ関数操作を実行して、必要に応じて正確な出力を取得できます。私の目的では、空白があるかどうかだけを数える必要がありました。

于 2018-01-04T11:19:08.150 に答える