demo:db<>biola (menggunakan kumpulan data lama dengan bagian A-B yang tumpang tindih)
Penafian: Ini berfungsi untuk interval hari bukan untuk cap waktu. Persyaratan untuk ts datang kemudian.
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
menghasilkan semua tanggal antara awal dan akhir. Jadi setiap tanggal ada aktivitas mendapat satu baris dengancount
tertentu- Mengelompokkan semua tanggal, menggabungkan semua aktivitas yang ada dan jumlah penghitungannya
HAVING
menyaring tanggal di mana hanya ada satu aktivitas- Karena ada hari yang berbeda dengan aktivitas yang sama, kami hanya membutuhkan satu perwakilan:Filter semua duplikat dengan
DISTINCT ON
- Gabungkan hasil ini dengan tabel asli untuk memulai dan mengakhiri. (perhatikan bahwa "akhir" adalah kata yang dicadangkan di Postgres, Anda sebaiknya mencari nama kolom lain!). Lebih nyaman untuk kehilangan mereka sebelumnya, tetapi mungkin untuk mendapatkan data ini di dalam subkueri.
- Kelompokkan ini bergabung untuk mendapatkan tanggal paling awal dan terbaru dari setiap interval.
Berikut adalah versi untuk stempel waktu:
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
Ide utamanya adalah untuk mengidentifikasi kemungkinan slot waktu. Jadi saya mengambil setiap waktu yang diketahui (baik awal dan akhir) dan memasukkannya ke dalam daftar yang diurutkan. Jadi saya dapat mengambil waktu derek pertama yang diketahui (17:00 dari start A dan 18:00 dari start B) dan memeriksa interval mana yang ada di dalamnya. Kemudian saya memeriksanya untuk tanggal 2 dan 3, lalu untuk tanggal 3 dan 4 dan seterusnya.
Di slot waktu pertama hanya A yang cocok. Di detik 18-19 juga B pas. Di slot berikutnya 19-20 juga C, dari 20 ke 20:30 A tidak pas lagi, hanya B dan C. Berikutnya 20:30-22 di mana hanya B yang cocok, akhirnya 22-23 D ditambahkan ke B dan yang terakhir hanya D yang cocok dengan 23-23:30.
Jadi saya mengambil daftar waktu ini dan menggabungkannya dengan tabel aktivitas di mana intervalnya berpotongan. Setelah itu hanya pengelompokan berdasarkan slot waktu dan jumlahkan hitungan Anda.
- ini menempatkan kedua t baris ke dalam satu larik yang elemennya diperluas menjadi satu baris per elemen dengan
unnest
. Jadi saya mendapatkan semua waktu menjadi satu kolom yang bisa dipesan dengan mudah - menggunakan lead fungsi jendela
memungkinkan untuk mengambil nilai dari baris berikutnya ke yang sekarang. Jadi saya bisa membuat rentang waktu dari kedua nilai ini dengan
tsrange
- Filter ini diperlukan karena baris terakhir tidak memiliki "nilai berikutnya". Ini membuat
NULL
nilai yang ditafsirkan olehtsrange
sebagai tak terhingga. Jadi ini akan membuat slot waktu salah yang luar biasa. Jadi kita perlu menyaring baris ini. - Bergabunglah dengan slot waktu dengan tabel aslinya.
&&
operator memeriksa apakah dua jenis rentang tumpang tindih. - Mengelompokkan berdasarkan slot waktu tunggal, menggabungkan nama dan jumlah. Saring slot waktu hanya dengan satu aktivitas dengan menggunakan
HAVING
klausa - Agak sulit untuk mendapatkan titik awal dan akhir yang tepat. Jadi titik awal adalah maksimum awal aktivitas atau awal slot waktu (yang dapat diperoleh dengan menggunakan
lower
). Misalnya. Ambil slot 20-20:30:Ini dimulai 20 jam tetapi baik B maupun C tidak memiliki titik awal di sana. Mirip dengan waktu akhir.