Как создать уникальные индексы в Doctrine и MySQL: Полное руководство

В этой статье мы разберем, как создавать уникальные индексы в Doctrine (для Symfony) и MySQL. Вы узнаете:

  • Как определять уникальные поля через Doctrine ORM
  • Особенности работы UNIQUE-индексов с NULL-значениями
  • Как создавать составные уникальные индексы
  • Различия между MySQL и другими СУБД

1. Создание уникальных индексов в Doctrine

1.1. Через атрибуты (PHP 8+)

use Doctrine\ORM\Mapping as ORM;

#[ORM\Entity]
#[ORM\Table(name: 'users')]
#[ORM\UniqueConstraint(name: 'unique_email', columns: ['email'])]
class User
{
    #[ORM\Id]
    #[ORM\GeneratedValue]
    #[ORM\Column]
    private ?int $id = null;

    #[ORM\Column(type: 'string', length: 255, unique: true)]
    private string $email;
}

1.2. Через YAML-конфигурацию

App\Entity\User:
  type: entity
  table: users
  uniqueConstraints:
    unique_email:
      columns: [email]
  fields:
    email:
      type: string
      unique: true

1.3. Составные уникальные индексы

#[ORM\Entity]
#[ORM\Table(uniqueConstraints: [
    new ORM\UniqueConstraint(
        name: 'unique_user_product', 
        columns: ['user_id', 'product_id']
    )
])]
class CartItem
{
    #[ORM\ManyToOne(targetEntity: User::class)]
    private User $user;

    #[ORM\ManyToOne(targetEntity: Product::class)]
    private Product $product;
}

2. Особенности работы с NULL в MySQL

2.1. Поведение NULL в UNIQUE-индексах

CREATE TABLE users (
    email VARCHAR(255) UNIQUE
);

INSERT INTO users (email) VALUES (NULL), (NULL); -- Разрешено в MySQL

2.2. Как запретить дубликаты NULL

Вариант 1: Использовать NOT NULL

CREATE TABLE users (
    email VARCHAR(255) NOT NULL DEFAULT '',
    UNIQUE (email)
);

Вариант 2: Триггер для проверки

DELIMITER //
CREATE TRIGGER prevent_null_duplicates
BEFORE INSERT ON users
FOR EACH ROW
BEGIN
    IF NEW.email IS NULL AND EXISTS (
        SELECT 1 FROM users WHERE email IS NULL
    ) THEN
        SIGNAL SQLSTATE '45000' 
        SET MESSAGE_TEXT = 'Duplicate NULL values not allowed';
    END IF;
END//
DELIMITER ;

Минус триггера, дополнительная нагрузка на БД и время выполнения запроса.

3. Различия между СУБД

СУБДПоведение NULL в UNIQUE
MySQLРазрешает несколько NULL
PostgreSQLРазрешает только один NULL

4. Проверка индексов

В Doctrine:

php bin/console doctrine:schema:validate

В MySQL:

SHOW INDEX FROM users WHERE Non_unique = 0;

Заключение

  • Используйте unique: true или UniqueConstraint в Doctrine
  • Помните о различиях в обработке NULL разными СУБД
  • Для строгой уникальности заменяйте NULL на пустые строки
  • Составные индексы работают по-разному в MySQL 8.0+

Совет: Всегда проверяйте поведение UNIQUE-индексов в вашей версии СУБД перед развертыванием в production.

Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *