← Back to course

Типы Данных и Ограничения

Типы Данных и Ограничения

С возвращением.

В предыдущем уроке ты изучил базовые SQL-действия:

Ты научился читать данные.

Добавлять данные.

Изменять данные.

Удалять данные.

Очень мощно.

И немного опасно.

Сегодня мы научимся делать таблицы безопаснее.

Потому что база данных не должна принимать всё подряд.

Если колонка предназначена для возраста, она не должна принимать:

banana

Если email должен быть уникальным, PostgreSQL должен остановить двух пользователей от использования одного и того же email.

Если имя обязательно, PostgreSQL не должен позволять загадочным пустым людям заходить в таблицу.

Именно здесь помогают типы данных и ограничения.

Типы данных определяют, какой тип данных может хранить колонка.

Ограничения определяют правила, которым данные должны следовать.

Вместе они делают базу данных сильнее.

Как серьёзный охранник.

Но с точками с запятой.

Что Ты Изучишь

В этом уроке ты изучишь:

В конце этого урока ты будешь понимать, как проектировать таблицы, которые не принимают ерунду.

Это важно.

Потому что плохие данные — как блёстки.

Если они один раз попали в систему, они появляются везде.

Навсегда.

Что Такое Типы Данных?

Тип данных говорит PostgreSQL, какой тип значения может хранить колонка.

Пример:

CREATE TABLE students (
  id SERIAL PRIMARY KEY,
  name VARCHAR(100),
  age INTEGER
);

Здесь:

name VARCHAR(100)

означает, что колонка name хранит текст.

А:

age INTEGER

означает, что колонка age хранит целые числа.

То есть PostgreSQL понимает:

name = текст
age  = число

Это полезно, потому что PostgreSQL может отклонить неправильные данные.

Если ты попробуешь вставить текст в колонку INTEGER, PostgreSQL будет протестовать.

И правильно.

Базы данных должны протестовать, когда приходит ерунда.

Тишина опасна.

Особенно в базах данных.

И на кухнях.

Почему Типы Данных Важны

Представь такую таблицу:

CREATE TABLE users (
  id SERIAL PRIMARY KEY,
  name TEXT,
  age TEXT
);

Технически age здесь текст.

Поэтому PostgreSQL может разрешить такое:

INSERT INTO users (name, age)
VALUES ('Anna', 'twenty two');

Или даже такое:

INSERT INTO users (name, age)
VALUES ('Marco', 'banana');

Это плохо.

Возраст должен быть числом.

Лучше:

CREATE TABLE users (
  id SERIAL PRIMARY KEY,
  name TEXT,
  age INTEGER
);

Теперь PostgreSQL ожидает число для age.

Это защищает твои данные.

Хорошая структура блокирует глупые данные ещё до входа в таблицу.

PostgreSQL фактически говорит:

Сегодня без банановых возрастов.

Прекрасно.

Популярные Типы Данных в PostgreSQL

PostgreSQL имеет много типов данных.

Сегодня мы изучим самые полезные для начинающих:

Этого достаточно для многих beginner-проектов.

Позже ты можешь изучить более advanced типы.

У PostgreSQL их много.

Потому что PostgreSQL посмотрел на данные и сказал:

Да, я могу всё это организовать.

Очень амбициозно.

Очень полезно.

INTEGER

INTEGER хранит целые числа.

Пример:

age INTEGER

Хорошие значения:

18
25
100

Плохие значения:

hello
25.5
banana

Пример таблицы:

CREATE TABLE players (
  id SERIAL PRIMARY KEY,
  name VARCHAR(100),
  score INTEGER
);

Вставь данные:

INSERT INTO players (name, score)
VALUES ('Anna', 100);

Это работает.

А это нет:

INSERT INTO players (name, score)
VALUES ('Marco', 'very good');

Потому что score ожидает число.

PostgreSQL отказывает.

И правильно делает.

База данных без стандартов — это просто электронная таблица с амбициями.

VARCHAR

VARCHAR хранит текст с максимальной длиной.

Пример:

name VARCHAR(100)

Это означает:

Имя может содержать максимум 100 символов.

Пример:

CREATE TABLE products (
  id SERIAL PRIMARY KEY,
  name VARCHAR(100),
  category VARCHAR(50)
);

Это хорошо для текстовых полей, где нужен разумный лимит.

Например:

Если ты попробуешь вставить текст длиннее лимита, PostgreSQL его отклонит.

Хорошо.

Иногда лимиты полезны.

Особенно для колонок базы данных.

И, возможно, для порций пиццы.

TEXT

TEXT хранит длинный текст.

Пример:

description TEXT

Используй TEXT, когда тебе не нужен строгий лимит символов.

