Какие знаешь типы constraint в PostgreSQL?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Типы ограничений (Constraints) в PostgreSQL
Constraints в PostgreSQL - это правила, которые определяют допустимые значения данных в таблице. Это основа целостности данных и один из ключевых инструментов проектирования БД. Рассмотрю все основные типы.
1. PRIMARY KEY - Первичный ключ
Уникально идентифицирует каждую строку в таблице:
// SQL
CREATE TABLE users (
id BIGSERIAL PRIMARY KEY,
email VARCHAR(255) NOT NULL UNIQUE,
name VARCHAR(100)
);
// На уровне entity (JPA)
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, unique = true)
private String email;
}
Характеристики:
- Не может быть NULL
- Уникален для каждой строки
- Возможен только один на таблицу (составной PRIMARY KEY из нескольких колонок - тоже один)
- Автоматически создаёт индекс
2. UNIQUE - Уникальность
Гарантирует уникальность значений (но может быть несколько NULL):
@Entity
public class User {
@Id
private Long id;
@Column(unique = true, nullable = false)
private String username;
@Column(unique = true)
private String email; // может быть NULL, и NULL != NULL
}
// SQL с несколькими колонками
CREATE TABLE user_emails (
user_id BIGINT,
email VARCHAR(255),
UNIQUE(user_id, email) -- эта комбинация уникальна
);
Отличие от PRIMARY KEY:
- Может быть несколько UNIQUE constraints на таблицу
- Может содержать NULL (несколько NULL'ов возможны)
- Можно удалить через ALTER TABLE
3. FOREIGN KEY - Внешний ключ
Звязывает данные в разных таблицах:
@Entity
public class Order {
@Id
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id", nullable = false)
private User user;
}
// SQL
CREATE TABLE orders (
id BIGSERIAL PRIMARY KEY,
user_id BIGINT NOT NULL,
created_at TIMESTAMP WITH TIME ZONE,
FOREIGN KEY (user_id) REFERENCES users(id)
ON DELETE CASCADE -- если удалить юзера, удалять заказы
ON UPDATE RESTRICT -- нельзя обновить id юзера если есть заказы
);
Действия при удалении/обновлении:
CASCADE- удалить связанные строкиSET NULL- установить NULLSET DEFAULT- установить значение по умолчаниюRESTRICT/NO ACTION- запретить
4. NOT NULL - Обязательное значение
Колонка не может содержать NULL:
@Entity
public class User {
@Column(nullable = false)
private String name;
@Column(nullable = false)
private LocalDateTime createdAt;
}
// SQL
CREATE TABLE users (
name VARCHAR(100) NOT NULL,
email VARCHAR(255) NOT NULL
);
5. CHECK - Проверка значений
Данные должны соответствовать условию:
@Entity
public class Order {
@Id
private Long id;
@Column(name = "total_amount")
private BigDecimal totalAmount; // CHECK constraint на БД уровне
@Min(0)
@Column(name = "discount")
private Integer discount;
}
// SQL - CHECK constraint на уровне БД
CREATE TABLE orders (
id BIGSERIAL PRIMARY KEY,
total_amount DECIMAL(10,2),
discount INTEGER,
CONSTRAINT check_positive_amount CHECK (total_amount > 0),
CONSTRAINT check_valid_discount CHECK (discount >= 0 AND discount <= 100)
);
6. DEFAULT - Значение по умолчанию
Автоматически присваивает значение если не указано:
@Entity
public class User {
@Id
private Long id;
@Column(columnDefinition = "TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP")
private LocalDateTime createdAt;
@Column(columnDefinition = "BOOLEAN DEFAULT false")
private Boolean isActive;
}
// SQL
CREATE TABLE users (
id BIGSERIAL PRIMARY KEY,
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
is_active BOOLEAN DEFAULT false,
status VARCHAR(50) DEFAULT 'PENDING'
);
7. EXCLUDE - Исключение диапазонов
Запрещает пересечение диапазонов (используется с расширением btree_gist):
// SQL - для расписания встреч (нельзя чтобы два совещания пересекались)
CREATE TABLE meetings (
id BIGSERIAL PRIMARY KEY,
room_id BIGINT,
start_time TIMESTAMP,
end_time TIMESTAMP,
EXCLUDE USING GIST (
room_id WITH =,
tsrange(start_time, end_time) WITH &&
)
);
// && оператор проверяет пересечение диапазонов
8. Составной (Composite) PRIMARY KEY
Первичный ключ из нескольких колонок:
@Entity
@IdClass(UserRoleId.class)
public class UserRole {
@Id
private Long userId;
@Id
private Long roleId;
@ManyToOne
@JoinColumn(name = "user_id", insertable = false, updatable = false)
private User user;
@ManyToOne
@JoinColumn(name = "role_id", insertable = false, updatable = false)
private Role role;
}
public class UserRoleId implements Serializable {
private Long userId;
private Long roleId;
// equals, hashCode...
}
// SQL
CREATE TABLE user_roles (
user_id BIGINT NOT NULL,
role_id BIGINT NOT NULL,
PRIMARY KEY (user_id, role_id),
FOREIGN KEY (user_id) REFERENCES users(id),
FOREIGN KEY (role_id) REFERENCES roles(id)
);
Практический пример - полная таблица с constraints:
@Entity
@Table(name = "products",
uniqueConstraints = @UniqueConstraint(columnNames = "sku"),
indexes = @Index(name = "idx_category", columnList = "category_id")
)
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(unique = true, nullable = false)
private String sku; // UNIQUE
@Column(nullable = false)
private String name; // NOT NULL
@Column(nullable = false)
private BigDecimal price; // NOT NULL, CHECK > 0
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "category_id", nullable = false)
private Category category; // FOREIGN KEY
@Column(columnDefinition = "TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP")
private LocalDateTime createdAt; // DEFAULT
}
Ключевые преимущества constraints:
- Целостность данных - БД гарантирует валидность на уровне схемы
- Производительность - индексы на PRIMARY/UNIQUE/FOREIGN KEY ускоряют запросы
- Меньше кода - не нужно валидировать в приложении
- Экономия памяти - связанные данные нормализованы
- Откат транзакций - нарушение constraint откатит всю транзакцию
Применение constraints на уровне БД - это best practice, который обеспечивает надёжность системы независимо от кода приложения.