Harga
Percuma untuk mula. RM 25/bulan untuk seluruh bank.
Cancel bila-bila masa — kebanyakan orang subscribe untuk satu hiring cycle, bukan selamanya.
Free
RM 0
- ●46 soalan SQL percuma merentasi setiap tahap kesukaran
- ●Semua 29 bab lesson SQL, dengan playground yang boleh dijalankan
- ●Keseluruhan track Excel — 15 bab dan graded practice
- ●Practice dan grading tanpa had — tiada apa yang bermasa
- ●AI coach, 3 sesi/hari — baca query sebenar anda dan bagitahu apa yang salah, tak pernah bagi jawapan
Sprint
RM 49
Bayaran sekali sahaja, akses Basic penuh selama 90 hari — tiada subscription, tiada apa untuk cancel
Bulanan
RM 25RM 20/mo
kadar founding, digunakan automatik semasa checkout, terkunci selagi anda kekal subscribe — tiada kod untuk ditaip
Cancel bila-bila masa dari akaun anda — tiada e-mel, tiada panggilan telefon.
Semua yang ada dalam Basic
- Track Data Analyst, dari awal sampai habis — Basic buka semua level yang locked, bukan setakat free preview
- ●AI coach pada 30 sesi/hari — selesaikan sebanyak mungkin query yang anda stuck dalam satu sesi tanpa terhad
- ●Free bagi jawapan. Basic ajar sebab — dan tanya soalan kedua, yang interview sebenar tanya seterusnya.
- ●Dashboard dan review queue yang makin berguna semakin lama anda guna laman ini — kategori terlemah, apa nak practice seterusnya, apa yang anda salah dan tak pernah balik semula
- ●Keseluruhan bank soalan terbuka, plus take-home round — setiap soalan SEA dan global-MNC, setiap pek yang locked, solution dan grading termasuk
- ●Data Lab (beta) — query performance pada warehouse ~300,000 baris, tiada practice site lain pergi lebih dari beberapa baris. Masih dalam pembinaan dan akan jadi Pro, termasuk percuma dengan Basic sehingga siap dibina — akses tamat bila beta tamat
- ●Soalan dan lesson baharu setiap minggu, free dan paid sama — subscribe sekali dan setiap release akan datang sudah termasuk
Ini maksud "ajar sebab"
Satu walkthrough penuh, tak dipendekkan — bukan preview.
Setiap soalan Basic datang dengan walkthrough berbentuk begini: cubaan pertama yang salah bentuk, sebab ia salah, dan reasoning yang bawa dari situ ke jawapan sebenar.
Grab · aggregation
Completed rides by city
Grab's operations team wants to know how many completed rides happened in each city.
From the rides table, return the city and the number of completed rides (status = 'completed') as completed_rides, for every city that has at least one completed ride.
Step 1: Filter before you count
SELECT id, city, status FROM rides WHERE status = 'completed' ORDER BY city, id;| id | city | status |
|---|---|---|
| 6 | Johor Bahru | completed |
| 7 | Johor Bahru | completed |
| 8 | Johor Bahru | completed |
| 1 | Kuala Lumpur | completed |
| 2 | Kuala Lumpur | completed |
| 4 | Penang | completed |
Nine rides in, six out. The three cancellations — including Ipoh's only ride — are gone before anything is counted. Ipoh dropping out entirely is not an accident to fix later; it's the correct answer to "every city that has at least one completed ride."
Step 2: Group the survivors and count
SELECT city, COUNT(*) AS completed_rides
FROM rides
WHERE status = 'completed'
GROUP BY city;| city | completed_rides |
|---|---|
| Johor Bahru | 3 |
| Kuala Lumpur | 2 |
| Penang | 1 |
GROUP BY city folds the six rows into three buckets; COUNT(*) runs once inside each bucket.
Step 3: Why WHERE and not HAVING
Both filter, but they run at different times. WHERE runs before grouping, on individual rows. HAVING runs after, on the groups — so it can only see things that survived aggregation.
A row-level condition like status = 'completed' belongs in WHERE. A group-level one like "only cities with 3 or more completed rides" belongs in HAVING, because the count doesn't exist until the groups do. See Query execution order and HAVING.
Step 4: The two wrong answers
Dropping the filter. No error, four rows, and every single number is wrong:
SELECT city, COUNT(*) AS completed_rides FROM rides GROUP BY city;| city | completed_rides |
|---|---|
| Ipoh | 1 |
| Johor Bahru | 3 |
| Kuala Lumpur | 3 |
| Penang | 2 |
Johor Bahru still reads 3, which is right by coincidence — all three of its rides completed. That coincidence is what lets this mistake survive a glance.
Moving the filter inside the aggregate. Postgres supports FILTER, and it counts correctly, but it changes which cities appear:
SELECT city, COUNT(*) FILTER (WHERE status = 'completed') AS completed_rides
FROM rides
GROUP BY city;| city | completed_rides |
|---|---|
| Ipoh | 0 |
| Johor Bahru | 3 |
| Kuala Lumpur | 2 |
| Penang | 1 |
The three real counts match — but Ipoh is back with a zero, because grouping happened before the filter and Ipoh's cancelled ride was still there to make a group. That's the whole difference between the two clauses: WHERE decides which rows exist, FILTER only decides what gets counted. Neither is wrong in general; here the prompt asks for cities with at least one completed ride, so WHERE is the one that answers the question.
Siapa di sebalik ini
mahir_data dibina dan dijalankan oleh seorang engineer, secara terbuka, untuk pasaran hiring SEA yang jadi tema — senario gaya Grab, Shopee, Maybank, bukan table textbook generik. Soalan dan lesson baharu ship setiap minggu.
FAQ
- Apa jadi bila saya cancel?
- Plan anda kembali ke free pada akhir billing period semasa — anda kekal ada akses kepada semua yang Basic buka sehingga itu, dan semua yang free selepas itu.
- Perlu install apa-apa tak?
- Tak. Setiap soalan jalan terhadap Postgres engine sebenar (PGlite) dalam browser anda — tak perlu sign up untuk cuba, tiada apa dihantar ke server untuk grade satu query.
- SQL saya dihantar ke server?
- Hanya jika anda minta feedback dari AI coach. Menjalankan dan grading query berlaku sepenuhnya client-side; teks query anda tak pernah tinggalkan browser melainkan anda klik "Ask the coach."
- Anda terus tambah soalan?
- Ya — soalan dan lesson baharu ship pada kadar mingguan, free dan paid sama, dan apa-apa yang ditambah selepas anda subscribe termasuk tanpa kos tambahan.