🔄 So sánh Python và SQL

← Quay lại Bắt đầu

File này dành riêng cho bạn

Bạn đã thành thạo T-SQL và PostgreSQL. File này đối chiếu từng khái niệm để bạn học Python nhanh hơn — và cảnh báo những chỗ hai ngôn ngữ khác nhau đủ để gây lỗi.


1. Khác biệt nền tảng

SQLPython
Loại ngôn ngữDeclarative (khai báo)Imperative (mệnh lệnh)
Bạn viết gìMô tả kết quả muốn cóMô tả từng bước thực hiện
Ai quyết định cách làmQuery optimizerChính bạn
Phạm viChỉ dữ liệu trong databaseMọi thứ: file, mạng, giao diện, tính toán
Tối ưu hóaDBMS loBạn phải tự lo

2. Bảng đối chiếu cú pháp cơ bản

Khái niệmT-SQLPythonGhi chú
Kết thúc lệnh;Xuống dòngPython không cần ;
Khối lệnhBEGIN ... ENDThụt lề 4 space⚠️ Khác biệt lớn nhất
Phân biệt hoa/thườngKhông (từ khóa)Có, tuyệt đối⚠️ Nguồn lỗi phổ biến
Khai báo biếnDECLARE @x INTx = 0Không cần khai báo kiểu
Gán giá trịSET @x = 5x = 5
So sánh bằng===⚠️ Dễ nhầm
Khác<> hoặc !=!=
Nối chuỗi+ hoặc CONCAT()+ hoặc f-string
Comment 1 dòng--#
Comment nhiều dòng/* ... */Nhiều dòng #Python không có block comment
Chỉ số chuỗiBắt đầu từ 1Bắt đầu từ 0⚠️ Lỗi off-by-one
Toán tử AND/ORAND, OR, NOTand, or, notPython viết thường

3. Kiểu dữ liệu

SQL ServerPostgreSQLPythonCảnh báo
INT, BIGINTinteger, bigintintPython không giới hạn độ lớn
DECIMAL(18,2), MONEYnumericDecimal⚠️ Dùng cho tiền, không dùng float
FLOAT, REALreal, double precisionfloatCó sai số ở cả 2 bên
VARCHAR, NVARCHARvarchar, textstrPython 3 mặc định Unicode
CHARcharstrPython không có kiểu độ dài cố định
BITbooleanboolTrue/False viết hoa chữ đầu
DATEdatedatetime.date
DATETIME2timestampdatetime.datetime
TIMEtimedatetime.time
NULLNULLNone⚠️ Hành vi khác nhau, xem mục 4
VARBINARYbyteabytes
—json, jsonbdict (qua module json)
—arraylist

Cảnh báo về tiền tệ

# ❌ SAI cho phần mềm kế toán
tong = 0.1 + 0.2
print(tong)                     # 0.30000000000000004
 
# ✅ ĐÚNG
from decimal import Decimal
tong = Decimal("0.1") + Decimal("0.2")
print(tong)                     # 0.3

Đây là lý do bạn dùng DECIMAL(18,2) chứ không dùng FLOAT trong SQL Server. Nguyên tắc hoàn toàn giống nhau ở Python.

→ 03 - Biến và kiểu dữ liệu


4. NULL vs None

SQL NULLPython None
Kiểm traWHERE Col IS NULLif x is None:
NULL = NULLUNKNOWN (không phải TRUE)None == None → True
Trong phép tính5 + NULL = NULL (lan truyền âm thầm)5 + None → TypeError ngay
Trong COUNTCOUNT(Col) bỏ qua NULLlen() đếm cả None
Trong SUMSUM bỏ qua NULLsum([1, None]) → TypeError
Thay thếISNULL(), COALESCE()x or mac_dinh, d.get(k, mac_dinh)

Khác biệt quan trọng

SQL cho NULL lan truyền âm thầm → dễ ra kết quả sai mà không biết.
Python báo lỗi ngay → dễ phát hiện, nhưng chương trình dừng.

Khi lấy dữ liệu từ database về Python, luôn xử lý None trước khi tính toán:

tong = sum(kh["cong_no"] or 0 for kh in danh_sach)

COALESCE tương đương

SELECT COALESCE(Email, N'chua co') FROM KhachHang
email = kh.get("email") or "chua co"        # Cẩn thận: 0 và "" cũng bị thay
email = kh.get("email", "chua co")          # Chỉ thay khi khóa không tồn tại
email = kh["email"] if kh["email"] is not None else "chua co"    # Chính xác nhất

5. Cấu trúc dữ liệu

SQLPythonVí dụ
Một dòng (row)dict{"MaKH": "KH001", "TenKH": "A"}
Một bảng / result setlist các dict[{...}, {...}]
Một cột của kết quảlist["KH001", "KH002"]
Danh sách giá trị IN (...)tuple hoặc set("A", "B", "C")
Indexdict (hash table){kh["ma"]: kh for kh in ds}
Temp tablelist biến tạm
# Tương đương SELECT * FROM KhachHang
khach_hang = [
    {"MaKH": "KH001", "TenKH": "Nguyen Van A", "CongNo": 5_000_000},
    {"MaKH": "KH002", "TenKH": "Tran Thi B",   "CongNo": 12_000_000},
]

→ 09 - Dict và Set


6. Truy vấn: SQL ↔ Python

Dữ liệu mẫu dùng chung cho các ví dụ dưới:

khach_hang = [
    {"ma": "KH001", "ten": "Nguyen Van A", "tinh": "Hue", "no": 45_800_000},
    {"ma": "KH002", "ten": "Tran Thi B",   "tinh": "HCM", "no": 12_500_000},
    {"ma": "KH003", "ten": "Le Van C",     "tinh": "Hue", "no": 0},
    {"ma": "KH004", "ten": "Pham Thi D",   "tinh": "HN",  "no": 89_200_000},
    {"ma": "KH005", "ten": "Hoang Van E",  "tinh": "Hue", "no": 3_200_000},
]

6.1. SELECT

SELECT * FROM KhachHang;
for kh in khach_hang:
    print(kh)

6.2. SELECT cột cụ thể

SELECT MaKH, TenKH FROM KhachHang;
[{"ma": kh["ma"], "ten": kh["ten"]} for kh in khach_hang]
 
# Hoặc chỉ lấy 1 cột
[kh["ten"] for kh in khach_hang]

6.3. WHERE

SELECT * FROM KhachHang WHERE CongNo > 0;
SELECT * FROM KhachHang WHERE Tinh = 'Hue' AND CongNo > 1000000;
SELECT * FROM KhachHang WHERE Tinh IN ('Hue', 'HCM');
SELECT * FROM KhachHang WHERE CongNo BETWEEN 1000000 AND 50000000;
SELECT * FROM KhachHang WHERE TenKH LIKE N'Nguyen%';
SELECT * FROM KhachHang WHERE TenKH LIKE N'%Van%';
[kh for kh in khach_hang if kh["no"] > 0]
[kh for kh in khach_hang if kh["tinh"] == "Hue" and kh["no"] > 1_000_000]
[kh for kh in khach_hang if kh["tinh"] in ("Hue", "HCM")]
[kh for kh in khach_hang if 1_000_000 <= kh["no"] <= 50_000_000]
[kh for kh in khach_hang if kh["ten"].startswith("Nguyen")]
[kh for kh in khach_hang if "Van" in kh["ten"]]

6.4. ORDER BY

SELECT * FROM KhachHang ORDER BY CongNo DESC;
SELECT * FROM KhachHang ORDER BY Tinh, TenKH;
sorted(khach_hang, key=lambda kh: kh["no"], reverse=True)
sorted(khach_hang, key=lambda kh: (kh["tinh"], kh["ten"]))