Хорошо подходит для:

Пример:

CREATE TABLE articles (
  id SERIAL PRIMARY KEY,
  title VARCHAR(150),
  content TEXT
);

Здесь:

title VARCHAR(150)

имеет лимит.

А:

content TEXT

может быть длинным.

Это логично.

Название не должно быть романом.

А поле content может им быть.

PostgreSQL практичен.

В основном.

BOOLEAN

BOOLEAN хранит значения true или false.

Пример:

available BOOLEAN

Хорошие значения:

true
false

Пример таблицы:

CREATE TABLE tasks (
  id SERIAL PRIMARY KEY,
  title VARCHAR(150),
  completed BOOLEAN
);

Вставь данные:

INSERT INTO tasks (title, completed)
VALUES ('Learn PostgreSQL', false);

Позже можно обновить:

UPDATE tasks
SET completed = true
WHERE title = 'Learn PostgreSQL';

BOOLEAN идеально подходит для значений да/нет:

Не храни это как текст:

yes
no
maybe
sort of

Там начинается хаос.

Используй BOOLEAN.

Позволь базе данных помочь тебе.

DATE

DATE хранит календарные даты.

Пример:

birth_date DATE

Вставь дату так:

'1992-10-23'

PostgreSQL любит формат:

YYYY-MM-DD

Пример таблицы:

CREATE TABLE events (
  id SERIAL PRIMARY KEY,
  title VARCHAR(150),
  event_date DATE
);

Вставь данные:

INSERT INTO events (title, event_date)
VALUES ('PostgreSQL Practice', '2026-05-03');

Даты должны быть датами.

Не текстом.

Не случайными строками.

Не “следующая пятница, наверное”.

PostgreSQL может нормально работать с датами только тогда, когда ты хранишь их как даты.

Шок.

Но правда.

NUMERIC

NUMERIC хранит точные десятичные числа.

Это полезно для денег.

Пример:

price NUMERIC(10, 2)

Это означает:

До 10 цифр всего, с 2 цифрами после десятичной точки.

Примеры значений:

49.99
100.00
1250.50

Пример таблицы:

CREATE TABLE courses (
  id SERIAL PRIMARY KEY,
  title VARCHAR(150),
  price NUMERIC(10, 2)
);

Вставь данные:

INSERT INTO courses (title, price)
VALUES ('PostgreSQL Basics', 49.99);

Для цен NUMERIC обычно лучше, чем INTEGER.

Разве что ты хранишь центы как целые числа.

Это тоже нормальный подход.

Но для начинающих NUMERIC(10, 2) легко понять.

Деньги заслуживают точности.

Особенно когда они уходят с твоего счёта.

SERIAL

SERIAL создаёт автоматически растущее целое число.

Пример:

id SERIAL PRIMARY KEY

Это означает, что PostgreSQL автоматически генерирует ID.

Пример:

CREATE TABLE students (
  id SERIAL PRIMARY KEY,
  name VARCHAR(100)
);

Вставь данные без id:

INSERT INTO students (name)
VALUES ('Anna');

PostgreSQL автоматически даст Anna ID.

Потом следующая строка получит следующий ID.

И так далее.

Очень полезно.

Не управляй ID вручную, если PostgreSQL может сделать это за тебя.

Жизнь и так сложная.

Пусть база данных считает.

Что Такое Ограничения?

Ограничения — это правила для данных в таблице.

Они говорят PostgreSQL:

Это значение обязательно.
Это значение должно быть уникальным.
Это значение должно соответствовать условию.
Это значение имеет default.

Популярные ограничения:

Ограничения делают таблицы безопаснее.

Они останавливают плохие данные до того, как те попадут в таблицу.

Думай об ограничениях как о правилах таблицы.

Например:

Без пустых имён.
Без дублированных email.
Без отрицательных цен.
Без невозможного возраста.

Очень разумно.

PostgreSQL строгий.

Но здесь строгость — это хорошо.

NOT NULL

NOT NULL означает, что колонка обязательная.

Пример:

name VARCHAR(100) NOT NULL

Это означает, что каждая строка должна иметь имя.

Пример таблицы:

CREATE TABLE customers (
  id SERIAL PRIMARY KEY,
  name VARCHAR(100) NOT NULL,
  email VARCHAR(150)
);

Это работает:

INSERT INTO customers (name, email)
VALUES ('Anna', 'anna@example.com');

Это не работает:

INSERT INTO customers (email)
VALUES ('no-name@example.com');

Потому что name обязательно.

Хорошо.

Никаких загадочных customers без имён.

Это не база данных шпионов.

Наверное.

UNIQUE

UNIQUE означает, что значения в колонке не могут повторяться.

Очень полезно для email.

