← Назад к вопросам

Сколько символов хранит поле VARCHAR в PostgreSQL?

1.2 Junior🔥 71 комментариев
#Базы данных и SQL

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

VARCHAR в PostgreSQL: лимиты и особенности

VARCHAR(n) в PostgreSQL может хранить ДО n символов, где n — число, указанное в скобках. Но есть важные детали, которые часто забывают разработчики.

Базовая информация

-- Определение типа VARCHAR
CREATE TABLE users (
    id SERIAL PRIMARY KEY,
    email VARCHAR(255),          -- До 255 символов
    name VARCHAR(100),           -- До 100 символов
    bio TEXT,                    -- Неограниченный текст
    description VARCHAR          -- Без лимита (как TEXT)
);

Сравнение типов

┌──────────────┬────────────────────────┬──────────────┐
│ Тип          │ Лимит                  │ Хранение     │
├──────────────┼────────────────────────┼──────────────┤
│ VARCHAR(n)   │ До n символов          │ Переменное   │
│ CHAR(n)      │ Ровно n символов       │ Фиксированное│
│ VARCHAR      │ До 1 GB (1073741823)   │ Переменное   │
│ TEXT         │ До 1 GB                │ Переменное   │
│ SMALLTEXT    │ Нет (используй TEXT)   │ -            │
└──────────────┴────────────────────────┴──────────────┘

Примеры использования в Java

@Entity
@Table(name = "users")
public class User {
    
    @Id
    private UUID id;
    
    // VARCHAR(255) — email должен быть разумной длины
    @Column(name = "email", length = 255, nullable = false, unique = true)
    private String email;  // max 255 символов
    
    // VARCHAR(100) — имя
    @Column(name = "name", length = 100, nullable = false)
    private String name;   // max 100 символов
    
    // TEXT — описание может быть длинным
    @Column(name = "bio", columnDefinition = "TEXT")
    private String bio;    // max ~1 GB
    
    // VARCHAR(50) — статус
    @Column(name = "status", length = 50)
    @Enumerated(EnumType.STRING)
    private UserStatus status;  // ACTIVE, INACTIVE, DELETED
}

// Validation
@NotBlank
@Email
@Column(length = 255)
private String email;  // Валидация на уровне приложения

@NotBlank
@Size(max = 100)
@Column(length = 100)
private String name;   // Максимум 100 символов

Что происходит при переполнении?

// VARCHAR(10)
CREATE TABLE test (
    id SERIAL,
    value VARCHAR(10)
);

// ❌ Это вызовет ошибку
INSERT INTO test (value) VALUES ('12345678901');  -- 11 символов
// ERROR: value too long for type character varying(10)

// ✅ Это работает
INSERT INTO test (value) VALUES ('1234567890');   -- 10 символов

// ✅ Это тоже работает (меньше)
INSERT INTO test (value) VALUES ('12345');        -- 5 символов

В Java:

@Column(length = 10)
private String value;

public void insertData() {
    User user = new User();
    user.setValue("12345678901");  // 11 символов!
    userRepository.save(user);     // ← DataIntegrityViolationException!
}

Практические рекомендации по выбору длины

-- Email: RFC 5321 говорит максимум 254 символов
-- На практике: 255 (распространённый лимит)
CREATE TABLE users (
    email VARCHAR(255) NOT NULL UNIQUE
);

-- Имена: зависит от языка
-- Англ: 20-50 символов
-- Китайский: может быть 1-2 символа
-- На практике: 100 хватает для 99% случаев
CREATE TABLE users (
    first_name VARCHAR(50),  -- John
    last_name VARCHAR(50),   -- Smith
    full_name VARCHAR(100)   -- John Smith
);

-- URL: могут быть длинными
-- На практике: 2000 символов хватает для большинства
CREATE TABLE posts (
    url VARCHAR(2000)
);

-- Телефон: +1 (555) 123-4567
-- На практике: 20 хватает
CREATE TABLE users (
    phone VARCHAR(20)
);

-- Статус/Enum: фиксированный список
CREATE TABLE orders (
    status VARCHAR(20)  -- PENDING, PROCESSING, COMPLETED, FAILED
);

-- Пароль (хэш): SHA-256 = 64 hex символа
CREATE TABLE users (
    password_hash VARCHAR(255)  -- Запас на bcrypt и др.
);

Сравнение: VARCHAR(n) vs TEXT

-- VARCHAR(255)
CREATE TABLE posts_varchar (
    title VARCHAR(255)
);
-- Pros: ясная граница, валидация на БД
-- Cons: может быть слишком коротко

-- TEXT
CREATE TABLE posts_text (
    title TEXT
);
-- Pros: неограниченная длина
-- Cons: может быть слишком большой, медленнее индексирование

