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`
また、イベントが重複することもありますが、これは非常にまれであり、データが不良であるため、各デバイスの可用性を個別に知る必要があるため、あるデバイスのイベントが別のデバイスのイベントと重複することは問題ではありません。
間隔を含むフィールドの名前が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
このクエリの重要な特性は、重複間隔でも機能することです。
サブクエリT1
は、他の間隔以外である間隔開始のみを受け入れます。サブクエリT2
は、インターバル終了に対して同じことを行います。これが重複を取り除くものです。
DISTINCT
は、他の間隔の外側に両方ある2つの同一の間隔の開始(または終了)がある場合に重要です。 WHERE Finish < Start
は、空の間隔(つまり、期間0)を単に削除します。
また、次のステップで必要になる時間的順序に関連する行番号を添付します。
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
これで、「アクティブ」または「非アクティブ」の間隔を再構築できます。
inactive間隔は、previous間隔の終わりと次の間隔の始まりを組み合わせることによって再構築されます。したがって、- 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
最初の回答-ただし、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 Fiddle here: 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 Fiddle here: http://sqlfiddle.com/#!3/db0fa/
これはそれほど単純ではないと思いますが、シナリオについての私の現在の理解に基づいたクエリを次に示します。
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
)
これで問題は解決しますか?
2番目のものはより関連性があるようです
'2つの列がDateFromとDateToであるテーブルがあります。両方の列に日付と時刻の値が含まれています。欠落している日付範囲、つまり、テーブルのどのエントリでもカバーされていないすべての日付範囲をどのように見つけますか?.
これが私が今行った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固有の機能ですが、おそらく他のエンジンにも同様の機能が存在します。
このクエリは、入力されたすべての分と空白のすべての分を提供します。このクエリを外部クエリでラップできます。外部クエリでは、時間、日ごとにグループ化するか、ウィンドウ関数操作を実行して、必要に応じて正確な出力を取得できます。私の目的では、空白があるかどうかだけを数える必要がありました。
私はあなたの質問を本当に理解していません:
「デバイスID、EventStart、EventEndの時刻が記載されたテーブルがあります」
に続く
「これらのイベント間の時間を取得するには、クエリを実行する必要があります」
"例えば:
Device 1 Event A runs from
01/01/2012 08:00-01/01/2012 10:00 "
これを取得するには、あなたがする必要があるだけです
select Device_ID,EventStart,EventEnd from Device
各デバイスの開始日と終了日の時間差を取得する場合は、DATEDIFFを使用できます。
select Device_ID,DATEDIFF(EventEnd - EventStart) from Device