Chuyển tới nội dung chính

Kỹ năng SQL và Python trong xử lý dữ liệu

· 19 phút để đọc

SQL và Python

Trong thế giới dữ liệu hiện đại, SQL và Python không phải là hai lựa chọn cạnh tranh — chúng bổ sung cho nhau. SQL giúp bạn truy vấn và tổng hợp dữ liệu ngay tại nguồn, trong khi Python mở rộng khả năng phân tích, tự động hóa và xây dựng pipeline. Bài viết này là hướng dẫn toàn diện từ nền tảng đến thực hành, dành cho Data Analyst, Data Engineer, Backend Developer và bất kỳ ai muốn làm việc hiệu quả với dữ liệu.

💡 Muốn học bài bản theo lộ trình từ A đến Z (SQL Server + Python thực chiến)? Tham khảo Khóa học Phân tích dữ liệu SQL Server & Python của Hướng Nghiệp Dữ Liệu.


1. Tại sao cần cả SQL lẫn Python?

1.1. Vai trò riêng của từng công cụ

Khía cạnhSQLPython
Mục đích chínhTruy vấn, lọc, gom nhóm trên databaseXử lý logic phức tạp, ML, visualization
Hiệu năngTối ưu trên tập dữ liệu lớn (index, query plan)Linh hoạt nhưng cần tải dữ liệu về bộ nhớ
Học tậpCú pháp khai báo, dễ đọcNgôn ngữ lập trình đầy đủ, thư viện phong phú
Ứng dụngBáo cáo, dashboard, ETL tại DBPipeline, API, model, automation

SQL được thiết kế để "nói chuyện" trực tiếp với cơ sở dữ liệu quan hệ. Khi bạn cần lọc 10 triệu dòng theo điều kiện, gom nhóm theo tháng, hoặc join nhiều bảng — database engine xử lý việc đó hiệu quả hơn nhiều so với việc tải toàn bộ dữ liệu về Python rồi mới lọc.

Python phù hợp khi logic vượt quá khả năng SQL thuần túy: làm sạch dữ liệu bẩn với quy tắc phức tạp, gọi API bên ngoài, huấn luyện mô hình machine learning, vẽ biểu đồ tương tác, hoặc gửi email báo cáo tự động.

1.2. Khi nào dùng SQL, khi nào dùng Python?

Quy tắc vàng: Dùng SQL để lọc, join và aggregate ở tầng database; dùng Python cho transform phức tạp, phân tích thống kê và output cuối cùng.

  • Ưu tiên SQL khi: Dữ liệu đã nằm trong database, cần aggregation lớn, cần join nhiều bảng, hoặc muốn giảm lượng dữ liệu truyền qua mạng.
  • Ưu tiên Python khi: Cần thư viện thống kê (scipy, statsmodels), ML (scikit-learn), visualization (matplotlib, plotly), hoặc tích hợp nhiều nguồn không phải SQL.
  • Kết hợp cả hai: SELECT có chọn lọc → pandas transform → matplotlib visualize → to_sql() ghi kết quả trở lại warehouse.

1.3. Ai cần nắm bộ đôi này?

  • Data Analyst: Truy vấn SQL hàng ngày, Python cho báo cáo và phân tích sâu.
  • Data Scientist: SQL lấy feature từ warehouse, Python cho modeling.
  • Data Engineer: SQL trong transformation layer, Python cho orchestration (Airflow, Prefect).
  • Backend Developer: SQL qua ORM hoặc raw query, Python cho business logic.

Theo khảo sát thị trường việc làm tại Việt Nam và quốc tế, SQL và Python nằm trong top 3 kỹ năng được yêu cầu cho vị trí liên quan đến dữ liệu. Nắm vững cả hai giúp bạn linh hoạt hơn trong team và giảm phụ thuộc vào người khác khi cần dữ liệu.


2. SQL cơ bản — Nền tảng bắt buộc

Phần này dùng schema mẫu gồm các bảng employees, departments, orders, customers, products — mô phỏng hệ thống bán hàng phổ biến.

2.1. SELECT, WHERE và ORDER BY

-- Lấy thông tin nhân viên IT có lương trên 15 triệu
SELECT name, salary, department, hire_date
FROM employees
WHERE salary > 15000000
AND department = 'IT'
ORDER BY salary DESC;
-- Tìm kiếm theo pattern (LIKE)
SELECT customer_name, email, city
FROM customers
WHERE customer_name LIKE 'Nguyễn%'
AND city IN ('Hà Nội', 'TP.HCM', 'Đà Nẵng');
-- Giới hạn kết quả (LIMIT / OFFSET — pagination)
SELECT order_id, total_amount, order_date
FROM orders
ORDER BY order_date DESC
LIMIT 20 OFFSET 40; -- Trang 3, mỗi trang 20 dòng

2.2. Aggregation với GROUP BY và HAVING

-- Thống kê lương theo phòng ban
SELECT
department,
COUNT(*) AS total_employees,
ROUND(AVG(salary), 0) AS avg_salary,
MIN(salary) AS min_salary,
MAX(salary) AS max_salary
FROM employees
GROUP BY department
HAVING AVG(salary) > 12000000
ORDER BY avg_salary DESC;
-- Doanh thu theo tháng và danh mục sản phẩm
SELECT
DATE_TRUNC('month', o.order_date) AS month,
p.category,
SUM(oi.quantity * oi.unit_price) AS revenue,
COUNT(DISTINCT o.customer_id) AS unique_customers
FROM orders o
JOIN order_items oi ON o.order_id = oi.order_id
JOIN products p ON oi.product_id = p.product_id
WHERE o.order_date >= '2025-01-01'
GROUP BY 1, 2
ORDER BY month, revenue DESC;

Lưu ý: WHERE lọc dòng trước khi gom nhóm; HAVING lọc sau khi gom nhóm.

2.3. JOIN — Kết nối nhiều bảng

-- INNER JOIN: chỉ lấy đơn hàng có khách hàng tồn tại
SELECT
o.order_id,
c.customer_name,
o.total_amount,
o.order_date
FROM orders o
INNER JOIN customers c ON o.customer_id = c.id
WHERE o.order_date >= '2025-01-01';
-- LEFT JOIN: lấy tất cả khách hàng, kể cả chưa có đơn
SELECT
c.customer_name,
COUNT(o.order_id) AS order_count,
COALESCE(SUM(o.total_amount), 0) AS total_spent
FROM customers c
LEFT JOIN orders o ON c.id = o.customer_id
GROUP BY c.id, c.customer_name
ORDER BY total_spent DESC;
-- Self JOIN: tìm nhân viên và quản lý trực tiếp
SELECT
e.name AS employee,
m.name AS manager
FROM employees e
LEFT JOIN employees m ON e.manager_id = m.id;

2.4. Subquery và CTE (Common Table Expression)

-- Subquery trong WHERE
SELECT name, salary
FROM employees
WHERE salary > (
SELECT AVG(salary) FROM employees
);
-- CTE: dễ đọc hơn subquery lồng nhau
WITH monthly_sales AS (
SELECT
DATE_TRUNC('month', order_date) AS month,
SUM(total_amount) AS revenue
FROM orders
GROUP BY 1
),
sales_with_growth AS (
SELECT
month,
revenue,
revenue - LAG(revenue) OVER (ORDER BY month) AS growth
FROM monthly_sales
)
SELECT
month,
revenue,
growth,
ROUND(100.0 * growth / NULLIF(LAG(revenue) OVER (ORDER BY month), 0), 2) AS growth_pct
FROM sales_with_growth;

2.5. INSERT, UPDATE, DELETE

-- Thêm dữ liệu
INSERT INTO employees (name, department, salary, hire_date)
VALUES ('Trần Văn A', 'IT', 18000000, '2025-06-01');

-- Cập nhật có điều kiện
UPDATE employees
SET salary = salary * 1.1
WHERE department = 'IT' AND hire_date < '2024-01-01';

-- Xóa an toàn (nên dùng transaction)
BEGIN;
DELETE FROM order_items WHERE order_id IN (
SELECT order_id FROM orders WHERE order_date < '2020-01-01'
);
DELETE FROM orders WHERE order_date < '2020-01-01';
COMMIT;

2.6. CASE WHEN — Phân loại linh hoạt

SELECT
name,
salary,
CASE
WHEN salary >= 25000000 THEN 'Cao'
WHEN salary >= 15000000 THEN 'Trung bình'
ELSE 'Thấp'
END AS salary_band,
CASE department
WHEN 'IT' THEN 'Kỹ thuật'
WHEN 'Sales' THEN 'Kinh doanh'
ELSE 'Khác'
END AS dept_vn
FROM employees;

3. Kết nối Python với Database

3.1. sqlite3 — Cho học tập và prototype

SQLite không cần server riêng, file .db là toàn bộ database — lý tưởng để học và thử nghiệm.

import sqlite3

# Kết nối (tạo file nếu chưa có)
conn = sqlite3.connect('company.db')
cursor = conn.cursor()

# Tạo bảng
cursor.execute("""
CREATE TABLE IF NOT EXISTS employees (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
department TEXT,
salary REAL
)
""")

# Insert với parameterized query (tránh SQL injection)
employees = [
('Nguyễn Văn A', 'IT', 18000000),
('Trần Thị B', 'Sales', 14000000),
('Lê Văn C', 'IT', 22000000),
]
cursor.executemany(
"INSERT INTO employees (name, department, salary) VALUES (?, ?, ?)",
employees
)
conn.commit()

# Truy vấn
cursor.execute(
"SELECT name, salary FROM employees WHERE department = ? ORDER BY salary DESC",
('IT',)
)
rows = cursor.fetchall()
for name, salary in rows:
print(f"{name}: {salary:,.0f} VND")

conn.close()

Context manager (khuyến nghị):

import sqlite3

with sqlite3.connect('company.db') as conn:
conn.row_factory = sqlite3.Row # Truy cập theo tên cột
cursor = conn.cursor()
cursor.execute("SELECT * FROM employees WHERE salary > ?", (15000000,))
for row in cursor:
print(dict(row))

3.2. SQLAlchemy — Chuẩn cho dự án thực tế

SQLAlchemy hỗ trợ PostgreSQL, MySQL, SQLite, SQL Server và nhiều engine khác qua connection string thống nhất.

from sqlalchemy import create_engine, text

# PostgreSQL
engine = create_engine(
'postgresql+psycopg2://user:password@localhost:5432/mydb',
pool_size=5,
max_overflow=10,
pool_pre_ping=True # Kiểm tra connection trước khi dùng
)

# Thực thi raw SQL
with engine.connect() as conn:
result = conn.execute(
text("SELECT department, AVG(salary) AS avg_sal FROM employees GROUP BY department")
)
for row in result:
print(row.department, f"{row.avg_sal:,.0f}")

# MySQL
# engine = create_engine('mysql+pymysql://user:pass@localhost/mydb')

ORM cơ bản (khi cần map object ↔ bảng):

from sqlalchemy import Column, Integer, String, Float, create_engine
from sqlalchemy.orm import DeclarativeBase, Session

class Base(DeclarativeBase):
pass

class Employee(Base):
__tablename__ = 'employees'
id = Column(Integer, primary_key=True)
name = Column(String(100))
department = Column(String(50))
salary = Column(Float)

engine = create_engine('sqlite:///company.db')
Base.metadata.create_all(engine)

with Session(engine) as session:
it_staff = session.query(Employee).filter(
Employee.department == 'IT',
Employee.salary > 15_000_000
).all()
for emp in it_staff:
print(emp.name, emp.salary)

3.3. pandas + SQL — Cầu nối phân tích dữ liệu

pandas đọc kết quả SQL trực tiếp thành DataFrame — workflow phổ biến nhất của Data Analyst.

import pandas as pd
from sqlalchemy import create_engine

engine = create_engine('postgresql://user:pass@localhost/mydb')

# Đọc SQL → DataFrame
df = pd.read_sql_query("""
SELECT
p.category,
SUM(oi.quantity) AS total_sold,
SUM(oi.quantity * oi.unit_price) AS revenue
FROM order_items oi
JOIN products p ON oi.product_id = p.product_id
JOIN orders o ON oi.order_id = o.order_id
WHERE o.order_date >= '2025-01-01'
GROUP BY p.category
ORDER BY revenue DESC
""", engine)

