BEFORE в MySQL. Введение
BEFORE в MySQL — момент срабатывания триггера до фактической записи данных. С его помощью удобно валидировать вход, нормализовать значения, автозаполнять поля и блокировать недопустимые операции через SIGNAL. Ниже — синтаксис, типовой шаблон и 100 уникальных примеров.
Синтаксис
DELIMITER //
CREATE TRIGGER trg_name
BEFORE INSERT ON table_name
FOR EACH ROW
BEGIN
-- изменяем NEW.* или валидируем
SET NEW.col = IFNULL(NEW.col, 'value');
END;//
DELIMITER ;DELIMITER //
CREATE TRIGGER trg_name_upd
BEFORE UPDATE ON table_name
FOR EACH ROW
BEGIN
-- защита и проверки
IF NEW.status='closed' AND OLD.status<>'closed' THEN
SET NEW.closed_at = NOW();
END IF;
END;//
DELIMITER ;DELIMITER //
CREATE TRIGGER trg_name_del
BEFORE DELETE ON table_name
FOR EACH ROW
BEGIN
-- запрет удаления критичных строк
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='Deletion is not allowed';
END;//
DELIMITER ;DELIMITER //
CREATE TRIGGER trg_lookup
BEFORE INSERT ON orders
FOR EACH ROW
BEGIN
-- пример чтения справочника
SELECT tax_rate
INTO @r
FROM regions
WHERE id = NEW.region_id;
SET NEW.tax_rate = @r;
END;//
DELIMITER ;Типовая конструкция
DELIMITER //
CREATE TRIGGER customers_bi_tier
BEFORE INSERT ON customers
FOR EACH ROW
BEGIN
SET NEW.tier = CASE
WHEN NEW.ltv>=10000 THEN 'vip'
WHEN NEW.ltv>=3000 THEN 'gold'
ELSE 'standard'
END;
END;//
DELIMITER ;100 примеров
1. Нормализация email к нижнему регистру
DELIMITER //
CREATE TRIGGER users_bi_email
BEFORE INSERT ON users
FOR EACH ROW
BEGIN
SET NEW.email = LOWER(NEW.email);
END;//
DELIMITER ;2. Обрезка пробелов в логине
DELIMITER //
CREATE TRIGGER users_bi_trim_login
BEFORE INSERT ON users
FOR EACH ROW
BEGIN
SET NEW.login = TRIM(NEW.login);
END;//
DELIMITER ;3. Дефолт display_name по имени
DELIMITER //
CREATE TRIGGER profiles_bi_display
BEFORE INSERT ON profiles
FOR EACH ROW
BEGIN
IF NEW.display_name IS NULL OR NEW.display_name='' THEN
SET NEW.display_name = NEW.first_name;
END IF;
END;//
DELIMITER ;4. Автозаполнение slug из title
DELIMITER //
CREATE TRIGGER posts_bi_slug
BEFORE INSERT ON posts
FOR EACH ROW
BEGIN
SET NEW.slug = REPLACE(LOWER(NEW.title),' ','-');
END;//
DELIMITER ;5. Гарантия положительной цены
DELIMITER //
CREATE TRIGGER products_bi_price
BEFORE INSERT ON products
FOR EACH ROW
BEGIN
SET NEW.price = GREATEST(NEW.price,0);
END;//
DELIMITER ;6. Обрезка описания до 500 символов
DELIMITER //
CREATE TRIGGER items_bi_desc
BEFORE INSERT ON items
FOR EACH ROW
BEGIN
SET NEW.description = LEFT(NEW.description,500);
END;//
DELIMITER ;7. Флаг активности по дате публикации
DELIMITER //
CREATE TRIGGER posts_bi_active
BEFORE INSERT ON posts
FOR EACH ROW
BEGIN
SET NEW.is_active = (NEW.published_at<=NOW());
END;//
DELIMITER ;8. Нормализация телефона (только цифры)
DELIMITER //
CREATE TRIGGER contacts_bi_phone
BEFORE INSERT ON contacts
FOR EACH ROW
BEGIN
SET NEW.phone = REGEXP_REPLACE(NEW.phone,'[^0-9]','');
END;//
DELIMITER ;9. Чистка тегов: в нижний регистр
DELIMITER //
CREATE TRIGGER tags_bi_norm
BEFORE INSERT ON tags
FOR EACH ROW
BEGIN
SET NEW.name = LOWER(NEW.name);
END;//
DELIMITER ;10. Установка created_at
DELIMITER //
CREATE TRIGGER any_bi_created
BEFORE INSERT ON any_table
FOR EACH ROW
BEGIN
SET NEW.created_at = COALESCE(NEW.created_at,NOW());
END;//
DELIMITER ;11. Автострана по IP
DELIMITER //
CREATE TRIGGER sessions_bi_geo
BEFORE INSERT ON sessions
FOR EACH ROW
BEGIN
SELECT country
INTO @c
FROM ip2country
WHERE ip = NEW.ip;
SET NEW.country = @c;
END;//
DELIMITER ;12. Заполнение валюты заказа по региону
DELIMITER //
CREATE TRIGGER orders_bi_currency
BEFORE INSERT ON orders
FOR EACH ROW
BEGIN
SELECT currency
INTO @cur
FROM regions
WHERE id = NEW.region_id;
SET NEW.currency = @cur;
END;//
DELIMITER ;13. Склейка ФИО из частей
DELIMITER //
CREATE TRIGGER profiles_bi_fullname
BEFORE INSERT ON profiles
FOR EACH ROW
BEGIN
SET NEW.full_name = CONCAT_WS(' ', NEW.last_name, NEW.first_name, NEW.middle_name);
END;//
DELIMITER ;14. Гарантия минимального количества
DELIMITER //
CREATE TRIGGER stock_bi_minqty
BEFORE INSERT ON stock
FOR EACH ROW
BEGIN
SET NEW.qty = GREATEST(NEW.qty,0);
END;//
DELIMITER ;15. Автовыбор ставки налога
DELIMITER //
CREATE TRIGGER invoices_bi_tax
BEFORE INSERT ON invoices
FOR EACH ROW
BEGIN
SELECT rate
INTO @r
FROM tax_rules
WHERE country=NEW.country;
SET NEW.tax_rate = COALESCE(@r,0);
END;//
DELIMITER ;16. Нормализация username (только латиница и _)
DELIMITER //
CREATE TRIGGER users_bi_username
BEFORE INSERT ON users
FOR EACH ROW
BEGIN
SET NEW.username = REGEXP_REPLACE(LOWER(NEW.username),'[^a-z0-9_]','');
END;//
DELIMITER ;17. Дефолт timezone=UTC
DELIMITER //
CREATE TRIGGER profiles_bi_tz
BEFORE INSERT ON profiles
FOR EACH ROW
BEGIN
SET NEW.tz = COALESCE(NEW.tz,'UTC');
END;//
DELIMITER ;18. Схлопывание пробелов в названии
DELIMITER //
CREATE TRIGGER products_bi_spaces
BEFORE INSERT ON products
FOR EACH ROW
BEGIN
SET NEW.name = REGEXP_REPLACE(TRIM(NEW.name),'[ ]+',' ');
END;//
DELIMITER ;19. Заполнение короткого описания
DELIMITER //
CREATE TRIGGER posts_bi_excerpt
BEFORE INSERT ON posts
FOR EACH ROW
BEGIN
SET NEW.excerpt = COALESCE(NEW.excerpt, LEFT(NEW.body,160));
END;//
DELIMITER ;20. Гарантия уникального slug (хвост‑счётчик)
DELIMITER //
CREATE TRIGGER posts_bi_slug_unique
BEFORE INSERT ON posts
FOR EACH ROW
BEGIN
DECLARE i INT DEFAULT 0;
DECLARE base VARCHAR(255);
SET base = REPLACE(LOWER(NEW.title),' ','-');
WHILE EXISTS(
SELECT 1
FROM posts
WHERE slug = IF(i=0, base, CONCAT(base,'-',i))
) DO
SET i = i + 1;
END WHILE;
SET NEW.slug = IF(i=0, base, CONCAT(base,'-',i));
END;//
DELIMITER ;21. Заполнение warehouse_id по адресу
DELIMITER //
CREATE TRIGGER shipments_bi_wh
BEFORE INSERT ON shipments
FOR EACH ROW
BEGIN
SELECT id
INTO @w
FROM warehouses
WHERE city=NEW.city
ORDER BY capacity DESC
LIMIT 1;
SET NEW.warehouse_id = @w;
END;//
DELIMITER ;22. Капитализация имени собственным правилом
DELIMITER //
CREATE TRIGGER profiles_bi_caps
BEFORE INSERT ON profiles
FOR EACH ROW
BEGIN
SET NEW.first_name = CONCAT(UPPER(LEFT(NEW.first_name,1)), LOWER(SUBSTRING(NEW.first_name,2)));
END;//
DELIMITER ;23. Нормализация URL (без хвоста /)
DELIMITER //
CREATE TRIGGER links_bi_norm
BEFORE INSERT ON links
FOR EACH ROW
BEGIN
SET NEW.url = REGEXP_REPLACE(NEW.url,'/$','');
END;//
DELIMITER ;24. Отсечение будущей даты рождения
DELIMITER //
CREATE TRIGGER profiles_bi_birth
BEFORE INSERT ON profiles
FOR EACH ROW
BEGIN
IF NEW.birthdate>CURDATE() THEN
SET NEW.birthdate = CURDATE();
END IF;
END;//
DELIMITER ;25. Автоперевод статуса при total=0
DELIMITER //
CREATE TRIGGER orders_bi_status
BEFORE INSERT ON orders
FOR EACH ROW
BEGIN
IF NEW.total=0 THEN
SET NEW.status='free';
END IF;
END;//
DELIMITER ;Ещё примеры
26. Запрет отрицательной цены
DELIMITER //
CREATE TRIGGER products_bi_price_check
BEFORE INSERT ON products
FOR EACH ROW
BEGIN
IF NEW.price<0 THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='Price must be non-negative';
END IF;
END;//
DELIMITER ;27. Уникальность email по таблице
DELIMITER //
CREATE TRIGGER users_bi_unique_email
BEFORE INSERT ON users
FOR EACH ROW
BEGIN
IF EXISTS(
SELECT 1
FROM users
WHERE email = NEW.email
) THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='Email already exists';
END IF;
END);//
DELIMITER ;28. Проверка длины пароля ≥ 8
DELIMITER //
CREATE TRIGGER users_bi_pwd_len
BEFORE INSERT ON users
FOR EACH ROW
BEGIN
IF CHAR_LENGTH(NEW.password_hash)<8 THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='Password too short';
END IF;
END;//
DELIMITER ;29. Запрет регистрации без согласия с правилами
DELIMITER //
CREATE TRIGGER users_bi_terms
BEFORE INSERT ON users
FOR EACH ROW
BEGIN
IF NEW.accepted_terms<>1 THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='Terms must be accepted';
END IF;
END;//
DELIMITER ;30. Проверка доступности товара на складе при заказе
DELIMITER //
CREATE TRIGGER order_items_bi_stock
BEFORE INSERT ON order_items
FOR EACH ROW
BEGIN
SELECT qty
INTO @q
FROM stock
WHERE product_id=NEW.product_id;
IF COALESCE(@q,0) < NEW.qty THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='Insufficient stock';
END IF;
END;//
DELIMITER ;31. Запрет удаления админ‑пользователя
DELIMITER //
CREATE TRIGGER users_bd_protect_admin
BEFORE DELETE ON users
FOR EACH ROW
BEGIN
IF OLD.role='admin' THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='Cannot delete admin';
END IF;
END;//
DELIMITER ;32. Контроль формата телефона
DELIMITER //
CREATE TRIGGER contacts_bi_phone_check
BEFORE INSERT ON contacts
FOR EACH ROW
BEGIN
IF NEW.phone NOT REGEXP '^[0-9]{10,15}$' THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='Invalid phone';
END IF;
END;//
DELIMITER ;33. Ограничение скидки ≤ 50%
DELIMITER //
CREATE TRIGGER orders_bi_discount_cap
BEFORE INSERT ON orders
FOR EACH ROW
BEGIN
IF NEW.discount>0.5 THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='Discount too high';
END IF;
END;//
DELIMITER ;34. Запрет отрицательного остатка при UPDATE
DELIMITER //
CREATE TRIGGER stock_bu_nonneg
BEFORE UPDATE ON stock
FOR EACH ROW
BEGIN
IF NEW.qty<0 THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='Negative qty not allowed';
END IF;
END;//
DELIMITER ;35. Проверка возраста ≥ 18
DELIMITER //
CREATE TRIGGER profiles_bi_age
BEFORE INSERT ON profiles
FOR EACH ROW
BEGIN
IF TIMESTAMPDIFF(YEAR, NEW.birthdate, CURDATE()) < 18 THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='Age must be 18+';
END IF;
END;//
DELIMITER ;36. Запрет смены primary email на пустой
DELIMITER //
CREATE TRIGGER users_bu_email_not_null
BEFORE UPDATE ON users
FOR EACH ROW
BEGIN
IF NEW.email IS NULL OR NEW.email='' THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='Email is required';
END IF;
END;//
DELIMITER ;37. Проверка ссылочной целостности без FK
DELIMITER //
CREATE TRIGGER orders_bi_customer_exists
BEFORE INSERT ON orders
FOR EACH ROW
BEGIN
IF NOT EXISTS(
SELECT 1
FROM customers
WHERE id = NEW.customer_id
) THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='Customer not found';
END IF;
END;//
DELIMITER ;38. Запрет уменьшать сумму выплаченного платежа
DELIMITER //
CREATE TRIGGER payments_bu_no_decrease
BEFORE UPDATE ON payments
FOR EACH ROW
BEGIN
IF NEW.amount < OLD.amount THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='Amount decrease forbidden';
END IF;
END;//
DELIMITER ;39. Блокировка удаления оплаченного счета
DELIMITER //
CREATE TRIGGER invoices_bd_paid_protect
BEFORE DELETE ON invoices
FOR EACH ROW
BEGIN
IF OLD.status='paid' THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='Cannot delete paid invoice';
END IF;
END;//
DELIMITER ;40. Согласованность валюты строки заказа с шапкой
DELIMITER //
CREATE TRIGGER order_items_bi_currency_match
BEFORE INSERT ON order_items
FOR EACH ROW
BEGIN
SELECT currency
INTO @c
FROM orders
WHERE id = NEW.order_id;
IF @c<>NEW.currency THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='Currency mismatch';
END IF;
END;//
DELIMITER ;41. Запрет изменения readonly поля
DELIMITER //
CREATE TRIGGER users_bu_readonly_created
BEFORE UPDATE ON users
FOR EACH ROW
BEGIN
IF NEW.created_at<>OLD.created_at THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='created_at is readonly';
END IF;
END;//
DELIMITER ;42. Проверка лимита позиций в заказе
DELIMITER //
CREATE TRIGGER order_items_bi_limit
BEFORE INSERT ON order_items
FOR EACH ROW
BEGIN
SELECT COUNT(*)
INTO @n
FROM order_items
WHERE order_id=NEW.order_id;
IF @n>=100 THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='Too many items';
END IF;
END;//
DELIMITER ;43. Запрет повторной подписки активному клиенту
DELIMITER //
CREATE TRIGGER subs_bi_no_dup
BEFORE INSERT ON subscriptions
FOR EACH ROW
BEGIN
IF EXISTS(
SELECT 1
FROM subscriptions
WHERE user_id=NEW.user_id AND status='active'
) THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='Already subscribed';
END IF;
END;//
DELIMITER ;44. Блокировка смены owner у проекта
DELIMITER //
CREATE TRIGGER projects_bu_lock_owner
BEFORE UPDATE ON projects
FOR EACH ROW
BEGIN
IF NEW.owner_id<>OLD.owner_id THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='Owner change forbidden';
END IF;
END;//
DELIMITER ;45. Порог минимальной суммы заказа
DELIMITER //
CREATE TRIGGER orders_bi_min_total
BEFORE INSERT ON orders
FOR EACH ROW
BEGIN
IF NEW.total<10 THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='Order total too low';
END IF;
END;//
DELIMITER ;Ещё примеры
46. Установка updated_at
DELIMITER //
CREATE TRIGGER any_bu_updated
BEFORE UPDATE ON any_table
FOR EACH ROW
BEGIN
SET NEW.updated_at = NOW();
END;//
DELIMITER ;47. Пересчёт поля total перед INSERT
DELIMITER //
CREATE TRIGGER orders_bi_total
BEFORE INSERT ON orders
FOR EACH ROW
BEGIN
SET NEW.total = COALESCE(NEW.subtotal,0) - COALESCE(NEW.discount,0) + COALESCE(NEW.shipping,0);
END;//
DELIMITER ;48. Расчёт НДС от суммы
DELIMITER //
CREATE TRIGGER invoices_bi_vat
BEFORE INSERT ON invoices
FOR EACH ROW
BEGIN
SET NEW.vat = ROUND(NEW.amount * NEW.tax_rate, 2);
END;//
DELIMITER ;49. Заполнение checksum строки
DELIMITER //
CREATE TRIGGER rows_bi_checksum
BEFORE INSERT ON rows
FOR EACH ROW
BEGIN
SET NEW.checksum = MD5(CONCAT_WS('#',NEW.c1,NEW.c2,NEW.c3));
END;//
DELIMITER ;50. Автогенерация referral_code
DELIMITER //
CREATE TRIGGER users_bi_ref
BEFORE INSERT ON users
FOR EACH ROW
BEGIN
SET NEW.referral_code = SUBSTRING(REPLACE(UUID(),'-',''),1,10);
END;//
DELIMITER ;51. Пре‑расчёт search_index
DELIMITER //
CREATE TRIGGER posts_bi_search
BEFORE INSERT ON posts
FOR EACH ROW
BEGIN
SET NEW.search_index = CONCAT(LOWER(NEW.title),' ',LOWER(NEW.body));
END;//
DELIMITER ;52. Подбор сегмента клиента по ARPU
DELIMITER //
CREATE TRIGGER customers_bu_segment
BEFORE UPDATE ON customers
FOR EACH ROW
BEGIN
SET NEW.segment = CASE
WHEN NEW.arpu>=100 THEN 'pro'
WHEN NEW.arpu>=20 THEN 'plus'
ELSE 'free'
END;
END;//
DELIMITER ;53. Расчёт due_at от created_at и sla_hours
DELIMITER //
CREATE TRIGGER tickets_bi_due
BEFORE INSERT ON tickets
FOR EACH ROW
BEGIN
SET NEW.due_at = DATE_ADD(COALESCE(NEW.created_at,NOW()), INTERVAL NEW.sla_hours HOUR);
END;//
DELIMITER ;54. Геохеш по широте/долготе
DELIMITER //
CREATE TRIGGER places_bi_geohash
BEFORE INSERT ON places
FOR EACH ROW
BEGIN
SET NEW.geohash = ST_AsText(POINT(NEW.lon, NEW.lat));
END;//
DELIMITER ;55. Формирование display_address
DELIMITER //
CREATE TRIGGER addresses_bi_display
BEFORE INSERT ON addresses
FOR EACH ROW
BEGIN
SET NEW.display = CONCAT_WS(', ', NEW.street, NEW.city, NEW.country);
END;//
DELIMITER ;56. Счётчик слов в статье
DELIMITER //
CREATE TRIGGER posts_bi_wc
BEFORE INSERT ON posts
FOR EACH ROW
BEGIN
SET NEW.word_count = LENGTH(NEW.body) - LENGTH(REPLACE(NEW.body,' ','')) + 1;
END;//
DELIMITER ;57. Проставить shipped=1 при наличии tracking
DELIMITER //
CREATE TRIGGER orders_bu_shipped
BEFORE UPDATE ON orders
FOR EACH ROW
BEGIN
IF NEW.tracking IS NOT NULL AND NEW.tracking<>'' THEN
SET NEW.shipped=1;
END IF;
END;//
DELIMITER ;58. Определить приоритет тикета по ключевым словам
DELIMITER //
CREATE TRIGGER tickets_bi_prio
BEFORE INSERT ON tickets
FOR EACH ROW
BEGIN
SET NEW.priority = CASE
WHEN NEW.title LIKE '%urgent%' OR NEW.body LIKE '%urgent%' THEN 'P1'
WHEN NEW.title LIKE '%high%' OR NEW.body LIKE '%high%' THEN 'P2'
ELSE 'P3'
END;
END;//
DELIMITER ;59. Расчёт бонусов = 5% от суммы заказа
DELIMITER //
CREATE TRIGGER orders_bi_bonus
BEFORE INSERT ON orders
FOR EACH ROW
BEGIN
SET NEW.bonus_points = ROUND(NEW.total*0.05);
END;//
DELIMITER ;60. Приведение значения сенсора к диапазону
DELIMITER //
CREATE TRIGGER sensors_bi_norm
BEFORE INSERT ON sensors
FOR EACH ROW
BEGIN
SET NEW.value = LEAST(GREATEST(NEW.value, NEW.min_value), NEW.max_value);
END;//
DELIMITER ;61. Синхронизация поля search_email
DELIMITER //
CREATE TRIGGER users_bu_search_email
BEFORE UPDATE ON users
FOR EACH ROW
BEGIN
SET NEW.search_email = LOWER(NEW.email);
END;//
DELIMITER ;62. Расклейка тегов в поле keywords
DELIMITER //
CREATE TRIGGER posts_bi_keywords
BEFORE INSERT ON posts
FOR EACH ROW
BEGIN
SET NEW.keywords = REPLACE(LOWER(NEW.tags), ';', ',');
END;//
DELIMITER ;63. Выбор валюты счета по клиенту
DELIMITER //
CREATE TRIGGER invoices_bi_currency
BEFORE INSERT ON invoices
FOR EACH ROW
BEGIN
SELECT currency
INTO @c
FROM customers
WHERE id=NEW.customer_id;
SET NEW.currency = @c;
END;//
DELIMITER ;64. Заполнение owner_id из auth_context
DELIMITER //
CREATE TRIGGER projects_bi_owner
BEFORE INSERT ON projects
FOR EACH ROW
BEGIN
SET NEW.owner_id = COALESCE(NEW.owner_id, @current_user_id);
END;//
DELIMITER ;65. Авторасчёт периода подписки
DELIMITER //
CREATE TRIGGER subs_bi_period
BEFORE INSERT ON subscriptions
FOR EACH ROW
BEGIN
SET NEW.valid_till = DATE_ADD(COALESCE(NEW.valid_from, CURDATE()), INTERVAL NEW.months MONTH);
END;//
DELIMITER ;66. Формирование канонического домена
DELIMITER //
CREATE TRIGGER sites_bi_canon
BEFORE INSERT ON sites
FOR EACH ROW
BEGIN
SET NEW.canonical = LOWER(REGEXP_REPLACE(NEW.host,'^www\.',''));
END;//
DELIMITER ;67. Предзаполнение поля initials
DELIMITER //
CREATE TRIGGER profiles_bi_initials
BEFORE INSERT ON profiles
FOR EACH ROW
BEGIN
SET NEW.initials = CONCAT(LEFT(NEW.first_name,1), LEFT(NEW.last_name,1));
END;//
DELIMITER ;68. Расчёт rating_weight из reviews_count
DELIMITER //
CREATE TRIGGER products_bu_weight
BEFORE UPDATE ON products
FOR EACH ROW
BEGIN
SET NEW.rating_weight = LOG10(1+NEW.reviews_count);
END;//
DELIMITER ;69. Расчёт unit_price из total/qty
DELIMITER //
CREATE TRIGGER order_items_bi_unit_price
BEFORE INSERT ON order_items
FOR EACH ROW
BEGIN
SET NEW.unit_price = NEW.total/NULLIF(NEW.qty,0);
END;//
DELIMITER ;70. Расчёт checksum адреса доставки
DELIMITER //
CREATE TRIGGER orders_bi_addr_crc
BEFORE INSERT ON orders
FOR EACH ROW
BEGIN
SET NEW.addr_crc = MD5(CONCAT_WS('|',NEW.country,NEW.city,NEW.street,NEW.zip));
END;//
DELIMITER ;Ещё примеры
71. Автозаполнение language по Accept-Language
DELIMITER //
CREATE TRIGGER sessions_bi_lang
BEFORE INSERT ON sessions
FOR EACH ROW
BEGIN
SET NEW.lang = SUBSTRING_INDEX(NEW.accept_language,',',1);
END;//
DELIMITER ;72. Запрет по времени суток (окно обслуживания)
DELIMITER //
CREATE TRIGGER maintenance_bd
BEFORE DELETE ON critical_table
FOR EACH ROW
BEGIN
IF HOUR(NOW()) BETWEEN 2 AND 4 THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='Maintenance window';
END IF;
END;//
DELIMITER ;73. Блокировка UPDATE закрытых тикетов
DELIMITER //
CREATE TRIGGER tickets_bu_closed
BEFORE UPDATE ON tickets
FOR EACH ROW
BEGIN
IF OLD.status='closed' THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='Ticket is closed';
END IF;
END;//
DELIMITER ;74. Нельзя понижать роль администратора
DELIMITER //
CREATE TRIGGER users_bu_role_guard
BEFORE UPDATE ON users
FOR EACH ROW
BEGIN
IF OLD.role='admin' AND NEW.role<>'admin' THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='Cannot demote admin';
END IF;
END;//
DELIMITER ;75. Запрет смены customer_id у заказа
DELIMITER //
CREATE TRIGGER orders_bu_lock_customer
BEFORE UPDATE ON orders
FOR EACH ROW
BEGIN
IF NEW.customer_id<>OLD.customer_id THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='customer_id is immutable';
END IF;
END;//
DELIMITER ;76. Поле paid_at только при paid=1
DELIMITER //
CREATE TRIGGER orders_bu_paid_at
BEFORE UPDATE ON orders
FOR EACH ROW
BEGIN
IF NEW.paid=1 AND OLD.paid=0 THEN
SET NEW.paid_at = NOW();
ELSEIF NEW.paid=0 THEN
SET NEW.paid_at = NULL;
END IF;
END;//
DELIMITER ;77. Лимит частоты изменений профиля
DELIMITER //
CREATE TRIGGER profiles_bu_rate_limit
BEFORE UPDATE ON profiles
FOR EACH ROW
BEGIN
IF TIMESTAMPDIFF(MINUTE, OLD.updated_at, NOW())<1 THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='Too frequent updates';
END IF;
END;//
DELIMITER ;78. Запрет DELETE для сотрудников с активными задачами
DELIMITER //
CREATE TRIGGER employees_bd_tasks
BEFORE DELETE ON employees
FOR EACH ROW
BEGIN
IF EXISTS(
SELECT 1
FROM tasks
WHERE assignee_id=OLD.id
) THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='Has tasks';
END IF;
END;//
DELIMITER ;79. Обязательный country для клиентов EU
DELIMITER //
CREATE TRIGGER customers_bi_country
BEFORE INSERT ON customers
FOR EACH ROW
BEGIN
IF NEW.region='EU' AND (NEW.country IS NULL OR NEW.country='') THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='Country required for EU';
END IF;
END;//
DELIMITER ;80. Контроль маски email домена
DELIMITER //
CREATE TRIGGER users_bi_corp_email
BEFORE INSERT ON users
FOR EACH ROW
BEGIN
IF NEW.email NOT LIKE '%@company.com' THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='Use @company.com email';
END IF;
END;//
DELIMITER ;81. Запрет удаления транзакций старше 24 часов
DELIMITER //
CREATE TRIGGER tx_bd_retention
BEFORE DELETE ON transactions
FOR EACH ROW
BEGIN
IF TIMESTAMPDIFF(HOUR, OLD.created_at, NOW())>24 THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='Retention policy';
END IF;
END;//
DELIMITER ;82. Логика дат начала/окончания подписки
DELIMITER //
CREATE TRIGGER subs_bi_dates
BEFORE INSERT ON subscriptions
FOR EACH ROW
BEGIN
IF NEW.valid_till<=NEW.valid_from THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='valid_till must be after valid_from';
END IF;
END];//
DELIMITER ;83. Защита от дублей платежа по checksum
DELIMITER //
CREATE TRIGGER payments_bi_dup
BEFORE INSERT ON payments
FOR EACH ROW
BEGIN
SET @crc = MD5(CONCAT_WS(':',NEW.user_id,NEW.amount,NEW.currency,NEW.ext_id));
IF EXISTS(
SELECT 1
FROM payments
WHERE checksum=@crc
) THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='Duplicate payment';
END IF;
SET NEW.checksum = @crc;
END;//
DELIMITER ;84. Запрет обновления цен каталога в выходные
DELIMITER //
CREATE TRIGGER products_bu_weekend_lock
BEFORE UPDATE ON products
FOR EACH ROW
BEGIN
IF WEEKDAY(NOW()) IN (5,6) THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='Weekend lock';
END IF;
END;//
DELIMITER ;85. Проверка уникальности username (case‑insensitive)
DELIMITER //
CREATE TRIGGER users_bi_uname_ci
BEFORE INSERT ON users
FOR EACH ROW
BEGIN
IF EXISTS(
SELECT 1
FROM users
WHERE LOWER(username)=LOWER(NEW.username)
) THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='Username exists';
END IF;
END;//
DELIMITER ;86. Запрет смены валюты счета после оплаты
DELIMITER //
CREATE TRIGGER invoices_bu_currency_lock
BEFORE UPDATE ON invoices
FOR EACH ROW
BEGIN
IF OLD.status='paid' AND NEW.currency<>OLD.currency THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='Currency locked after payment';
END IF;
END];//
DELIMITER ;87. Контроль формата ISO даты
DELIMITER //
CREATE TRIGGER events_bi_date_iso
BEFORE INSERT ON events
FOR EACH ROW
BEGIN
IF NEW.date_txt NOT REGEXP '^[0-9]{4}-[0-9]{2}-[0-9]{2}$' THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='Invalid date format';
END IF;
END;//
DELIMITER ;88. Блокировка удаления аккаунта с балансом
DELIMITER //
CREATE TRIGGER wallets_bd_balance
BEFORE DELETE ON wallets
FOR EACH ROW
BEGIN
IF OLD.balance<>0 THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='Non-zero balance';
END IF;
END];//
DELIMITER ;89. Запрет изменения внешнего ID
DELIMITER //
CREATE TRIGGER ext_bu_lock_external_id
BEFORE UPDATE ON external_map
FOR EACH ROW
BEGIN
IF NEW.external_id<>OLD.external_id THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='external_id immutable';
END IF;
END];//
DELIMITER ;90. Обязательный индекс качества данных
DELIMITER //
CREATE TRIGGER leads_bi_quality
BEFORE INSERT ON leads
FOR EACH ROW
BEGIN
IF COALESCE(NEW.email,'')='' AND COALESCE(NEW.phone,'')='' THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='Email or phone required';
END IF;
END];//
DELIMITER ;Ещё примеры
91. Шаблон: upsert‑нормализация перед INSERT
DELIMITER //
CREATE TRIGGER any_bi_norm_template
BEFORE INSERT ON any_table
FOR EACH ROW
BEGIN
SET NEW.key = LOWER(TRIM(NEW.key));
IF NEW.key='' THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='Key required';
END IF;
END;//
DELIMITER ;92. Шаблон: soft‑delete блок вместо DELETE
DELIMITER //
CREATE TRIGGER any_bd_soft
BEFORE DELETE ON any_table
FOR EACH ROW
BEGIN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='Use soft delete';
END];//
DELIMITER ;93. Шаблон: audit updated_by
DELIMITER //
CREATE TRIGGER any_bu_audit_user
BEFORE UPDATE ON any_table
FOR EACH ROW
BEGIN
SET NEW.updated_by = COALESCE(@current_user_id, NEW.updated_by);
END];//
DELIMITER ;94. Шаблон: денормализация счётчиков
DELIMITER //
CREATE TRIGGER comments_bi_counter
BEFORE INSERT ON comments
FOR EACH ROW
BEGIN
SET NEW.body = TRIM(NEW.body);
END];//
DELIMITER ;95. Шаблон: сигнал с кодом и подробностями
DELIMITER //
CREATE TRIGGER orders_bi_signal_detail
BEFORE INSERT ON orders
FOR EACH ROW
BEGIN
IF NEW.total<=0 THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT='Bad total', MYSQL_ERRNO=1644;
END IF;
END];//
DELIMITER ;Ещё примеры
96. Шаблон: защита от нулевого делителя
DELIMITER //
CREATE TRIGGER stats_bi_guard
BEFORE INSERT ON stats
FOR EACH ROW
BEGIN
IF NEW.den=0 THEN
SET NEW.den = 1;
END IF;
END];//
DELIMITER ;97. Шаблон: нормализация JSON
DELIMITER //
CREATE TRIGGER meta_bi_json
BEFORE INSERT ON meta
FOR EACH ROW
BEGIN
SET NEW.payload = JSON_REMOVE(NEW.payload,'$.debug');
END];//
DELIMITER ;98. Шаблон: обрезка HTML
DELIMITER //
CREATE TRIGGER posts_bi_strip_html
BEFORE INSERT ON posts
FOR EACH ROW
BEGIN
SET NEW.body = REGEXP_REPLACE(NEW.body,'<[^>]+>','');
END];//
DELIMITER ;99. Шаблон: защитить parent_id от циклов (простой чек)
DELIMITER //
CREATE TRIGGER cats_bu_no_self
BEFORE UPDATE ON categories
FOR EACH ROW
BEGIN
IF NEW.parent_id=OLD.id THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='Self as parent';
END IF;
END];//
DELIMITER ;100. Шаблон: нормализация валютного кода
DELIMITER //
CREATE TRIGGER money_bi_ccy
BEFORE INSERT ON money
FOR EACH ROW
BEGIN
SET NEW.currency = UPPER(LEFT(NEW.currency,3));
END];//
DELIMITER ;BEFORE в MySQL. Заключение
BEFORE‑триггеры позволяют раннее вмешательство в поток данных: чистить и нормализовать значения, безопасно рассчитывать derived‑поля, а также жестко останавливать ошибочные операции через SIGNAL. Следите за простотой логики и избегайте тяжёлых запросов внутри триггеров.
Справка: официальная документация MySQL.