C, PHP, VB, .NET

Дневникът на Филип Петров


* Симулиране на CHECK с VIEW

Публикувано на 01 април 2010 в раздел Бази от Данни.

В предишна статия свързана с ограниченията CHECK писахме за нещо изключително неприятно – не се поддържат от MySQL. Същевременно именно CHECK понякога е доста важно за интегритета на данните, когато пишем в „несигурна среда“, т.е. работим с програми, на които не можем да вярваме.

Първият и най-качествен вариант за справяне с проблема е да не даваме директен достъп за въвеждане и промяна на данни в базата данни. Вместо това можем да направим набор от вградени процедури, които да правят проверка на входните параметри. За жалост в повечето случаи писането на процедури за всяка ситуация е много трудоемка работа, а и кодът става много по-трудно преносим.

Вторият доста популярен вариант е с използването на тригери (процедури, които се задействат при insert и update). По мое мнение обаче той трябва да се избягва за бази данни със сложен дизайн, защото тригерите не се активират при каскадно обновяване на данните. Може би ще разгледаме подобна функционалност когато стигнем до писане и на статия свързана с тригери.

Методът който ще разгледаме на пръв поглед е доста странен – задаване на функционалност CHECK чрез VIEW. Фундаменталната основа, на която ще стъпим е, че в MySQL върху VIEW може да се изпълняват INSERT, UPDATE и DELETE заявки. Именно това тяхно свойство ни позволява да заобиколим недостатъка от липсата на ограничение CHECK. Нека например вземем една съвсем проста таблица с продукти и техните цени:

CREATE DATABASE viewcheckexample;

USE viewcheckexample;

CREATE TABLE products(
	`id` INT AUTO_INCREMENT,
	PRIMARY KEY(id),
	`name` VARCHAR(255) NOT NULL,
	`price` DOUBLE NOT NULL,
	UNIQUE(name, price),
	CHECK(price>2)
) ENGINE=INNODB;

Както знаем, условието CHECK ще бъде прието, но просто ще бъде пропуснато от MySQL, т.е. спокойно можем да вкараме продукт с price=0. Нека обаче направим изглед (VIEW), от който генерираме същата таблица, но с въведеното условие от проверката:

CREATE VIEW products_view
AS SELECT * FROM products
   WHERE price>2
   WITH CASCADED CHECK OPTION;

От тук нататък можем да проверим, че условието работи, ако се обръщаме към въпросното VIEW:

INSERT INTO products_view(name, price)
VALUES ('A', 0);
ERROR 1369 (HY000): CHECK OPTION failed 'viewcheckexample.products_view'

INSERT INTO products_view(name, price)
VALUES ('A', 5);
Query OK, 1 row affected (0.00 sec)

UPDATE products_view
SET price=0
WHERE id=1;
ERROR 1369 (HY000): CHECK OPTION failed 'viewcheckexample.products_view'

UPDATE products_view
SET price=50
WHERE id=1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

Недостатък при използвания подход е, че винаги съобщението за грешка е едно и също, т.е. потребителят няма да бъде уведомен какво точно не е в ред. И все пак въпреки, че показаната функционалност работи, ще бъде по-добре ако от MySQL просто добавят нужната функционалност колкото се може по-бързо. Остава да чакаме…

Задача: „Поправете“ всички досега разгледани примерни бази данни, като създадете необходимите VIEW при таблици с повече от един външен ключ и по този начин създадете алтернатива на CHECK условия за интегритет.

 



3 коментара


  1. Hi Morg,

    I actually mentioned this in the article. It is worse variant, because triggers are not activated on cascade operations.

  2. Morg. каза:

    Not bad, although I can’t read a word of what you wrote (no translator on this browser…) –
    What is your opinion of the other workaround, using before insert/update triggers to check the value as the check constraint would do ?

  3. Morg. каза:

    True that… start using PostgreSQL ;)

    OR

    Rewrite all the foreign keys /cascades as triggers to make MySQL ACID compliant and then you can use triggers to simulate check etc.

    No kidding, workarounds are great but starting with a better base is even better.

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

Адресът на електронната поща няма да се публикува


*