print(df.head())

# Ghi DataFrame → database
df.to_sql(
'category_summary_2025',
engine,
if_exists='replace', # 'append', 'fail'
index=False,
method='multi', # Bulk insert nhanh hơn
chunksize=1000
)

# Đọc theo chunk khi dữ liệu quá lớn
for chunk in pd.read_sql("SELECT * FROM large_table", engine, chunksize=50_000):
processed = chunk[chunk['status'] == 'active']
# xử lý từng chunk...

So sánh nhanh:

Phương phápKhi nào dùng
sqlite3Học tập, script nhỏ, không cần dependency nặng
SQLAlchemyProduction, nhiều loại DB, connection pooling, ORM
pandas.read_sqlPhân tích dữ liệu, báo cáo, notebook

4. Workflow ETL đầy đủ: Extract → Transform → Load

Ví dụ pipeline xử lý dữ liệu bán hàng từ CSV thô đến báo cáo trong database.

Bước 1: Extract — Thu thập từ nhiều nguồn

import pandas as pd
from sqlalchemy import create_engine

engine = create_engine('postgresql://user:pass@localhost/analytics')

# Nguồn 1: File CSV
orders_raw = pd.read_csv('data/orders_2025.csv', parse_dates=['order_date'])

# Nguồn 2: API (ví dụ)
import requests
resp = requests.get('https://api.example.com/exchange_rates')
rates = pd.DataFrame(resp.json())

# Nguồn 3: Database hiện có
customers = pd.read_sql("SELECT id, customer_name, segment FROM customers", engine)

Bước 2: Transform — Làm sạch và tính toán

# Làm sạch
orders = orders_raw.dropna(subset=['order_id', 'customer_id', 'total_amount'])
orders = orders[orders['total_amount'] > 0]
orders['order_date'] = pd.to_datetime(orders['order_date'])
orders = orders.drop_duplicates(subset=['order_id'])

# Join với customers
df = orders.merge(customers, left_on='customer_id', right_on='id', how='left')

# Tính metric
df['month'] = df['order_date'].dt.to_period('M')
df['year'] = df['order_date'].dt.year

monthly_summary = df.groupby(['year', 'month', 'segment']).agg(
revenue=('total_amount', 'sum'),
order_count=('order_id', 'nunique'),
avg_order_value=('total_amount', 'mean'),
unique_customers=('customer_id', 'nunique')
).reset_index()

monthly_summary['month'] = monthly_summary['month'].astype(str)

Bước 3: Load — Ghi vào warehouse

monthly_summary.to_sql(
'monthly_sales_by_segment',
engine,
if_exists='replace',
index=False
)

# Hoặc upsert với raw SQL (PostgreSQL)
from sqlalchemy import text

with engine.begin() as conn:
conn.execute(text("""
INSERT INTO monthly_sales_by_segment (year, month, segment, revenue, order_count)
SELECT year, month, segment, revenue, order_count
FROM staging_monthly_sales
ON CONFLICT (year, month, segment)
DO UPDATE SET
revenue = EXCLUDED.revenue,
order_count = EXCLUDED.order_count
"""))

Bước 4: Báo cáo — SQL + Visualization

report_df = pd.read_sql("""
SELECT month, segment, revenue,
revenue - LAG(revenue) OVER (PARTITION BY segment ORDER BY month) AS mom_growth
FROM monthly_sales_by_segment
WHERE year = 2025
ORDER BY segment, month
""", engine)

import matplotlib.pyplot as plt

pivot = report_df.pivot(index='month', columns='segment', values='revenue')
pivot.plot(kind='bar', figsize=(14, 6), title='Doanh thu theo phân khúc khách hàng — 2025')
plt.ylabel('Doanh thu (VND)')
plt.xticks(rotation=45)
plt.tight_layout()
plt.savefig('reports/revenue_by_segment_2025.png', dpi=150)

Pipeline trên có thể bọc trong script, cron job, hoặc Airflow DAG để chạy tự động hàng ngày.


