Skip to content
← All Skills
📊

SQL

Data & MessagingOfficial Site ›

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

sql
-- 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 — อ่านข้อมูลจาก table
  • INSERT — เพิ่ม 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(*) — นับจำนวน rows
  • SUM(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 tables
  • LEFT JOIN — ทุก rows จาก left + matched จาก right (NULL ถ้าไม่ match)
  • RIGHT JOIN — ทุก rows จาก right + matched จาก left
  • FULL 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 เก็บ duplicates
  • INTERSECT — เฉพาะ rows ที่อยู่ใน ทั้ง 2 queries
  • EXCEPT — rows ที่อยู่ใน query แรก แต่ไม่อยู่ใน query ที่สอง

CASE Expressions

Conditional logic ใน SQL — เหมือน if/else

sql
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 ใน partition
  • RANK() / DENSE_RANK() — ranking ที่ handle ties
  • LAG(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

sql
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