Пошук уроків, статей та іншого контенту
Числа, текст, дати, булеві значення — і чому для грошей ніколи не використовують FLOAT.
INTEGER — ціле число в межах приблизно ±2 мільярди; BIGINT — для більших діапазонів (наприклад, лічильники, що можуть перевищити межу INTEGER з часом).
NUMERIC(precision, scale) — точне десяткове число з фіксованою кількістю знаків після коми, без похибок округлення — обов'язковий вибір для грошових сум.
REAL / DOUBLE PRECISION — числа з рухомою комою (те саме IEEE 754 представлення, що й number у JavaScript, курс JavaScript, урок про Math і точність чисел) — швидші, але з тими самими похибками округлення, тому непридатні для грошей.
CREATE TABLE products (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL,
price NUMERIC(10, 2) NOT NULL -- до 10 цифр загалом, 2 після коми: 99999999.99
);Та сама проблема з 0.1 + 0.2 !== 0.3 з курсу JavaScript існує й у REAL/DOUBLE PRECISION PostgreSQL — з тієї самої причини (двійкове представлення дробових чисел). NUMERIC зберігає число точно як десятковий дріб і не має цієї похибки, ціною трохи повільніших обчислень — прийнятний компроміс для грошових сум, де точність критичніша за швидкість.
TEXT — рядок необмеженої довжини; VARCHAR(n) — рядок з явним обмеженням довжини. У PostgreSQL (на відміну від деяких інших СУБД) різниця в продуктивності між ними мінімальна — TEXT типово обирають за замовчуванням, а VARCHAR(n) — коли обмеження довжини є реальним бізнес-правилом (наприклад, поштовий індекс), а не довільним лімітом.
DATE — лише дата, без часу (день народження, дата видання статті).
TIMESTAMP — дата й час без прив'язки до часового поясу.
TIMESTAMPTZ (timestamp with time zone) — дата й час, що зберігається у UTC і конвертується під клієнтський часовий пояс при читанні; практично завжди правильний вибір за замовчуванням для міток часу подій (created_at, updated_at), щоб уникнути двозначності, коли сервер і користувачі в різних часових поясах.
BOOLEAN зберігає true/false — але, на відміну від TypeScript boolean, стовпець без NOT NULL додатково може містити третє значення NULL («невідомо»), яке в логічних виразах поводиться не так, як false: NOT NULL::boolean лишається NULL, а не true.
Коли частина даних справді не має наперед фіксованої структури (наприклад, довільні налаштування користувача, що змінюються між версіями застосунку), JSONB зберігає JSON-документ у бінарному, ефективно індексованому форматі — компроміс між гнучкістю документоорієнтованих баз даних і рештою реляційних гарантій PostgreSQL:
CREATE TABLE user_settings (
user_id UUID PRIMARY KEY REFERENCES users(id),
preferences JSONB NOT NULL DEFAULT '{}'
);
SELECT * FROM user_settings WHERE preferences->>'theme' = 'dark';Використання REAL/DOUBLE PRECISION для грошових сум — накопичені похибки округлення проявляються після достатньої кількості операцій, і сума в базі перестає збігатися з очікуваною копійка в копійку.
TIMESTAMP без часового поясу для міток часу подій — призводить до плутанини й помилок, коли сервер і користувачі перебувають у різних часових поясах.
Класти геть усі дані в один стовпець JSONB «про всяк випадок», навіть коли структура насправді відома заздалегідь, — втрачаються переваги типізованих стовпців (перевірка типу на рівні бази даних, простіші й швидші звичайні запити, можливість NOT NULL/CHECK-обмежень на конкретне поле).
PostgreSQL пропонує точні (NUMERIC) і наближені (REAL/DOUBLE PRECISION) числові типи — для грошей завжди NUMERIC через ту саму проблему двійкової точності, що й у JavaScript. TIMESTAMPTZ — типовий безпечний вибір для міток часу подій. JSONB дає контрольовану гнучкість для дійсно неструктурованих даних, не замінюючи звичайні типізовані стовпці там, де структура даних відома заздалегідь.