Errore PostgreSQL: column does not exist
ERROR: column "createdat" does not exist
LINE 1: SELECT createdAt FROM users;
^
HINT: Perhaps you meant to reference the column "users.createdAt".
PostgreSQL non è riuscito a risolvere un nome di colonna — e molto spesso l'HINT ti sta già dicendo perché. Guarda l'esempio: la query dice createdAt, l'errore dice "createdat", l'hint propone "users.createdAt". È la regola della conversione in minuscolo all'opera.
Cosa significa questo errore
Due regole SQL producono quasi tutti questi errori:
- Gli identificatori non quotati vengono convertiti in minuscolo. Una colonna creata come
"createdAt"(quotata — ORM e strumenti GUI adorano farlo) può essere referenziata solo come"createdAt". IlcreatedAtnon quotato significacreatedat, un nome diverso. - I doppi apici delimitano identificatori, gli apici singoli delimitano stringhe.
WHERE name = "alice"non confronta con la stringa 'alice' — cerca una colonna chiamataalice, e fallisce con esattamente questo errore.
Cause comuni
- Colonna con maiuscole miste creata con le virgolette, referenziata senza (o viceversa).
- Doppi apici usati per una stringa letterale — l'indizio è che la "colonna mancante" è un valore di dato.
- Un alias di SELECT usato in
WHERE: gli alias non sono visibili lì (la clausola WHERE è valutata prima della select list).SELECT price * qty AS total ... WHERE total > 100fallisce. - Schema drift: la colonna davvero non esiste in questo ambiente perché una migrazione non è stata eseguita.
Come diagnosticarlo
\d users -- nomi e case ESATTI delle colonne
Confronta carattere per carattere, incluso il case. Se la query è generata da un ORM, logga l'SQL effettivo — il quoting che l'ORM emette è tutta la storia.
Come risolverlo
- Problema di case → quotalo esattamente (
"createdAt") — o metti fine al dolore per sempre:
Identificatori snake_case minuscoli, mai quotati, è la convenzione che evita del tutto questa classe di bug.ALTER TABLE users RENAME COLUMN "createdAt" TO created_at; - Stringa letterale → apici singoli:
WHERE name = 'alice'. - Alias in WHERE → ripeti l'espressione, o avvolgi la query:
SELECT * FROM (SELECT price * qty AS total ...) t WHERE total > 100.
🔍
Stesse regole, edizione tabelle: relation does not exist — search_path e identificatori quotati.