Пример:

email VARCHAR(150) UNIQUE

Таблица:

CREATE TABLE users (
  id SERIAL PRIMARY KEY,
  name VARCHAR(100) NOT NULL,
  email VARCHAR(150) UNIQUE
);

Вставь первого пользователя:

INSERT INTO users (name, email)
VALUES ('Anna', 'anna@example.com');

Это работает.

Попробуй вставить другого пользователя с тем же email:

INSERT INTO users (name, email)
VALUES ('Another Anna', 'anna@example.com');

PostgreSQL это отклонит.

Потому что email должен быть уникальным.

Это защищает твои данные.

Без UNIQUE дубли email могут создать проблемы с login, account и головной болью.

Головная боль от базы данных не весёлая.

Она носит тяжёлые ботинки.

DEFAULT

DEFAULT даёт колонке значение, если ты ничего не передал.

Пример:

active BOOLEAN DEFAULT true

Таблица:

CREATE TABLE subscribers (
  id SERIAL PRIMARY KEY,
  email VARCHAR(150) UNIQUE NOT NULL,
  active BOOLEAN DEFAULT true
);

Вставь без active:

INSERT INTO subscribers (email)
VALUES ('reader@example.com');

Теперь проверь:

SELECT * FROM subscribers;

Колонка active должна быть true.

PostgreSQL использовал значение по умолчанию.

Default-ы полезны, когда большинство строк должны начинаться с одного и того же значения.

Например:

Хорошие default-ы уменьшают повторение кода.

А повторение кода — это место, где маленькие bugs строят гнёзда.

CHECK

CHECK создаёт правило, которому значения должны соответствовать.

Пример:

price NUMERIC(10, 2) CHECK (price >= 0)

Это означает, что цена не может быть отрицательной.

Таблица:

CREATE TABLE store_products (
  id SERIAL PRIMARY KEY,
  name VARCHAR(100) NOT NULL,
  price NUMERIC(10, 2) CHECK (price >= 0)
);

Это работает:

INSERT INTO store_products (name, price)
VALUES ('Mouse', 25.00);

Это не работает:

INSERT INTO store_products (name, price)
VALUES ('Magic Refund Product', -10.00);

PostgreSQL это отклонит.

Хорошо.

Отрицательные цены могут существовать в бухгалтерии.

Но не в этой таблице.

База данных защищает правило.

Очень серьёзно.

Очень полезно.

CHECK с Возрастом

Другой пример:

age INTEGER CHECK (age >= 0)

Это означает, что возраст не может быть отрицательным.

Таблица:

CREATE TABLE people (
  id SERIAL PRIMARY KEY,
  name VARCHAR(100) NOT NULL,
  age INTEGER CHECK (age >= 0)
);

Это работает:

INSERT INTO people (name, age)
VALUES ('Anna', 22);

Это не работает:

INSERT INTO people (name, age)
VALUES ('Time Traveler', -5);

PostgreSQL отказывает.

Хорошо.

Если тебе нужны путешественники во времени, создай отдельную таблицу.

Возможно.

Сочетание Ограничений

Можно сочетать несколько ограничений в одной таблице.

Пример:

CREATE TABLE app_users (
  id SERIAL PRIMARY KEY,
  name VARCHAR(100) NOT NULL,
  email VARCHAR(150) UNIQUE NOT NULL,
  active BOOLEAN DEFAULT true,
  age INTEGER CHECK (age >= 0)
);

Эта таблица имеет много правил:

Это намного безопаснее, чем таблица без правил.

Таблица без ограничений принимает слишком много.

Она слишком вежливая.

Базы данных не должны быть слишком вежливыми.

Они должны защищать данные.

Как серьёзный фейс-контроль в клубе информации.

Создай Более Безопасную Таблицу Products

Создадим лучшую таблицу products.

Сначала удали старую, если нужно:

DROP TABLE IF EXISTS safe_products;

Теперь создай:

CREATE TABLE safe_products (
  id SERIAL PRIMARY KEY,
  name VARCHAR(100) NOT NULL,
  category VARCHAR(100) DEFAULT 'General',
  price NUMERIC(10, 2) CHECK (price >= 0),
  available BOOLEAN DEFAULT true
);

Эта таблица имеет лучшие правила:

Вставь данные:

INSERT INTO safe_products (name, price)
VALUES ('Laptop', 900.00);

Проверь:

SELECT * FROM safe_products;

Ты должен увидеть:

category = General
available = true

PostgreSQL заполнил default-ы.

Приятно.

База данных помогает.

Наконец-то.

Протестируй Правила

Попробуй вставить товар без названия:

INSERT INTO safe_products (price)
VALUES (10.00);

