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

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ạnh | SQL | Python |
|---|---|---|
| Mục đích chính | Truy vấn, lọc, gom nhóm trên database | Xử lý logic phức tạp, ML, visualization |
| Hiệu năng | Tố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ập | Cú pháp khai báo, dễ đọc | Ngôn ngữ lập trình đầy đủ, thư viện phong phú |
| Ứng dụng | Báo cáo, dashboard, ETL tại DB | Pipeline, 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:
SELECTcó chọn lọc →pandastransform →matplotlibvisualize →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áp | Khi nào dùng |
|---|---|
sqlite3 | Học tập, script nhỏ, không cần dependency nặng |
SQLAlchemy | Production, nhiều loại DB, connection pooling, ORM |
pandas.read_sql | Phâ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ện | Mục đích | Ghi chú |
|---|---|---|
sqlite3 | Database nhẹ, học tập, prototype | Built-in Python, không cần cài thêm |
SQLAlchemy | ORM + raw SQL, connection pooling | Chuẩn de facto cho Python + SQL |
pandas | Đọc/ghi SQL, xử lý DataFrame | read_sql, to_sql |
psycopg2 / psycopg | Driver PostgreSQL | Dùng với SQLAlchemy hoặc trực tiếp |
pymysql | Driver MySQL / MariaDB | Phổ biến tại VN |
pyodbc | Driver SQL Server | Môi trường enterprise |
DuckDB | OLAP in-process, SQL trên Parquet/CSV | Thay thế SQLite cho analytics |
DBeaver | GUI đa nền tảng, quản lý DB | Miễn phí, hỗ trợ nhiều DB |
pgAdmin | GUI chuyên PostgreSQL | Quản trị server, query tool |
DataGrip | IDE database (JetBrains) | Trả phí, mạnh cho team |
Jupyter Notebook | Thực hành SQL + Python tương tác | Kết hợp %sql magic (ipython-sql) |
Apache Airflow | Orchestrate ETL pipeline | Lên lịch, monitor, retry |
dbt | Transform trong warehouse bằng SQL | Data 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:
- Tải dataset bán hàng từ Kaggle hoặc tạo CSV mẫu.
- Dùng
pandas.to_sql()hoặcsqlite3import vàosales.db. - Viết 5–10 câu SQL: doanh thu theo tháng, top sản phẩm, khách hàng VIP.
- Đọc kết quả bằng
pd.read_sql(), vẽ bar chart và line chart. - 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:
- Cài PostgreSQL (hoặc dùng Docker), tạo schema
orders,customers. - Viết SQL view
v_daily_revenuetổng hợp doanh thu ngày. - Dùng
SQLAlchemy+pandasđọc view trong script Streamlit. - Thêm filter theo ngày, danh mục; hiển thị metric cards và plotly chart.
- 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:
- Gọi API công khai (thời tiết, tỷ giá, hoặc mock API).
- Làm sạch JSON response thành DataFrame chuẩn hóa.
- Ghi vào bảng
staging_*, chạy SQL transform sang bảngfact_*. - Lên lịch chạy bằng
schedulehoặc cron. - 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:
- Thiết kế bảng
experiments,user_assignments,conversion_events. - SQL tính conversion rate theo variant (control vs treatment).
- Python: kiểm định z-test hoặc chi-square với
scipy.stats. - Báo cáo p-value, confidence interval, lift %.
- 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_sqlvàread_sql_query - Cách tránh SQL injection (parameterized query,
text()với bind) - Khi nào dùng
chunksizetrongread_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
- Đọc kỹ yêu cầu — output cần cột nào, sort thế nào.
- Viết CTE thay vì subquery lồng 4–5 tầng — dễ debug hơn.
- Kiểm tra NULL —
COALESCE,IS NULL,NULLIF. - Giải thích query plan nếu được hỏi —
EXPLAIN ANALYZE(PostgreSQL). - 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 BYtất cả cột non-aggregate trong SELECT
9. Tài nguyên học tập
SQL
- SQLBolt — Tutorial tương tác, miễn phí
- Mode Analytics SQL Tutorial — Từ cơ bản đến nâng cao
- LeetCode Database — Luyện phỏng vấn
- HackerRank SQL — Certificate có giá trị
- PostgreSQL Documentation — Tài liệu chuẩn
- Use The Index, Luke — Hiểu index và performance
Python + SQL
- Python for Data Analysis (Wes McKinney) — Chương database và I/O
- Real Python — SQLAlchemy
- Real Python — SQLite
- SQLAlchemy Documentation
- pandas IO Tools
Thực hành với dataset
- Kaggle — Intro to SQL
- Kaggle Datasets — Retail, e-commerce, HR
- DuckDB Examples — SQL trên file Parquet/CSV
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 JOIN và WHERE 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:
- Tuần 1–2: SELECT, WHERE, GROUP BY, JOIN — thực hành trên SQLite.
- Tuần 3–4: Kết nối Python (
sqlite3,pandas.read_sql), CTE, subquery. - Tuần 5–6: SQLAlchemy, PostgreSQL, window functions.
- 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.