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

Можно ли вставить null в таблицу с Primary Key?

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

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

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

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

NULL в Primary Key

Краткий ответ: НЕТ, невозможно вставить NULL в столбец, являющийся Primary Key.

Это одно из фундаментальных правил реляционных баз данных, закреплённое в стандарте SQL.

Почему PRIMARY KEY не может быть NULL?

1. Primary Key автоматически получает ограничения

-- Когда ты определяешь PRIMARY KEY
CREATE TABLE users (
    user_id INT PRIMARY KEY,  -- Автоматически NOT NULL + UNIQUE
    name VARCHAR(100)
);

-- Эквивалентно
CREATE TABLE users (
    user_id INT NOT NULL UNIQUE,  -- PRIMARY KEY = NOT NULL + UNIQUE
    name VARCHAR(100)
);

PRIMARY KEY включает два ограничения:

  • NOT NULL — столбец не может быть пустым
  • UNIQUE — все значения уникальны

2. Попытка вставить NULL

-- ❌ ОШИБКА: NULL не допускается в PRIMARY KEY
INSERT INTO users (user_id, name) VALUES (NULL, 'John');
-- ERROR: NOT NULL constraint failed: users.user_id

-- ❌ ОШИБКА: Даже если не упомянуть column
INSERT INTO users (name) VALUES ('Jane');
-- ERROR: NOT NULL constraint failed: users.user_id

-- ✅ ПРАВИЛЬНО: нужно указать значение
INSERT INTO users (user_id, name) VALUES (1, 'Bob');

Что произойдёт в разных БД

PostgreSQL

CREATE TABLE employees (
    emp_id INT PRIMARY KEY,
    emp_name VARCHAR(100)
);

-- Попытка вставить NULL
INSERT INTO employees (emp_id, emp_name) VALUES (NULL, 'Alice');
-- ERROR: null value in column "emp_id" violates not-null constraint

MySQL

CREATE TABLE products (
    product_id INT PRIMARY KEY,
    product_name VARCHAR(100)
);

-- Попытка вставить NULL
INSERT INTO products (product_id, product_name) VALUES (NULL, 'Laptop');
-- Error Code: 1048. Column 'product_id' cannot be null

SQLite

CREATE TABLE items (
    item_id INTEGER PRIMARY KEY,
    item_name TEXT
);

-- Попытка вставить NULL
INSERT INTO items (item_id, item_name) VALUES (NULL, 'Book');
-- Error: NOT NULL constraint failed: items.item_id

SQL Server

CREATE TABLE customers (
    customer_id INT PRIMARY KEY,
    customer_name VARCHAR(100)
);

-- Попытка вставить NULL
INSERT INTO customers (customer_id, customer_name) VALUES (NULL, 'Smith');
-- Msg 515, Level 16, State 2: 
-- Cannot insert the value NULL into column 'customer_id', table 'db.dbo.customers'

Правильные способы работы с PRIMARY KEY

1. Явно указать значение

INSERT INTO users (user_id, name) VALUES (1, 'John');
INSERT INTO users (user_id, name) VALUES (2, 'Jane');

2. Использовать AUTO INCREMENT / SERIAL

-- PostgreSQL
CREATE TABLE accounts (
    account_id SERIAL PRIMARY KEY,  -- Автоинкремент
    account_name VARCHAR(100)
);

INSERT INTO accounts (account_name) VALUES ('Account A');
-- account_id будет 1 (автоматически)

INSERT INTO accounts (account_name) VALUES ('Account B');
-- account_id будет 2 (автоматически)

-- MySQL
CREATE TABLE posts (
    post_id INT AUTO_INCREMENT PRIMARY KEY,
    post_title VARCHAR(200)
);

INSERT INTO posts (post_title) VALUES ('First Post');
-- post_id = 1 (автоматически)

-- SQLite
CREATE TABLE notes (
    note_id INTEGER PRIMARY KEY AUTOINCREMENT,
    note_content TEXT
);

INSERT INTO notes (note_content) VALUES ('My Note');
-- note_id = 1 (автоматически)

3. Использовать UUID

-- PostgreSQL с UUID
CREATE TABLE users (
    user_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    username VARCHAR(100)
);

INSERT INTO users (username) VALUES ('alice');
-- user_id будет сгенерирован автоматически

-- MySQL с UUID
CREATE TABLE documents (
    doc_id CHAR(36) PRIMARY KEY DEFAULT (UUID()),
    doc_title VARCHAR(200)
);

INSERT INTO documents (doc_title) VALUES ('Document 1');
-- doc_id будет сгенерирован автоматически

Составной PRIMARY KEY

-- Составной PRIMARY KEY (несколько столбцов)
CREATE TABLE user_roles (
    user_id INT,
    role_id INT,
    assigned_date DATE,
    PRIMARY KEY (user_id, role_id)  -- Оба столбца NOT NULL
);

