CHECK - умова, яку має задовольняти кожен рядок. База перевіряє її при кожній вставці й оновленні.
CREATE TABLE products (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
price DECIMAL(10, 2) NOT NULL,
discount_price DECIMAL(10, 2),
stock INT NOT NULL,
CONSTRAINT price_positive CHECK (price > 0),
CONSTRAINT stock_not_negative CHECK (stock >= 0),
CONSTRAINT discount_below_price CHECK (discount_price IS NULL OR discount_price < price)
);
INSERT INTO products (price, stock) VALUES (-5, 10);
-- ERROR 3819: Check constraint 'price_positive' is violated.
Важлива історія версій: до MySQL 8.0.16 синтаксис CHECK приймався, але ігнорувався - обмеження не перевірялися зовсім. Код, написаний для старих версій з «перевірками», насправді нічого не перевіряв. З 8.0.16 обмеження працюють.
Що вміє CHECK:
- умови на значення однієї колонки і на кілька колонок того самого рядка;
NOT ENFORCED- оголосити, але не перевіряти (для поступового впровадження).
Обмеження:
- Лише в межах рядка: не можна посилатися на інші рядки чи таблиці («сума залишків не більше ліміту складу») і використовувати недетерміновані функції (
NOW(),RAND()) чи підзапити. - Колонки з
AUTO_INCREMENTуCHECKне беруть участі. - Додавання до наявної таблиці перевіряє всі рядки: якщо хоч один порушує умову,
ALTER TABLEне виконається - спершу виправити дані.
Навіщо, якщо є валідація в застосунку: застосунок - не єдине джерело змін (консоль, міграції, імпорт, інший сервіс, баг у коді). CHECK - остання лінія захисту цілісності, а від'ємний залишок на складі - це завжди баг, хай би звідки він прийшов.
У Laravel-міграціях окремого методу для CHECK немає - додають сирим SQL через DB::statement('ALTER TABLE ... ADD CONSTRAINT ... CHECK (...)').