PostgreSQL должен отклонить это, потому что name является NOT NULL.

Попробуй вставить отрицательную цену:

INSERT INTO safe_products (name, price)
VALUES ('Broken Product', -5.00);

PostgreSQL должен отклонить это из-за CHECK.

Это хорошо.

Таблица защищает себя сама.

Таблица, которая защищает себя сама, намного безопаснее таблицы, которая принимает всё.

Как дверь с замком.

Не идеально.

Но лучше, чем штора.

Проверь Таблицу

Выполни:

\d safe_products

Ты должен увидеть колонки, типы, default-ы и constraints.

Это полезная команда.

Используй её часто.

Когда забудешь, как выглядит таблица, спроси PostgreSQL.

Он помнит.

Он всегда помнит.

Очень базоданно.

Немного страшно.

Типичные Ошибки

Использовать TEXT для Всего

Плохо:

CREATE TABLE users (
  id SERIAL PRIMARY KEY,
  name TEXT,
  age TEXT,
  active TEXT
);

Лучше:

CREATE TABLE users (
  id SERIAL PRIMARY KEY,
  name VARCHAR(100),
  age INTEGER,
  active BOOLEAN
);

Используй правильный тип.

Text — это не универсальное решение.

Это полезный инструмент.

Не мусорный пакет для storage.

Забыть NOT NULL

Плохо:

CREATE TABLE users (
  id SERIAL PRIMARY KEY,
  name VARCHAR(100),
  email VARCHAR(150)
);

Это позволяет создавать пользователей без имён или email.

Возможно, это не то, что ты хочешь.

Лучше:

CREATE TABLE users (
  id SERIAL PRIMARY KEY,
  name VARCHAR(100) NOT NULL,
  email VARCHAR(150) NOT NULL
);

Если значение обязательно, скажи это.

PostgreSQL будет это контролировать.

Забыть UNIQUE для Email

Плохо:

email VARCHAR(150)

Лучше:

email VARCHAR(150) UNIQUE

Если email идентифицирует пользователей, обычно он должен быть уникальным.

Дубли email — это как два человека с одним паспортом.

Возможно в плохой системе.

Ужасная идея.

Разрешать Отрицательные Цены

Плохо:

price NUMERIC(10, 2)

Лучше:

price NUMERIC(10, 2) CHECK (price >= 0)

Если отрицательные цены не имеют смысла, заблокируй их.

Не доверяй будущему коду, что он всегда будет вести себя хорошо.

Будущий код пишут будущие люди.

Опасно.

Практика

Создай таблицу с названием employees.

Она должна иметь:

Используй эти правила:

Пример:

CREATE TABLE employees (
  id SERIAL PRIMARY KEY,
  name VARCHAR(100) NOT NULL,
  email VARCHAR(150) UNIQUE NOT NULL,
  salary NUMERIC(10, 2) CHECK (salary >= 0),
  active BOOLEAN DEFAULT true,
  hired_at DATE
);

Вставь employees:

INSERT INTO employees (name, email, salary, hired_at)
VALUES ('Anna', 'anna@example.com', 2500.00, '2026-05-03');
INSERT INTO employees (name, email, salary, active, hired_at)
VALUES ('Marco', 'marco@example.com', 3000.00, false, '2026-05-04');

Прочитай данные:

SELECT * FROM employees;

Потом протестируй constraints.

Попробуй дублированный email.

Попробуй отрицательную salary.

Попробуй отсутствующее name.

Позволь PostgreSQL тебя остановить.

В этом и смысл.

Мини-Челлендж

Создай таблицу с названием courses.

Она должна иметь:

Правила:

Подсказка:

created_at DATE DEFAULT CURRENT_DATE

Вставь минимум три курса.

Потом выполни:

SELECT * FROM courses;

Попробуй вставить курс без title.

Попробуй вставить курс с отрицательной price.

PostgreSQL должен отклонить плохие данные.

Это не PostgreSQL раздражает тебя.

Это PostgreSQL полезен.

Есть разница.

Иногда.

Итог

Сегодня ты изучил:

Это большой шаг.

Ты уже не просто создаёшь таблицы.

Ты создаёшь более безопасные таблицы.

База данных не должна принимать ерунду.

Она должна защищать структуру.

Она должна отклонять плохие данные.

Она должна останавливать банановые возрасты, дублированные email, отрицательные цены и загадочных пустых пользователей.

PostgreSQL может это делать.

Если ты дашь ему правила.

Следующий Урок

В следующем уроке мы научимся фильтровать и сортировать данные.

Мы глубже разберём:

Потому что когда у тебя есть данные, нужно найти правильные данные.

Не все данные.

Правильные данные.

Именно здесь запросы становятся интересными.

И немного опасными.