-- В PostgreSQL оба хранят одинаково эффективно!
-- Разница минимальна
INDEX post_title_varchar ON posts_varchar(title);  -- OK
INDEX post_title_text ON posts_text(title);        -- OK

Проблемы с очень короткими VARCHAR

// ❌ Слишком коротко
@Column(length = 10)
private String username;  // "john_smith" = 10 символов, а если 11?

// ✅ Разумно
@Column(length = 50)
private String username;  // Места хватит для большинства

// ❌ Слишком коротко
@Column(length = 5)
private String countryCode;  // Только для 4-символьных кодов

// ✅ Правильно
@Column(length = 10)
private String countryCode;  // ISO 3166-1 alpha-2 = 2 символа

Проблемы с очень длинными VARCHAR

-- ❌ Неправильно: для простого текста
CREATE TABLE users (
    email VARCHAR(1000)  -- Зачем 1000 для email?
);

-- ✅ Правильно: для действительно больших данных
CREATE TABLE articles (
    content TEXT  -- Статья может быть 10000+ символов
);

-- VARCHAR(1000) занимает столько же места, что и TEXT для малых данных
-- Но при индексировании может быть неэффективно
CREATE INDEX idx_email ON users(email);     -- OK для VARCHAR(255)
CREATE INDEX idx_content ON articles(content(100));  -- TEXT требует спецификации префикса

Оптимизация в PostgreSQL

-- PostgreSQL хранит VARCHAR(n) эффективно:
-- Если текст меньше page size (8KB), то всё локально
-- Если текст больше, идёт в TOAST (The Oversized-Attribute Storage Technique)

CREATE TABLE documents (
    id SERIAL PRIMARY KEY,
    title VARCHAR(255),     -- На основной page
    content VARCHAR(10000)  -- Если > 8KB, идёт в TOAST
);

-- Сжатие для TOAST
ALTER TABLE documents ALTER COLUMN content SET STORAGE EXTENDED;
-- EXTENDED: сжимать и вкусно
-- MAIN: сначала сжимать, потом TOAST
-- EXTERNAL: никогда не сжимать
-- PLAIN: никогда не сжимать и не TOAST

Реальные примеры схем

-- E-Commerce
CREATE TABLE products (
    id BIGSERIAL PRIMARY KEY,
    name VARCHAR(255) NOT NULL,           -- Название продукта
    sku VARCHAR(50) NOT NULL UNIQUE,      -- Stock Keeping Unit
    description TEXT,                     -- Полное описание
    manufacturer_code VARCHAR(100),       -- Код производителя
    category_code VARCHAR(20)             -- Категория
);

-- Социальная сеть
CREATE TABLE posts (
    id BIGSERIAL PRIMARY KEY,
    author_id BIGINT NOT NULL,
    title VARCHAR(255),                  -- Заголовок
    content TEXT NOT NULL,               -- Основной контент
    tags TEXT,                           -- JSON или CSV
    hashtags VARCHAR(1000)               -- #programming #java
);

-- Система тикетов
CREATE TABLE tickets (
    id UUID PRIMARY KEY,
    issue_key VARCHAR(20) UNIQUE,        -- PROJ-123
    summary VARCHAR(255) NOT NULL,       -- Краткое описание
    description TEXT,                    -- Полное описание
    status VARCHAR(20) NOT NULL,         -- OPEN, IN_PROGRESS, CLOSED
    priority VARCHAR(20) NOT NULL,       -- LOW, MEDIUM, HIGH, CRITICAL
    assignee_email VARCHAR(255),         -- user@example.com
    labels VARCHAR(500)                  -- tag1,tag2,tag3
);

Миграция между VARCHAR типами

-- Была VARCHAR(100), нужна VARCHAR(200)
ALTER TABLE users
ALTER COLUMN name TYPE VARCHAR(200);
-- Fast! Нет перезаписи данных

-- Была VARCHAR(1000), нужна VARCHAR(100)
ALTER TABLE users
ALTER COLUMN name TYPE VARCHAR(100);
-- МЕДЛЕННО! Может быть ошибка если есть data > 100
ALTER TABLE users
ALTER COLUMN name TYPE VARCHAR(100) USING name;  -- Явно указываем

-- Была VARCHAR(100), нужна TEXT
ALTER TABLE users
ALTER COLUMN name TYPE TEXT;
-- Очень быстро (совместимые типы)

Вывод

  • VARCHAR(n) хранит ДО n символов
  • PostgreSQL эффективно использует пространство (переменная длина)
  • Выбирай разумные лимиты:
    • Email: 255
    • Имя: 100
    • Опис: TEXT
    • Статус: 20
    • URL: 2000
  • VARCHAR vs TEXT: в PostgreSQL почти нет разницы в производительности
  • Всегда валидируй на уровне приложения (Java annotations)
  • Лучше переполнить VARCHAR, чем потом менять схему