6.5. TOP / LIMIT

SELECT TOP 3 * FROM KhachHang ORDER BY CongNo DESC;        -- T-SQL
SELECT * FROM KhachHang ORDER BY CongNo DESC LIMIT 3;      -- PostgreSQL
sorted(khach_hang, key=lambda kh: kh["no"], reverse=True)[:3]

6.6. Hàm tổng hợp

SELECT COUNT(*), SUM(CongNo), AVG(CongNo), MIN(CongNo), MAX(CongNo)
FROM KhachHang;
no = [kh["no"] for kh in khach_hang]
 
print(len(khach_hang))          # COUNT(*)
print(sum(no))                  # SUM
print(sum(no) / len(no))        # AVG
print(min(no))                  # MIN
print(max(no))                  # MAX
SELECT COUNT(*) FROM KhachHang WHERE CongNo > 0;
len([kh for kh in khach_hang if kh["no"] > 0])
# hoặc
sum(1 for kh in khach_hang if kh["no"] > 0)

6.7. GROUP BY

SELECT Tinh, COUNT(*) AS SoLuong, SUM(CongNo) AS TongNo
FROM KhachHang
GROUP BY Tinh
ORDER BY TongNo DESC;
from collections import defaultdict
 
nhom = defaultdict(lambda: {"so_luong": 0, "tong_no": 0})
for kh in khach_hang:
    nhom[kh["tinh"]]["so_luong"] += 1
    nhom[kh["tinh"]]["tong_no"] += kh["no"]
 
for tinh, tt in sorted(nhom.items(), key=lambda x: x[1]["tong_no"], reverse=True):
    print(f"{tinh:<8}{tt['so_luong']:>5}{tt['tong_no']:>18,}")

6.8. GROUP BY … HAVING

SELECT Tinh, SUM(CongNo) AS TongNo
FROM KhachHang
GROUP BY Tinh
HAVING SUM(CongNo) > 10000000;
ket_qua = {t: tt for t, tt in nhom.items() if tt["tong_no"] > 10_000_000}

6.9. DISTINCT

SELECT DISTINCT Tinh FROM KhachHang;
set(kh["tinh"] for kh in khach_hang)              # Mất thứ tự
list(dict.fromkeys(kh["tinh"] for kh in khach_hang))   # Giữ thứ tự

6.10. CASE WHEN

SELECT MaKH,
       CASE
           WHEN CongNo = 0 THEN N'Khong no'
           WHEN CongNo < 10000000 THEN N'No it'
           ELSE N'No nhieu'
       END AS PhanLoai
FROM KhachHang;
def phan_loai(no):
    if no == 0:
        return "Khong no"
    elif no < 10_000_000:
        return "No it"
    else:
        return "No nhieu"
 
[{"ma": kh["ma"], "phan_loai": phan_loai(kh["no"])} for kh in khach_hang]

6.11. JOIN

SELECT hd.SoHD, kh.TenKH, hd.SoTien
FROM HoaDon hd
INNER JOIN KhachHang kh ON hd.MaKH = kh.MaKH;
hoa_don = [
    {"so_hd": "HD001", "ma_kh": "KH001", "so_tien": 15_000_000},
    {"so_hd": "HD002", "ma_kh": "KH002", "so_tien": 8_500_000},
    {"so_hd": "HD003", "ma_kh": "KH999", "so_tien": 3_000_000},   # KH không tồn tại
]
 
# Tạo index để tra cứu nhanh (tương đương index trong DB)
index_kh = {kh["ma"]: kh for kh in khach_hang}
 
# INNER JOIN — chỉ lấy dòng khớp
inner = [
    {"so_hd": hd["so_hd"], "ten_kh": index_kh[hd["ma_kh"]]["ten"], "so_tien": hd["so_tien"]}
    for hd in hoa_don
    if hd["ma_kh"] in index_kh
]
 
