demo:db <> fiddle (重複するA-Bパーツを持つ古いデータセットを使用します)
免責事項: これは、タイムスタンプではなく日間隔で機能します。 tsの要件は後で来ました。
SELECT
s.acts,
s.sum,
MIN(a.start) as start,
MAX(a.end) as end
FROM (
SELECT DISTINCT ON (acts)
array_agg(name) as acts,
SUM(count)
FROM
activities, generate_series(start, "end", interval '1 day') gs
GROUP BY gs
HAVING cardinality(array_agg(name)) > 1
) s
JOIN activities a
ON a.name = ANY(s.acts)
GROUP BY s.acts, s.sum
-
generate_series
開始から終了までのすべての日付を生成します。したがって、アクティビティが存在するすべての日付は、特定のcount
を持つ1つの行を取得します - すべての日付をグループ化し、既存のすべてのアクティビティとそのカウントの合計を集計します
-
HAVING
アクティビティが1つしかない日付を除外します - 同じアクティビティで日が異なるため、必要な代理人は1人だけです。
DISTINCT ON
ですべての重複をフィルタリングします - この結果を元のテーブルと結合して、開始と終了を取得します。 (「end」はPostgresの予約語であることに注意してください。別の列名を見つけた方がよいでしょう!)。以前はそれらを失う方が快適でしたが、サブクエリ内でこれらのデータを取得することは可能でした。
- この参加をグループ化して、各間隔の最も早い日付と最も遅い日付を取得します。
タイムスタンプのバージョンは次のとおりです:
WITH timeslots AS (
SELECT * FROM (
SELECT
tsrange(timepoint, lead(timepoint) OVER (ORDER BY timepoint)),
lead(timepoint) OVER (ORDER BY timepoint) -- 2
FROM (
SELECT
unnest(ARRAY[start, "end"]) as timepoint -- 1
FROM
activities
ORDER BY timepoint
) s
)s WHERE lead IS NOT NULL -- 3
)
SELECT
GREATEST(MAX(start), lower(tsrange)), -- 6
LEAST(MIN("end"), upper(tsrange)),
array_agg(name), -- 5
sum(count)
FROM
timeslots t
JOIN activities a
ON t.tsrange && tsrange(a.start, a.end) -- 4
GROUP BY tsrange
HAVING cardinality(array_agg(name)) > 1
主なアイデアは、可能なタイムスロットを特定することです。だから私はすべての既知の時間(開始と終了の両方)を取り、それらをソートされたリストに入れます。したがって、最初の2つの既知の時間(開始Aから17:00と開始Bから18:00)を取得して、どの間隔が含まれているかを確認できます。次に、2番目と3番目、次に3番目と4番目というようにチェックします。
最初のタイムスロットでは、Aのみが適合します。 18-19の2番目にもBがフィッティングしています。次のスロット19-20でもC、20から20:30までAはもう適合せず、BとCのみです。次のスロットは20:30-22で、Bのみが適合し、最後に22-23Dが追加されます。 Bと最後になりますが、Dだけが23-23:30に適合します。
そこで、このタイムリストを取得して、間隔が交差するアクティビティテーブルに再度参加します。その後、タイムスロットごとにグループ化して、カウントを合計します。
- これにより、行の両方のtsが1つの配列に配置され、その要素は
unnest
を使用して要素ごとに1つの行に展開されます。 。だから私はいつも1つの列に入れて簡単に注文することができます - リード
ウィンドウ関数 を使用する 次の行の値を現在の行に取り込むことができます。したがって、 tsrange
を使用して、これらの両方の値からタイムスタンプ範囲を作成できます。 - 最後の行には「次の値」がないため、このフィルターが必要です。これにより、
NULL
が作成されますtsrange
によって解釈される値 無限大として。したがって、これは信じられないほど間違ったタイムスロットを作成します。したがって、この行を除外する必要があります。 - 元のテーブルに対してタイムスロットを結合します。
&&
演算子は、2つの範囲タイプが重複しているかどうかを確認します。 - 単一のタイムスロットでグループ化し、名前と数を集計します。
HAVING
を使用して、アクティビティが1つしかないタイムスロットを除外します 条項 - 正しい開始点と終了点を取得するには少し注意が必要です。したがって、開始点は、アクティビティ開始の最大値またはタイムスロットの開始(
lower
を使用して取得できます)のいずれかです。 )。例えば。 20-20:30スロットを取ります:それは20時間始まりますが、BもCもそこに出発点がありません。終了時間も同様です。