Lỗi này trông như thế nào
ERROR: relation "table_name" does not exist
LINE 1: SELECT * FROM table_name;
^
Bạn chạy một câu query mà bạn biết rõ là đúng — vậy mà PostgreSQL lại báo bảng không tồn tại. Bạn thấy rõ ràng nó ngay trong pgAdmin. Có bốn nguyên nhân gây ra lỗi này trong hầu hết mọi trường hợp, và không cái nào có nghĩa là dữ liệu của bạn đã mất.
Nguyên nhân
PostgreSQL nhận được query nhưng không thể tìm thấy bảng bạn chỉ định. Bản thân bảng có thể vẫn còn nguyên vẹn. Vấn đề thường là một trong những nguyên nhân sau:
- Bảng nằm ở một schema khác và
search_pathcủa bạn không bao gồm nó - Phân biệt hoa thường — bảng được tạo với tên có chữ hoa/thường trong dấu ngoặc kép như
"Users" - Bạn đang kết nối vào sai database (thường gặp trong môi trường nhiều database)
- Bảng thực sự không tồn tại — migration chưa chạy, hoặc đã chạy trên môi trường sai
- Đây là bảng tạm từ session khác — bảng tạm chỉ tồn tại trong session tạo ra nó và không hiển thị ở nơi khác
Cách sửa từng bước
Bước 1: Kiểm tra xem bảng có thực sự tồn tại không
Trước tiên, hãy xác nhận bảng có thực sự tồn tại không:
-- Liệt kê tất cả bảng trong mọi schema
SELECT table_schema, table_name
FROM information_schema.tables
WHERE table_name = 'your_table_name';
Hoặc trong psql:
\dt *.*
Không có kết quả? Bảng không tồn tại. Kiểm tra lại file migration hoặc chạy lệnh CREATE TABLE thủ công.
Bước 2: Kiểm tra Schema và Search Path
Mặc định PostgreSQL tìm kiếm trong schema public. Nhiều môi trường production sử dụng schema tùy chỉnh — app, api, reporting — và nếu search_path không bao gồm chúng, mọi query trên các bảng đó sẽ thất bại với lỗi này.
-- Xem search path hiện tại
SHOW search_path;
-- Xem schema hiện tại đang dùng
SELECT current_schema();
Bảng nằm trong schema tên app? Có ba cách xử lý:
-- Cách A: Thêm prefix schema vào tên bảng
SELECT * FROM app.users;
-- Cách B: Thêm schema vào search path cho session hiện tại
SET search_path TO app, public;
SELECT * FROM users;
-- Cách C: Đặt cố định cho user hiện tại
ALTER ROLE your_user SET search_path TO app, public;
Bước 3: Kiểm tra vấn đề phân biệt hoa thường
Lỗi này rất dễ gặp. PostgreSQL tự động chuyển tất cả định danh không có dấu ngoặc kép về chữ thường — vì vậy Users, users, và USERS đều trỏ đến cùng một thứ. Ngoại lệ: bảng được tạo với dấu ngoặc kép.
-- Tạo bảng như thế này:
CREATE TABLE "Users" (id serial, name text);
-- Lệnh này THẤT BẠI (PostgreSQL tìm tên viết thường "users"):
SELECT * FROM users;
-- Lệnh này HOẠT ĐỘNG:
SELECT * FROM "Users";
Tìm tên chính xác đã lưu trong database:
SELECT table_name FROM information_schema.tables
WHERE table_schema = 'public';
Nếu kết quả hiển thị tên có chữ hoa/thường, bạn phải dùng dấu ngoặc kép trong mọi query. Cách tốt hơn về lâu dài: luôn dùng chữ thường với dấu gạch dưới khi tạo bảng — user_accounts thay vì UserAccounts.
Bước 4: Đảm bảo bạn đang kết nối đúng Database
Mỗi database PostgreSQL hoàn toàn độc lập. Một bảng trong db_production không thể thấy từ kết nối đến db_staging — dù trên cùng một server. Đây là bẫy kinh điển trong Docker và CI pipeline khi DATABASE_URL âm thầm trỏ đến nơi không mong muốn.
-- Kiểm tra database hiện tại
SELECT current_database();
-- Trong psql, chuyển database (cần kết nối lại)
\c correct_database_name
Dùng connection string trong ứng dụng? Kiểm tra lại tên database trong file .env hoặc config.
Bước 5: Kiểm tra xem có phải View hoặc Sequence không
Lỗi tương tự cũng xảy ra khi trỏ đến view hoặc sequence không tồn tại. Tìm kiếm trên tất cả loại đối tượng cùng một lúc:
SELECT schemaname, tablename, 'table' AS type FROM pg_tables WHERE tablename = 'target_name'
UNION ALL
SELECT schemaname, viewname, 'view' FROM pg_views WHERE viewname = 'target_name'
UNION ALL
SELECT schemaname, sequencename, 'sequence' FROM pg_sequences WHERE sequencename = 'target_name';
Kiểm tra lại sau khi sửa
Kiểm tra nhanh sau khi thực hiện thay đổi:
-- Nên trả về dữ liệu hoặc tập rỗng — không phải lỗi
SELECT * FROM your_schema.your_table LIMIT 5;
-- Xác nhận đang dùng đúng schema và database
SELECT current_schema(), current_database();
Đã thay đổi search_path cho một role? Ngắt kết nối và kết nối lại để xác nhận thay đổi có hiệu lực.
Mẹo nhanh
- Đặt tên bảng bằng chữ thường với dấu gạch dưới —
user_ordersthay vì"UserOrders". Giúp loại bỏ vĩnh viễn vấn đề phân biệt hoa thường. - Dùng schema prefix tường minh (
schema.table) đáng tin cậy hơnsearch_path, đặc biệt trong code chạy trên nhiều môi trường. - Trước khi chạy migration, chạy
SELECT current_database(), current_schema();để xác nhận đang nhắm vào đúng nơi. - Trong Docker hoặc CI,
DATABASE_URLtrỏ sai database là nguyên nhân số 1 của lỗi này ngoài môi trường local. - Django và SQLAlchemy đều hỗ trợ cấu hình schema tường minh — kiểm tra
Meta.db_tablehoặc__table_args__nếu query chạy được trong psql nhưng lại lỗi trong ứng dụng của bạn.

