免責事項:(優れた)次の投稿に基づいて回答を書いています:
https://www.itprotoday.com/sql-server/calculating-concurrent-sessions-part-3(パート1とパート2も推奨されます)
この問題についてここで最初に理解することは、インターネットで見つかった現在のソリューションのほとんどには、基本的に2つの問題がある可能性があるということです
- 結果は正解ではありません(たとえば、範囲AがBおよびCと重複しているが、BがCと重複していない場合、3つの重複範囲としてカウントされます)。
- それを計算する方法は非常に非効率的です(O(n ^ 2)であるため、および/または期間の1秒ごとに循環するため)
Unreasonsによって提案されたようなソリューションの一般的なパフォーマンスの問題は、明確なソリューションです。呼び出しごとに、他のすべての呼び出しが重複していないかどうかを確認する必要があります。
日付順に並べられたすべての「イベント」(呼び出しの開始と終了)を一覧表示し、開始の場合は1を加算し、ハングアップの場合は1を減算する、アルゴリズムの線形共通ソリューションがあります。最大値を覚えておいてください。これはカーソルで簡単に実装できますが(Hafhorによって提案された解決策はそのように思われます)、カーソルは問題を解決するための最も効率的な方法ではありません。
参照された記事には、優れた例、異なるソリューション、それらのパフォーマンス比較が含まれています。提案された解決策は次のとおりです。
WITH C1 AS
(
SELECT starttime AS ts, +1 AS TYPE,
ROW_NUMBER() OVER(ORDER BY starttime) AS start_ordinal
FROM Calls
UNION ALL
SELECT endtime, -1, NULL
FROM Calls
),
C2 AS
(
SELECT *,
ROW_NUMBER() OVER( ORDER BY ts, TYPE) AS start_or_end_ordinal
FROM C1
)
SELECT MAX(2 * start_ordinal - start_or_end_ordinal) AS mx
FROM C2
WHERE TYPE = 1
説明
このデータセットを想定してください
+-------------------------+-------------------------+
| starttime | endtime |
+-------------------------+-------------------------+
| 2009-01-01 00:02:10.000 | 2009-01-01 00:05:24.000 |
| 2009-01-01 00:02:19.000 | 2009-01-01 00:02:35.000 |
| 2009-01-01 00:02:57.000 | 2009-01-01 00:04:04.000 |
| 2009-01-01 00:04:12.000 | 2009-01-01 00:04:52.000 |
+-------------------------+-------------------------+
これは、同じアイデアをクエリで実装する方法です。呼び出しの開始ごとに1を追加し、終了ごとに1を減算します。
SELECT starttime AS ts, +1 AS TYPE,
ROW_NUMBER() OVER(ORDER BY starttime) AS start_ordinal
FROM Calls
C1 CTEのこの部分は、各通話の開始時刻と番号を取得します
+-------------------------+------+---------------+
| ts | TYPE | start_ordinal |
+-------------------------+------+---------------+
| 2009-01-01 00:02:10.000 | 1 | 1 |
| 2009-01-01 00:02:19.000 | 1 | 2 |
| 2009-01-01 00:02:57.000 | 1 | 3 |
| 2009-01-01 00:04:12.000 | 1 | 4 |
+-------------------------+------+---------------+
今このコード
SELECT endtime, -1, NULL
FROM Calls
行番号なしですべての「終了時間」を生成します
+-------------------------+----+------+
| endtime | | |
+-------------------------+----+------+
| 2009-01-01 00:02:35.000 | -1 | NULL |
| 2009-01-01 00:04:04.000 | -1 | NULL |
| 2009-01-01 00:04:52.000 | -1 | NULL |
| 2009-01-01 00:05:24.000 | -1 | NULL |
+-------------------------+----+------+
これで、UNIONが完全なC1 CTE定義を持つようになり、両方のテーブルが混在するようになります
+-------------------------+------+---------------+
| ts | TYPE | start_ordinal |
+-------------------------+------+---------------+
| 2009-01-01 00:02:10.000 | 1 | 1 |
| 2009-01-01 00:02:19.000 | 1 | 2 |
| 2009-01-01 00:02:57.000 | 1 | 3 |
| 2009-01-01 00:04:12.000 | 1 | 4 |
| 2009-01-01 00:02:35.000 | -1 | NULL |
| 2009-01-01 00:04:04.000 | -1 | NULL |
| 2009-01-01 00:04:52.000 | -1 | NULL |
| 2009-01-01 00:05:24.000 | -1 | NULL |
+-------------------------+------+---------------+
C2は、新しい列を使用してC1の並べ替えと番号付けを計算します
C2 AS
(
SELECT *,
ROW_NUMBER() OVER( ORDER BY ts, TYPE) AS start_or_end_ordinal
FROM C1
)
+-------------------------+------+-------+--------------+
| ts | TYPE | start | start_or_end |
+-------------------------+------+-------+--------------+
| 2009-01-01 00:02:10.000 | 1 | 1 | 1 |
| 2009-01-01 00:02:19.000 | 1 | 2 | 2 |
| 2009-01-01 00:02:35.000 | -1 | NULL | 3 |
| 2009-01-01 00:02:57.000 | 1 | 3 | 4 |
| 2009-01-01 00:04:04.000 | -1 | NULL | 5 |
| 2009-01-01 00:04:12.000 | 1 | 4 | 6 |
| 2009-01-01 00:04:52.000 | -1 | NULL | 7 |
| 2009-01-01 00:05:24.000 | -1 | NULL | 8 |
+-------------------------+------+-------+--------------+
そして、魔法が発生する場所があります。いつでも#start-#endsの結果は、現時点での同時呼び出しの量です。
Type =1(開始イベント)ごとに、3番目の列に#start値があります。また、#start + #end(4列目)
#start_or_end = #start + #end
#end = (#start_or_end - #start)
#start - #end = #start - (#start_or_end - #start)
#start - #end = 2 * #start - #start_or_end
したがって、SQLの場合:
SELECT MAX(2 * start_ordinal - start_or_end_ordinal) AS mx
FROM C2
WHERE TYPE = 1
この場合、提案された一連の呼び出しを使用すると、結果は2になります。
提案された記事では、たとえばサービスまたは「電話会社」または「電話セントラル」によってグループ化された結果を得るには少し改善があり、このアイデアは、たとえばタイムスロットごとにグループ化して最大の同時実行性を持つためにも使用できます特定の日の時間ごと。