# LEFT JOIN — lấy hết bên trái, bên phải None nếu không khớp
left = [
    {
        "so_hd": hd["so_hd"],
        "ten_kh": index_kh.get(hd["ma_kh"], {}).get("ten"),
        "so_tien": hd["so_tien"],
    }
    for hd in hoa_don
]

6.12. UNION / INTERSECT / EXCEPT

SELECT MaKH FROM BangA
UNION
SELECT MaKH FROM BangB;
a = {"KH001", "KH002", "KH003"}
b = {"KH002", "KH003", "KH004"}
 
a | b       # UNION      -> {KH001, KH002, KH003, KH004}
a & b       # INTERSECT  -> {KH002, KH003}
a - b       # EXCEPT     -> {KH001}
a ^ b       # FULL OUTER trừ phần chung -> {KH001, KH004}

Ứng dụng thực tế nhất cho bạn

Đối chiếu dữ liệu giữa 2 hệ thống:

erp = set(ma_kh_tu_database)
excel = set(ma_kh_tu_file_excel)
 
print("Thieu trong Excel:", sorted(erp - excel))
print("Thua trong Excel :", sorted(excel - erp))
print("Khop nhau        :", len(erp & excel))

6.13. Hàm chuỗi

T-SQLPython
UPPER(s)s.upper()
LOWER(s)s.lower()
LEN(s)len(s)
LTRIM(s) / RTRIM(s) / TRIM(s)s.lstrip() / s.rstrip() / s.strip()
LEFT(s, n)s[:n]
RIGHT(s, n)s[-n:]
SUBSTRING(s, start, len)s[start-1 : start-1+len] ⚠️ chỉ số từ 0
CHARINDEX(x, s) (từ 1, không thấy = 0)s.find(x) (từ 0, không thấy = -1)
REPLACE(s, a, b)s.replace(a, b)
REPLICATE(s, n)s * n
CONCAT(a, b)a + b hoặc f-string
REVERSE(s)s[::-1]
STRING_SPLIT(s, ',')s.split(",")
STRING_AGG(s, ',')",".join(ds)

6.14. Hàm ngày tháng

T-SQLPython
GETDATE()datetime.now()
CAST(GETDATE() AS DATE)date.today()
DATEADD(day, 30, d)d + timedelta(days=30)
DATEDIFF(day, d1, d2)(d2 - d1).days
YEAR(d) / MONTH(d) / DAY(d)d.year / d.month / d.day
FORMAT(d, 'dd/MM/yyyy')d.strftime("%d/%m/%Y")
CONVERT(DATE, s, 103)datetime.strptime(s, "%d/%m/%Y").date()
EOMONTH(d)calendar.monthrange(y, m)[1]

6.15. Hàm toán học

T-SQLPython
ABS(x)abs(x)
ROUND(x, n)round(x, n) ⚠️ quy tắc làm tròn KHÁC
CEILING(x)math.ceil(x)
FLOOR(x)math.floor(x)
POWER(x, y)x ** y
SQRT(x)math.sqrt(x) hoặc x ** 0.5
x % yx % y

ROUND() khác nhau

  • T-SQL: ROUND(2.5, 0) = 3 (round half away from zero)
  • Python: round(2.5) = 2 (banker’s rounding — làm tròn về số chẵn)

Khi viết chương trình đối chiếu số liệu với hệ thống ERP, đây là nguồn lệch số rất dễ bỏ sót.

Cách làm tròn giống SQL Server:

from decimal import Decimal, ROUND_HALF_UP
Decimal("2.5").quantize(Decimal("1"), rounding=ROUND_HALF_UP)   # 3

7. Điều khiển luồng

T-SQLPython
IF ... BEGIN ... ENDif ...: + thụt lề
IF ... ELSE ...if ...: / else:
WHILE ... BEGIN ... ENDwhile ...:
BREAKbreak
CONTINUEcontinue
DECLARE CURSOR ... FETCHfor x in ds:
RETURNreturn
TRY ... CATCHtry ... except
THROW / RAISERRORraise
ERROR_MESSAGE()str(e) trong except ... as e
BEGIN TRY
    -- ...