5. Window Functions — SQL nâng cao cho Data Analyst

Window functions tính toán trên một "cửa sổ" dòng liên quan mà không gom nhóm thành một dòng duy nhất — cực kỳ hữu ích cho phân tích xu hướng, ranking và so sánh.

5.1. ROW_NUMBER, RANK, DENSE_RANK

-- Top 3 sản phẩm bán chạy mỗi tháng
SELECT month, product, revenue, rank
FROM (
SELECT
DATE_TRUNC('month', o.order_date) AS month,
p.product_name AS product,
SUM(oi.quantity * oi.unit_price) AS revenue,
ROW_NUMBER() OVER (
PARTITION BY DATE_TRUNC('month', o.order_date)
ORDER BY SUM(oi.quantity * oi.unit_price) DESC
) AS rank
FROM orders o
JOIN order_items oi ON o.order_id = oi.order_id
JOIN products p ON oi.product_id = p.product_id
GROUP BY 1, 2
) ranked
WHERE rank <= 3;
-- Xếp hạng nhân viên theo lương trong từng phòng ban
SELECT
name,
department,
salary,
RANK() OVER (PARTITION BY department ORDER BY salary DESC) AS salary_rank,
DENSE_RANK() OVER (PARTITION BY department ORDER BY salary DESC) AS dense_rank
FROM employees;

5.2. LAG và LEAD — So sánh kỳ trước / kỳ sau

SELECT
month,
revenue,
LAG(revenue) OVER (ORDER BY month) AS prev_month_revenue,
revenue - LAG(revenue) OVER (ORDER BY month) AS mom_change,
LEAD(revenue) OVER (ORDER BY month) AS next_month_revenue
FROM (
SELECT DATE_TRUNC('month', order_date) AS month, SUM(total_amount) AS revenue
FROM orders
GROUP BY 1
) monthly;

5.3. Running total và moving average

SELECT
order_date,
total_amount,
SUM(total_amount) OVER (ORDER BY order_date ROWS UNBOUNDED PRECEDING) AS running_total,
AVG(total_amount) OVER (
ORDER BY order_date
ROWS BETWEEN 6 PRECEDING AND CURRENT ROW
) AS moving_avg_7d
FROM orders
ORDER BY order_date;

5.4. NTILE — Chia nhóm phân vị

-- Chia khách hàng thành 4 nhóm theo tổng chi tiêu (RFM đơn giản)
SELECT
customer_name,
total_spent,
NTILE(4) OVER (ORDER BY total_spent DESC) AS spending_quartile
FROM (
SELECT c.customer_name, SUM(o.total_amount) AS total_spent
FROM customers c
JOIN orders o ON c.id = o.customer_id
GROUP BY c.id, c.customer_name
) customer_totals;

6. Bảng công cụ và thư viện

Công cụ / Thư việnMục đíchGhi chú
sqlite3Database nhẹ, học tập, prototypeBuilt-in Python, không cần cài thêm
SQLAlchemyORM + raw SQL, connection poolingChuẩn de facto cho Python + SQL
pandasĐọc/ghi SQL, xử lý DataFrameread_sql, to_sql
psycopg2 / psycopgDriver PostgreSQLDùng với SQLAlchemy hoặc trực tiếp
pymysqlDriver MySQL / MariaDBPhổ biến tại VN
pyodbcDriver SQL ServerMôi trường enterprise
DuckDBOLAP in-process, SQL trên Parquet/CSVThay thế SQLite cho analytics
DBeaverGUI đa nền tảng, quản lý DBMiễn phí, hỗ trợ nhiều DB
pgAdminGUI chuyên PostgreSQLQuản trị server, query tool
DataGripIDE database (JetBrains)Trả phí, mạnh cho team
Jupyter NotebookThực hành SQL + Python tương tácKết hợp %sql magic (ipython-sql)
Apache AirflowOrchestrate ETL pipelineLên lịch, monitor, retry
dbtTransform trong warehouse bằng SQLData Engineer / Analytics Engineer

7. Bốn dự án thực hành

Dự án 1: Phân tích bán hàng với SQLite

