sql >> データベース >  >> RDS >> PostgreSQL

postgresqlで重複するレコードのある日付範囲を検索して合計します

    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
    
    1. generate_series 開始から終了までのすべての日付を生成します。したがって、アクティビティが存在するすべての日付は、特定のcountを持つ1つの行を取得します
    2. すべての日付をグループ化し、既存のすべてのアクティビティとそのカウントの合計を集計します
    3. HAVING アクティビティが1つしかない日付を除外します
    4. 同じアクティビティで日が異なるため、必要な代理人は1人だけです。DISTINCT ONですべての重複をフィルタリングします
    5. この結果を元のテーブルと結合して、開始と終了を取得します。 (「end」はPostgresの予約語であることに注意してください。別の列名を見つけた方がよいでしょう!)。以前はそれらを失う方が快適でしたが、サブクエリ内でこれらのデータを取得することは可能でした。
    6. この参加をグループ化して、各間隔の最も早い日付と最も遅い日付を取得します。

    タイムスタンプのバージョンは次のとおりです:

    demo:db <> fiddle

    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に適合します。

    そこで、このタイムリストを取得して、間隔が交差するアクティビティテーブルに再度参加します。その後、タイムスロットごとにグループ化して、カウントを合計します。

    1. これにより、行の両方のtsが1つの配列に配置され、その要素はunnestを使用して要素ごとに1つの行に展開されます。 。だから私はいつも1つの列に入れて簡単に注文することができます
    2. リードウィンドウ関数 を使用する 次の行の値を現在の行に取り込むことができます。したがって、tsrangeを使用して、これらの両方の値からタイムスタンプ範囲を作成できます。
    3. 最後の行には「次の値」がないため、このフィルターが必要です。これにより、NULLが作成されます tsrangeによって解釈される値 無限大として。したがって、これは信じられないほど間違ったタイムスロットを作成します。したがって、この行を除外する必要があります。
    4. 元のテーブルに対してタイムスロットを結合します。 && 演算子は、2つの範囲タイプが重複しているかどうかを確認します。
    5. 単一のタイムスロットでグループ化し、名前と数を集計します。 HAVINGを使用して、アクティビティが1つしかないタイムスロットを除外します 条項
    6. 正しい開始点と終了点を取得するには少し注意が必要です。したがって、開始点は、アクティビティ開始の最大値またはタイムスロットの開始(lowerを使用して取得できます)のいずれかです。 )。例えば。 20-20:30スロットを取ります:それは20時間始まりますが、BもCもそこに出発点がありません。終了時間も同様です。


    1. テーブルスイッチの変更はSQLサーバーでどのように機能しますか?

    2. Postgresqlで同等のテーブル値パラメーター

    3. SQLServerのAlwaysOn可用性グループを監視するさまざまな方法

    4. EntityFrameworkで接続文字列を設定する方法