END TRY
BEGIN CATCH
    PRINT ERROR_MESSAGE();
END CATCH
try:
    ...
except Exception as e:
    print(e)

8. Stored Procedure ↔ Function

CREATE PROCEDURE sp_TinhThue
    @SoTien DECIMAL(18,2),
    @ThueSuat DECIMAL(5,4) = 0.1
AS
BEGIN
    SELECT @SoTien * @ThueSuat AS TienThue;
END
from decimal import Decimal
 
def tinh_thue(so_tien: Decimal, thue_suat: Decimal = Decimal("0.1")) -> Decimal:
    """Tính tiền thuế."""
    return so_tien * thue_suat
Stored ProcedurePython function
Tham số mặc định✅✅
Trả về nhiều giá trịQua OUTPUT parameterreturn a, b, c (tuple)
Gọi lồng nhau✅✅
Chi phí tạoCao (thay đổi schema)Rất thấp
Số lượng thường thấyVài chục / DBVài trăm / dự án

→ 10 - Hàm (Function)


9. Giao dịch (Transaction)

BEGIN TRANSACTION;
BEGIN TRY
    UPDATE TaiKhoan SET SoDu = SoDu - 1000 WHERE Id = 1;
    UPDATE TaiKhoan SET SoDu = SoDu + 1000 WHERE Id = 2;
    COMMIT;
END TRY
BEGIN CATCH
    ROLLBACK;
END CATCH
try:
    cur.execute("UPDATE TaiKhoan SET SoDu = SoDu - ? WHERE Id = ?", (1000, 1))
    cur.execute("UPDATE TaiKhoan SET SoDu = SoDu + ? WHERE Id = ?", (1000, 2))
    conn.commit()
except Exception:
    conn.rollback()
    raise

→ 16 - Python kết nối Database


10. Mười cái bẫy cho dân SQL học Python

#BẫyChi tiết
1Chỉ số bắt đầu từ 0SUBSTRING(s,1,1) → s[0]
2Phân biệt hoa/thườngPrint ≠ print, MaKH ≠ makh
3Thụt lề là bắt buộcSai một dấu cách là lỗi
4= vs === là gán, == là so sánh
5stop không được lấys[0:3] lấy 3 ký tự, không phải 4
6float không dùng cho tiềnDùng Decimal như DECIMAL(18,2)
7round() khác nhauPython dùng banker’s rounding
8None không lan truyền5 + None báo lỗi ngay, khác 5 + NULL
9b = a không copy listSửa b thì a cũng đổi
10input() luôn trả chuỗi"10" > "9" là True

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

Nguyên tắc vàng

Làm càng nhiều càng tốt trong SQL, chỉ mang về Python những gì SQL không làm được.

ViệcNên làm ở
Lọc, nhóm, tổng hợp dữ liệu lớnSQL — có index, optimizer
JOIN nhiều bảngSQL
Sắp xếp, phân trangSQL
Đọc/ghi file Excel, CSVPython
Gọi API bên ngoàiPython
Gửi email, tạo file PDFPython
Logic nghiệp vụ phức tạp nhiều nhánhPython
Vẽ biểu đồ, báo cáo trực quanPython
Tự động hóa lặp lại theo lịchPython

Ví dụ sai và đúng

# ❌ SAI — mang 1 triệu dòng về rồi mới lọc bằng Python
cur.execute("SELECT * FROM HoaDon")
tat_ca = cur.fetchall()
loc = [hd for hd in tat_ca if hd["Nam"] == 2026]
 
# ✅ ĐÚNG — để SQL lọc, chỉ mang về cái cần
cur.execute("SELECT * FROM HoaDon WHERE YEAR(NgayLap) = ?", (2026,))
loc = cur.fetchall()

Liên kết