SQL
SQL proficiency — JOINs, window functions, CTEs, recursive queries, query optimization สำหรับ financial data
What I Can Do
- เขียน complex queries: JOINs, subqueries, window functions, CTEs, recursive queries
- Optimize slow queries ด้วย EXPLAIN ANALYZE และ indexing
- เขียน reporting queries สำหรับ trading analytics
- แก้ปัญหา N+1 queries จาก ORM
- เขียน data migration scripts ที่ไม่ lock production
Commands I Use Daily
-- LEFT JOIN เพื่อดู users ที่ยังไม่เคย trade
SELECT u.email, COUNT(t.id) AS trade_count
FROM users u
LEFT JOIN trades t ON t.user_id = u.id
GROUP BY u.email
HAVING COUNT(t.id) = 0;
-- Window function — ranking, running total
SELECT
user_id,
executed_at,
amount * price AS volume,
ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY executed_at DESC) AS trade_rank,
SUM(amount * price) OVER (
PARTITION BY user_id
ORDER BY executed_at
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
) AS cumulative_volume
FROM trades;
-- CTE สำหรับ break down complex logic
WITH active_users AS (
SELECT DISTINCT user_id
FROM trades
WHERE executed_at >= NOW() - INTERVAL '30 days'
),
user_balances AS (
SELECT user_id, SUM(balance) AS total_balance
FROM wallets
GROUP BY user_id
)
SELECT au.user_id, ub.total_balance
FROM active_users au
JOIN user_balances ub ON ub.user_id = au.user_id;
-- Recursive CTE — ใช้กับ hierarchical data เช่น referral chain
WITH RECURSIVE referral_chain AS (
SELECT id, email, referred_by, 0 AS depth
FROM users
WHERE id = 'root-user-id'
UNION ALL
SELECT u.id, u.email, u.referred_by, rc.depth + 1
FROM users u
JOIN referral_chain rc ON u.referred_by = rc.id
WHERE rc.depth < 5 -- limit depth
)
SELECT * FROM referral_chain;SELECT / INSERT / UPDATE / DELETE
พื้นฐานของ SQL — 4 operations หลักสำหรับจัดการข้อมูล
SELECT— อ่านข้อมูลจาก tableINSERT— เพิ่ม row ใหม่UPDATE— แก้ไขข้อมูลที่มีอยู่DELETE— ลบ row
WHERE
Filter rows ตามเงื่อนไข — รองรับ =, !=, >, <, >=, <=, BETWEEN, IN, LIKE, IS NULL, AND, OR, NOT
ORDER BY
เรียงลำดับ results — ASC (default) หรือ DESC สามารถ sort หลาย columns ได้ เช่น ORDER BY created_at DESC, email ASC
GROUP BY
จัดกลุ่ม rows ที่มี value เหมือนกัน เพื่อใช้คู่กับ aggregate functions — ทุก column ใน SELECT ที่ไม่ได้อยู่ใน aggregate ต้องอยู่ใน GROUP BY
HAVING
Filter กลุ่มหลัง GROUP BY — ต่างจาก WHERE ที่ filter ก่อน grouping เช่น HAVING COUNT(*) > 5
Aggregate Functions
COUNT(*)— นับจำนวน rowsSUM(amount)— รวมค่าAVG(price)— ค่าเฉลี่ยMIN()/MAX()— ค่าน้อยสุด/มากสุด
Aliases
ตั้งชื่อย่อให้ column หรือ table — SELECT u.email, COUNT(*) AS trade_count FROM users u ทำให้ query อ่านง่ายขึ้น
DISTINCT
กรอง duplicate rows ออก — SELECT DISTINCT symbol FROM trades ได้ unique symbols ทั้งหมด
JOINs (INNER, LEFT, RIGHT, FULL)
INNER JOIN— เฉพาะ rows ที่ match ทั้ง 2 tablesLEFT JOIN— ทุก rows จาก left + matched จาก right (NULL ถ้าไม่ match)RIGHT JOIN— ทุก rows จาก right + matched จาก leftFULL OUTER JOIN— ทุก rows จากทั้ง 2 tables
Subqueries
Query ซ้อนใน query อื่น — ใช้ใน WHERE (WHERE id IN (SELECT ...)), FROM (FROM (SELECT ...) sub), หรือ SELECT clause
CTEs (WITH)
Common Table Expression — ตั้งชื่อให้ subquery เพื่อให้อ่านง่ายและ reuse ได้ ใช้ WITH cte_name AS (SELECT ...) ก่อน main query
UNION / INTERSECT / EXCEPT
UNION— รวม results จาก 2 queries (ลบ duplicates),UNION ALLเก็บ duplicatesINTERSECT— เฉพาะ rows ที่อยู่ใน ทั้ง 2 queriesEXCEPT— rows ที่อยู่ใน query แรก แต่ไม่อยู่ใน query ที่สอง
CASE Expressions
Conditional logic ใน SQL — เหมือน if/else
SELECT symbol,
CASE
WHEN side = 'buy' THEN amount
WHEN side = 'sell' THEN -amount
END AS signed_amount
FROM trades;Date/Time Functions
NOW(), CURRENT_DATE, DATE(timestamp), EXTRACT(YEAR FROM ts), date_trunc('month', ts), interval arithmetic (ts + INTERVAL '30 days')
String Functions
CONCAT(), LOWER() / UPPER(), TRIM(), SUBSTRING(), LENGTH(), REPLACE(), LIKE / ILIKE (case-insensitive)
Window Functions
ทำ computation ข้าม rows ที่เกี่ยวข้องกันโดยไม่ collapse rows เหมือน GROUP BY
ROW_NUMBER()— ลำดับ row ใน partitionRANK()/DENSE_RANK()— ranking ที่ handle tiesLAG(col, n)/LEAD(col, n)— ดูค่าของ row ก่อน/หลัง- Running totals:
SUM(amount) OVER (ORDER BY date ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
Recursive CTEs
WITH RECURSIVE — query ที่ reference ตัวเองเพื่อ traverse hierarchical/graph data เช่น org chart, referral chains, category trees
EXPLAIN ANALYZE
ดู actual execution plan ของ query — เวลาจริง, จำนวน rows จริง vs estimated, buffer usage ใช้ identify ว่า query ช้าตรงไหนและทำไม
Query Plan Reading
อ่าน execution plan เพื่อ optimize — ดู node types (Seq Scan, Index Scan, Hash Join, Nested Loop), cost estimates, row counts ที่ผิดพลาด, memory usage
Index-aware Query Writing
เขียน query ให้ใช้ index ได้ — หลีกเลี่ยง function บน indexed column (WHERE LOWER(email) ไม่ใช้ index), ใช้ leading column ของ composite index, หลีกเลี่ยง OR ที่ทำให้ optimizer เลือก Seq Scan
Set-returning Functions
Functions ที่ return หลาย rows — generate_series() สำหรับสร้าง date ranges, unnest() สำหรับ expand arrays เป็น rows, json_each() สำหรับ expand JSON
Lateral Joins
LATERAL ทำให้ subquery reference columns จาก preceding tables — ใช้สำหรับ "top-N per group" pattern หรือ call set-returning function กับแต่ละ row
SELECT u.email, t.volume
FROM users u
CROSS JOIN LATERAL (
SELECT SUM(amount * price) AS volume
FROM trades
WHERE user_id = u.id
ORDER BY executed_at DESC
LIMIT 10
) t;Related Skills
- Database Design — schema design ที่อยู่เบื้องหลัง queries
- PostgreSQL — database engine ที่ใช้ run SQL queries
- Go — backend ที่ interact กับ database ผ่าน SQL