Mục tiêu: Import CSV vào SQLite, viết truy vấn SQL, visualize bằng matplotlib.

Các bước:

  1. Tải dataset bán hàng từ Kaggle hoặc tạo CSV mẫu.
  2. Dùng pandas.to_sql() hoặc sqlite3 import vào sales.db.
  3. Viết 5–10 câu SQL: doanh thu theo tháng, top sản phẩm, khách hàng VIP.
  4. Đọc kết quả bằng pd.read_sql(), vẽ bar chart và line chart.
  5. Xuất báo cáo PDF hoặc HTML đơn giản.

Kỹ năng rèn luyện: CREATE TABLE, GROUP BY, JOIN, pandas, matplotlib.

Dự án 2: Dashboard Streamlit + PostgreSQL

Mục tiêu: Kết nối PostgreSQL, hiển thị KPI và biểu đồ tương tác trên web.

Các bước:

  1. Cài PostgreSQL (hoặc dùng Docker), tạo schema orders, customers.
  2. Viết SQL view v_daily_revenue tổng hợp doanh thu ngày.
  3. Dùng SQLAlchemy + pandas đọc view trong script Streamlit.
  4. Thêm filter theo ngày, danh mục; hiển thị metric cards và plotly chart.
  5. Deploy lên Streamlit Community Cloud (tuỳ chọn).

Kỹ năng rèn luyện: SQL view, SQLAlchemy, Streamlit, plotly.

Dự án 3: ETL pipeline tự động

Mục tiêu: Extract từ API → Transform bằng Python → Load vào database.

Các bước:

  1. Gọi API công khai (thời tiết, tỷ giá, hoặc mock API).
  2. Làm sạch JSON response thành DataFrame chuẩn hóa.
  3. Ghi vào bảng staging_*, chạy SQL transform sang bảng fact_*.
  4. Lên lịch chạy bằng schedule hoặc cron.
  5. Ghi log và xử lý lỗi (retry, email alert).

Kỹ năng rèn luyện: requests, pandas, SQL INSERT/UPSERT, automation.

Dự án 4: Phân tích A/B Testing

Mục tiêu: Lấy dữ liệu experiment từ database, tính statistical significance bằng Python.

Các bước:

  1. Thiết kế bảng experiments, user_assignments, conversion_events.
  2. SQL tính conversion rate theo variant (control vs treatment).
  3. Python: kiểm định z-test hoặc chi-square với scipy.stats.
  4. Báo cáo p-value, confidence interval, lift %.
  5. Visualization phân phối và funnel chart.

Kỹ năng rèn luyện: SQL aggregation, thống kê, scipy, seaborn.


8. Mẹo phỏng vấn SQL và Python

8.1. Câu hỏi SQL thường gặp

  • Tìm dòng trùng lặp: GROUP BY ... HAVING COUNT(*) > 1
  • Doanh thu lũy kế (running total): SUM() OVER (ORDER BY ...)
  • Top N mỗi nhóm: ROW_NUMBER() OVER (PARTITION BY ... ORDER BY ...)
  • Chênh lệch so với trung bình: so sánh với subquery AVG()
  • Self join: nhân viên — quản lý, ngày liên tiếp

8.2. Câu hỏi Python + SQL thường gặp

  • Sự khác biệt giữa read_sqlread_sql_query
  • Cách tránh SQL injection (parameterized query, text() với bind)
  • Khi nào dùng chunksize trong read_sql
  • Connection pooling là gì, tại sao cần trong production
  • if_exists='append' vs 'replace' — trade-off

8.3. Tips khi làm bài test

  1. Đọc kỹ yêu cầu — output cần cột nào, sort thế nào.
  2. Viết CTE thay vì subquery lồng 4–5 tầng — dễ debug hơn.
  3. Kiểm tra NULLCOALESCE, IS NULL, NULLIF.
  4. Giải thích query plan nếu được hỏi — EXPLAIN ANALYZE (PostgreSQL).
  5. Nói rõ trade-off — "Tôi aggregate trong SQL để giảm data transfer, rồi dùng pandas cho outlier detection vì logic phức tạp hơn."

