karawaci.kode

← Semua snippet

SQL Lanjut Database

Gap and Island detection di SQL (consecutive sequences)

Cari runtutan konsekutif dalam data — streak hari user aktif, range tanggal kerja tanpa libur, periode tanpa gangguan. Pattern: gap-and-island.

Dipublikasikan 22 Mei 2026

Pertanyaan analytics yang common: “berapa hari user aktif berturut-turut?”, “kapan periode terpanjang tanpa downtime?”, “berapa range tanggal yang berturut-turut ada data?” Jawabannya: gap-and-island pattern.

Setup data

CREATE TABLE user_login (
  user_id INT,
  login_date DATE,
  PRIMARY KEY (user_id, login_date)
);

INSERT INTO user_login VALUES
  (1, '2026-05-01'), (1, '2026-05-02'), (1, '2026-05-03'),  -- streak 1: 3 days
  (1, '2026-05-08'), (1, '2026-05-09'),                       -- streak 2: 2 days
  (1, '2026-05-15'),                                          -- streak 3: 1 day
  (1, '2026-05-20'), (1, '2026-05-21'), (1, '2026-05-22'), (1, '2026-05-23'); -- streak 4: 4 days

Query: temukan semua “islands” (consecutive streaks)

WITH ranked AS (
  SELECT
    user_id,
    login_date,
    ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY login_date) AS rn,
    login_date - (ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY login_date) || ' days')::INTERVAL AS grp
  FROM user_login
),
streaks AS (
  SELECT
    user_id,
    grp,
    MIN(login_date) AS streak_start,
    MAX(login_date) AS streak_end,
    COUNT(*) AS streak_length
  FROM ranked
  GROUP BY user_id, grp
)
SELECT
  user_id,
  streak_start,
  streak_end,
  streak_length
FROM streaks
WHERE user_id = 1
ORDER BY streak_start;

Output

 user_id | streak_start | streak_end | streak_length
---------+--------------+------------+---------------
       1 | 2026-05-01   | 2026-05-03 |             3
       1 | 2026-05-08   | 2026-05-09 |             2
       1 | 2026-05-15   | 2026-05-15 |             1
       1 | 2026-05-20   | 2026-05-23 |             4

Cara kerja

Trick: kalau tanggal konsekutif, tanggal - rownumber menghasilkan nilai konstan untuk semua row dalam satu streak. Group by hasil itu = group by streak.

login_daterntanggal - rn
2026-05-0112026-04-30
2026-05-0222026-04-30
2026-05-0332026-04-30
2026-05-0842026-05-04
2026-05-0952026-05-04
2026-05-1562026-05-09
2026-05-2072026-05-13
2026-05-2182026-05-13
2026-05-2292026-05-13
2026-05-23102026-05-13

Use cases real

Streak harian user aktif

Sudah di atas.

Periode tanpa downtime

-- monitoring_status: hari yang status='UP'
WITH ranked AS (
  SELECT
    ts::DATE AS day,
    ROW_NUMBER() OVER (ORDER BY ts::DATE) AS rn
  FROM monitoring_status
  WHERE status = 'UP'
),
periods AS (
  SELECT
    MIN(day) AS uptime_start,
    MAX(day) AS uptime_end,
    COUNT(*) AS uptime_days
  FROM ranked
  GROUP BY day - (rn || ' days')::INTERVAL
)
SELECT * FROM periods ORDER BY uptime_days DESC LIMIT 5;

Top 5 periode uptime terpanjang.

Periode kerja tanpa libur

-- attendance: catatan kehadiran
WITH ranked AS (
  SELECT
    employee_id,
    date,
    ROW_NUMBER() OVER (PARTITION BY employee_id ORDER BY date) AS rn
  FROM attendance
)
SELECT
  employee_id,
  MIN(date) AS period_start,
  MAX(date) AS period_end,
  COUNT(*) AS consecutive_days
FROM ranked
GROUP BY employee_id, date - (rn || ' days')::INTERVAL
HAVING COUNT(*) > 60  -- yang > 60 hari berturut-turut
ORDER BY consecutive_days DESC;

Identifikasi karyawan yang kerja > 60 hari tanpa hari libur (untuk HR follow-up).

Catatan

  • Date interval works for daily granularity. Untuk hourly/minutely, ganti 'days' ke 'hours' atau 'minutes'.
  • PostgreSQL syntax di atas. MySQL 8+ juga support window function, syntax mirip.
  • Performance: untuk dataset besar (>10M row), tambahkan index di kolom partition + order. Window function tetap O(N log N).

Variasi: detect gaps (kebalikan dari islands)

WITH consecutive_pairs AS (
  SELECT
    user_id,
    login_date AS day,
    LEAD(login_date) OVER (PARTITION BY user_id ORDER BY login_date) AS next_day
  FROM user_login
)
SELECT
  user_id,
  day + INTERVAL '1 day' AS gap_start,
  next_day - INTERVAL '1 day' AS gap_end,
  (next_day - day - 1) AS gap_days
FROM consecutive_pairs
WHERE next_day - day > 1
ORDER BY gap_days DESC;

Cari periode di mana user TIDAK login (gap).

Pattern gap-and-island adalah salah satu “advanced SQL technique” yang sering muncul di whiteboard interview senior backend. Worth dikuasai.

# tags

sqlpostgreswindow-functionanalytics

Ditulis oleh Asti Larasati · 22 Mei 2026