Add explanation: hourly summary data is REPLACED not ADDED (upsert behavior)
This commit is contained in:
186
UPSERT_BEHAVIOR.md
Normal file
186
UPSERT_BEHAVIOR.md
Normal file
@@ -0,0 +1,186 @@
|
||||
# Penjelasan Behavior Upsert di Hourly Summary
|
||||
|
||||
## Jawaban Singkat
|
||||
|
||||
**Data DIGANTI (REPLACE), bukan ditambah.**
|
||||
|
||||
## Detail Penjelasan
|
||||
|
||||
### Query Upsert yang Digunakan
|
||||
|
||||
```sql
|
||||
INSERT INTO hourly_summary
|
||||
(summary_date, summary_hour, location_code, gate_code, category, total_count, total_amount)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
total_count = VALUES(total_count),
|
||||
total_amount = VALUES(total_amount)
|
||||
```
|
||||
|
||||
### Primary Key
|
||||
|
||||
```sql
|
||||
PRIMARY KEY (summary_date, summary_hour, location_code, gate_code, category)
|
||||
```
|
||||
|
||||
### Behavior: ON DUPLICATE KEY UPDATE
|
||||
|
||||
**Jika row BELUM ADA:**
|
||||
- ✅ **INSERT** → Tambah row baru
|
||||
|
||||
**Jika row SUDAH ADA:**
|
||||
- ✅ **UPDATE** → Ganti `total_count` dan `total_amount` dengan nilai baru
|
||||
- ❌ **BUKAN** tambah ke nilai lama
|
||||
|
||||
## Contoh Skenario
|
||||
|
||||
### Skenario 1: Cron Pertama Kali (Row Belum Ada)
|
||||
|
||||
**Jam 2:00, cron jalan untuk update jam 1:00:**
|
||||
|
||||
```
|
||||
1. Query entry_events untuk jam 1:00:
|
||||
- location_code: kerkof_01
|
||||
- gate_code: gate01
|
||||
- category: motor
|
||||
- COUNT: 10 events
|
||||
- total_amount: 10 × 2000 = 20000
|
||||
|
||||
2. INSERT ke hourly_summary:
|
||||
✅ Row baru dibuat:
|
||||
summary_date: 2025-12-17
|
||||
summary_hour: 1
|
||||
location_code: kerkof_01
|
||||
gate_code: gate01
|
||||
category: motor
|
||||
total_count: 10
|
||||
total_amount: 20000
|
||||
```
|
||||
|
||||
### Skenario 2: Cron Kedua Kali (Row Sudah Ada)
|
||||
|
||||
**Jam 2:30, ada event baru masuk (terlambat):**
|
||||
- Event jam 1:00 masuk di jam 2:30 (terlambat)
|
||||
|
||||
**Jam 3:00, cron jalan lagi untuk update jam 1:00:**
|
||||
|
||||
```
|
||||
1. Query entry_events untuk jam 1:00 (SEKARANG):
|
||||
- location_code: kerkof_01
|
||||
- gate_code: gate01
|
||||
- category: motor
|
||||
- COUNT: 11 events (termasuk yang terlambat)
|
||||
- total_amount: 11 × 2000 = 22000
|
||||
|
||||
2. ON DUPLICATE KEY UPDATE:
|
||||
✅ Row LAMA diganti:
|
||||
- total_count: 10 → 11 (DIGANTI, bukan 10 + 11 = 21)
|
||||
- total_amount: 20000 → 22000 (DIGANTI, bukan 20000 + 22000 = 42000)
|
||||
```
|
||||
|
||||
## Mengapa Diganti, Bukan Ditambah?
|
||||
|
||||
### 1. **Akurasi Data**
|
||||
- Summary harus selalu mencerminkan data aktual di `entry_events`
|
||||
- Jika ditambah, data akan double-count jika cron jalan berulang
|
||||
|
||||
### 2. **Idempotent**
|
||||
- Cron bisa jalan berulang tanpa merusak data
|
||||
- Hasil selalu sama, tidak peduli berapa kali dijalankan
|
||||
|
||||
### 3. **Event Terlambat**
|
||||
- Jika ada event yang masuk terlambat, akan otomatis terhitung saat rekap ulang
|
||||
- Tidak perlu manual correction
|
||||
|
||||
## Contoh Timeline Lengkap
|
||||
|
||||
### Hari: 2025-12-17
|
||||
|
||||
**Jam 1:00 - 1:59:**
|
||||
```
|
||||
Event masuk ke entry_events:
|
||||
- 1:05 → motor (kerkof_01, gate01)
|
||||
- 1:15 → motor (kerkof_01, gate01)
|
||||
- 1:30 → motor (kerkof_01, gate01)
|
||||
Total: 3 events
|
||||
```
|
||||
|
||||
**Jam 2:00 - Cron jalan:**
|
||||
```
|
||||
1. Query entry_events untuk jam 1:00:
|
||||
→ COUNT: 3 events
|
||||
→ total_amount: 3 × 2000 = 6000
|
||||
|
||||
2. INSERT ke hourly_summary:
|
||||
✅ Row baru: total_count = 3, total_amount = 6000
|
||||
```
|
||||
|
||||
**Jam 2:30 - Event terlambat masuk:**
|
||||
```
|
||||
Event masuk ke entry_events (terlambat):
|
||||
- 2:30 → motor (kerkof_01, gate01) dengan event_time = 1:45
|
||||
Total sekarang: 4 events untuk jam 1:00
|
||||
```
|
||||
|
||||
**Jam 3:00 - Cron jalan lagi (opsional, jika rekap ulang):**
|
||||
```
|
||||
1. Query entry_events untuk jam 1:00 (SEKARANG):
|
||||
→ COUNT: 4 events (termasuk yang terlambat)
|
||||
→ total_amount: 4 × 2000 = 8000
|
||||
|
||||
2. ON DUPLICATE KEY UPDATE:
|
||||
✅ Row LAMA diganti:
|
||||
- total_count: 3 → 4 (DIGANTI)
|
||||
- total_amount: 6000 → 8000 (DIGANTI)
|
||||
```
|
||||
|
||||
## Verifikasi di Database
|
||||
|
||||
### Cek Data Sebelum Cron:
|
||||
|
||||
```sql
|
||||
SELECT * FROM hourly_summary
|
||||
WHERE summary_date = '2025-12-17'
|
||||
AND summary_hour = 1
|
||||
AND location_code = 'kerkof_01'
|
||||
AND gate_code = 'gate01'
|
||||
AND category = 'motor';
|
||||
|
||||
-- Hasil:
|
||||
-- total_count: 3
|
||||
-- total_amount: 6000
|
||||
```
|
||||
|
||||
### Jalankan Cron:
|
||||
|
||||
```bash
|
||||
php bin/hourly_summary.php today 1
|
||||
```
|
||||
|
||||
### Cek Data Setelah Cron:
|
||||
|
||||
```sql
|
||||
SELECT * FROM hourly_summary
|
||||
WHERE summary_date = '2025-12-17'
|
||||
AND summary_hour = 1
|
||||
AND location_code = 'kerkof_01'
|
||||
AND gate_code = 'gate01'
|
||||
AND category = 'motor';
|
||||
|
||||
-- Hasil (jika ada event baru):
|
||||
-- total_count: 4 (DIGANTI dari 3, bukan 3 + 4 = 7)
|
||||
-- total_amount: 8000 (DIGANTI dari 6000, bukan 6000 + 8000 = 14000)
|
||||
```
|
||||
|
||||
## Kesimpulan
|
||||
|
||||
| Aspek | Behavior |
|
||||
|-------|----------|
|
||||
| **Jika row belum ada** | ✅ INSERT (tambah baru) |
|
||||
| **Jika row sudah ada** | ✅ UPDATE (ganti nilai) |
|
||||
| **Apakah ditambah?** | ❌ TIDAK, diganti |
|
||||
| **Apakah diganti?** | ✅ YA, diganti dengan nilai baru |
|
||||
| **Nilai lama** | ❌ Tidak dipertahankan, diganti dengan hasil rekap ulang |
|
||||
|
||||
**Intinya:** Data selalu di-rekap ulang dari `entry_events`, bukan ditambah ke nilai lama. Ini memastikan akurasi dan konsistensi data summary.
|
||||
|
||||
Reference in New Issue
Block a user