8.4. Red flags cần tránh

  • SELECT * trên bảng lớn không cần thiết
  • String concatenation trong SQL (f"WHERE id = {user_input}") — SQL injection
  • Tải toàn bộ bảng 100M dòng vào pandas
  • Không đóng connection / không dùng context manager
  • Quên GROUP BY tất cả cột non-aggregate trong SELECT

9. Tài nguyên học tập

SQL

Python + SQL

Thực hành với dataset

Cộng đồng tiếng Việt

  • Group Facebook Data Science / Data Analyst Việt Nam
  • Viblo — tag SQL
  • YouTube: tìm "SQL cho người mới bắt đầu", "pandas read_sql"

10. FAQ — Câu hỏi thường gặp

Tôi nên học SQL hay Python trước?

Học SQL trước nếu mục tiêu là Data Analyst — bạn sẽ truy vấn dữ liệu sớm hơn. Học Python trước nếu bạn đã quen lập trình và muốn làm automation/backend. Lý tưởng nhất là song song: SQL 2–3 tuần cơ bản, rồi kết hợp pandas ngay.

SQLite có đủ cho production không?

SQLite phù hợp ứng dụng nhỏ, embedded, read-heavy. Production web app đa user nên dùng PostgreSQL hoặc MySQL vì concurrent write, replication và quản trị tốt hơn.

pandas có thay thế SQL được không?

Không hoàn toàn. pandas xử lý tốt trên dữ liệu đã nằm trong RAM (vài GB tuỳ máy). Với warehouse lớn, SQL (hoặc dbt) transform tại source hiệu quả hơn. pandas bổ sung cho giai đoạn phân tích sau khi SQL đã lọc/gom nhóm.

SQLAlchemy ORM hay raw SQL?

Dự án CRUD đơn giản: ORM tiện, type-safe. Báo cáo phức tạp, window functions, CTE: raw SQL rõ ràng và dễ maintain hơn. Nhiều team dùng hybrid — ORM cho app, raw SQL cho analytics.

Làm sao biết query SQL chậm?

Dùng EXPLAIN / EXPLAIN ANALYZE, kiểm tra full table scan, thiếu index, hoặc join sai thứ tự. Tránh SELECT *, filter sớm bằng WHERE, và đảm bảo cột trong JOINWHERE có index phù hợp.

Có cần học NoSQL khi đã biết SQL?

Tuỳ vai trò. Data Analyst chủ yếu vẫn dùng SQL trên warehouse (BigQuery, Snowflake, PostgreSQL). NoSQL (MongoDB, Redis) hữu ích nếu bạn làm full-stack hoặc Data Engineer xử lý nhiều nguồn.

Làm sao luyện tập mỗi ngày?

  • 1–2 bài LeetCode SQL
  • 1 notebook kết hợp read_sql + visualization
  • Tự đặt câu hỏi business và viết query trả lời
  • Tham gia dự án open data trên Kaggle

11. Kết luận

SQL và Python không phải hai thế giới riêng biệt — chúng là hai mảnh ghép của cùng một quy trình làm việc với dữ liệu. SQL giúp bạn truy cập, lọc và tổng hợp dữ liệu ngay tại nguồn với hiệu năng cao. Python mở rộng khả năng transform, phân tích thống kê, machine learning và tự động hóa pipeline.

Lộ trình đề xuất:

  1. Tuần 1–2: SELECT, WHERE, GROUP BY, JOIN — thực hành trên SQLite.
  2. Tuần 3–4: Kết nối Python (sqlite3, pandas.read_sql), CTE, subquery.
  3. Tuần 5–6: SQLAlchemy, PostgreSQL, window functions.
  4. Tuần 7–8: Hoàn thành ít nhất một dự án ETL và một dashboard.

Nắm vững cả SQL lẫn Python, bạn không chỉ trả lời câu hỏi kinh doanh nhanh hơn — bạn còn trở thành người có thể thiết kế pipeline dữ liệu end-to-end, từ raw data đến insight có hành động. Đó chính là kỹ năng được săn đón nhất trên thị trường việc làm dữ liệu tại Việt Nam và toàn cầu.