-- ❌ ОШИБКА: user_id не может быть NULL
INSERT INTO user_roles (user_id, role_id) VALUES (NULL, 1);
-- ERROR: NOT NULL constraint failed: user_roles.user_id

-- ❌ ОШИБКА: role_id не может быть NULL
INSERT INTO user_roles (user_id, role_id) VALUES (1, NULL);
-- ERROR: NOT NULL constraint failed: user_roles.role_id

-- ✅ ПРАВИЛЬНО: оба значения указаны
INSERT INTO user_roles (user_id, role_id) VALUES (1, 5);
INSERT INTO user_roles (user_id, role_id) VALUES (1, 7);
INSERT INTO user_roles (user_id, role_id) VALUES (2, 5);

Частые ошибки в Java коде

Ошибка 1: Забыли установить ID

@Entity
@Table(name = "users")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long userId;
    
    private String name;
}

// НЕПРАВИЛЬНО: userId = null
public class UserService {
    public void createUser(String name) {
        User user = new User();
        // userId не установлен!
        user.setName(name);
        
        // ❌ Будет ошибка при сохранении
        userRepository.save(user);
        // ERROR: NULL constraint failed
    }
}

// ПРАВИЛЬНО: используем @GeneratedValue
public class UserService {
    public void createUser(String name) {
        User user = new User();
        user.setName(name);
        
        // ✅ Okay: @GeneratedValue сгенерирует ID
        User saved = userRepository.save(user);
        // userId будет установлен БД автоматически
    }
}

Ошибка 2: Пустой Optional как ID

// ❌ ОПАСНО
public void updateUser(Optional<Long> userId, String name) {
    User user = new User();
    user.setId(userId.orElse(null));  // NULL!
    user.setName(name);
    
    // Будет ошибка
    userRepository.save(user);
}

// ✅ ПРАВИЛЬНО
public void updateUser(Optional<Long> userId, String name) {
    if (userId.isEmpty()) {
        throw new IllegalArgumentException("User ID required");
    }
    
    User user = new User();
    user.setId(userId.get());
    user.setName(name);
    
    userRepository.save(user);
}

Архитектурные решения

Pattern: Surrogate Key (Искусственный ключ)

// Используем auto-increment для PRIMARY KEY
@Entity
public class Product {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;  // Никогда не NULL
    
    @Column(unique = true, nullable = false)
    private String sku;  // Уникальный код товара
    
    private String name;
}

// Использование
public class ProductService {
    public Product create(String sku, String name) {
        Product product = new Product();
        // id будет установлен БД
        product.setSku(sku);
        product.setName(name);
        
        return productRepository.save(product);
    }
}

Pattern: Natural Key с @GeneratedValue

// Когда PRIMARY KEY имеет смысл в бизнесе
@Entity
public class Invoice {
    @Id
    private String invoiceNumber;  // например: "INV-2024-001"
    
    @Column(nullable = false)
    private LocalDate invoiceDate;
    
    private BigDecimal amount;
}

// Нужно явно установить Primary Key
public class InvoiceService {
    public Invoice create(LocalDate date, BigDecimal amount) {
        Invoice invoice = new Invoice();
        invoice.setInvoiceNumber(generateInvoiceNumber());  // ✅ Установить!
        invoice.setInvoiceDate(date);
        invoice.setAmount(amount);
        
        return invoiceRepository.save(invoice);
    }
    
    private String generateInvoiceNumber() {
        return "INV-" + System.currentTimeMillis();
    }
}

Контроль на уровне приложения

// Добавить проверку перед сохранением
@Service
public class UserService {
    public User saveUser(User user) {
        // Проверка на NULL в PRIMARY KEY
        if (user.getId() == null) {
            throw new IllegalArgumentException(
                "User ID cannot be null. " +
                "Use @GeneratedValue for auto-increment or " +
                "set ID manually before saving."
            );
        }
        
        return userRepository.save(user);
    }
}

// Test
@Test
public void testPrimaryKeyValidation() {
    User user = new User();
    user.setName("John");
    // user.setId(null)  — не установлен
    
    // Должна выбросить исключение
    assertThrows(IllegalArgumentException.class, 
        () -> userService.saveUser(user)
    );
}

Заключение

Ответ: НЕТ, нельзя вставить NULL в столбец PRIMARY KEY.

Причины:

  1. PRIMARY KEY автоматически включает ограничение NOT NULL
  2. PRIMARY KEY должен уникально идентифицировать строку
  3. NULL — неполная информация, не может идентифицировать

Решения:

  1. Используй AUTO_INCREMENT / SERIAL для автоинкремента
  2. Используй @GeneratedValue в Hibernate/JPA
  3. Используй UUID для распределённых систем
  4. Явно установи значение перед сохранением
  5. Добавь валидацию на уровне приложения

Золотое правило: PRIMARY KEY никогда не может быть NULL, это нарушение целостности данных.

Можно ли вставить null в таблицу с Primary Key? | PrepBro