kontrol kısıtlaması çalışmıyor mu?


23

Aşağıdaki tabloya sahibim.

create table test (
   id smallint unsigned AUTO_INCREMENT,
   age tinyint not null,
   primary key(id),
   check (age<20)
);

Sorun, CHECKkısıtlamanın yaş sütununda işe yaramamasıdır. Örneğin, yaş alanı için 222 eklediğimde MySQL kabul ediyor.

Yanıtlar:


16

İhtiyacınız olan şey, geçersiz yaş koşulunu yakalamak için iki tetikleyici.

  • INSERT ÖNCESİ
  • GÜNCELLEME ÖNCESİ

Aşağıdakiler, 'Tetikleyicilerle Verilerin Doğrulanması' alt başlığı altındaki MySQL Saklı Prosedürü Programlama bölümündeki Bölüm 11, Sayfa 254-256'daki MySQL Tetikleyicileri için jerry-riged hata yakalama yöntemine dayanmaktadır :

drop table mytable; 
create table mytable ( 
    id smallint unsigned AUTO_INCREMENT, 
    age tinyint not null, 
    primary key(id) 
); 
DELIMITER $$  
CREATE TRIGGER checkage_bi BEFORE INSERT ON mytable FOR EACH ROW  
BEGIN  
    DECLARE dummy,baddata INT;  
    SET baddata = 0;  
    IF NEW.age > 20 THEN  
        SET baddata = 1;  
    END IF;  
    IF NEW.age < 1 THEN  
        SET baddata = 1;  
    END IF;  
    IF baddata = 1 THEN  
        SELECT CONCAT('Cannot Insert This Because Age ',NEW.age,' is Invalid')  
        INTO dummy FROM information_schema.tables;
    END IF;  
END; $$  
CREATE TRIGGER checkage_bu BEFORE UPDATE ON mytable FOR EACH ROW  
BEGIN  
    DECLARE dummy,baddata INT;  
    SET baddata = 0;  
    IF NEW.age > 20 THEN  
        SET baddata = 1;  
    END IF;  
    IF NEW.age < 1 THEN  
        SET baddata = 1;  
    END IF;  
    IF baddata = 1 THEN  
        SELECT CONCAT('Cannot Update This Because Age ',NEW.age,' is Invalid')  
        INTO dummy FROM information_schema.tables;
    END IF;  
END; $$  
DELIMITER ;  
insert into mytable (age) values (10);
insert into mytable (age) values (15);
insert into mytable (age) values (20);
insert into mytable (age) values (25);
insert into mytable (age) values (35);
select * from mytable;
insert into mytable (age) values (5);
select * from mytable;

İşte sonuç:

mysql> drop table mytable;
Query OK, 0 rows affected (0.03 sec)

mysql> create table mytable (
    ->     id smallint unsigned AUTO_INCREMENT,
    ->     age tinyint not null,
    ->     primary key(id)
    -> );
Query OK, 0 rows affected (0.06 sec)

mysql> DELIMITER $$
mysql> CREATE TRIGGER checkage_bi BEFORE INSERT ON mytable FOR EACH ROW
    -> BEGIN
    ->     DECLARE dummy,baddata INT;
    ->     SET baddata = 0;
    ->     IF NEW.age > 20 THEN
    ->         SET baddata = 1;
    ->     END IF;
    ->     IF NEW.age < 1 THEN
    ->         SET baddata = 1;
    ->     END IF;
    ->     IF baddata = 1 THEN
    ->         SELECT CONCAT('Cannot Insert This Because Age ',NEW.age,' is Invalid')
    ->         INTO dummy FROM information_schema.tables;
    ->     END IF;
    -> END; $$
Query OK, 0 rows affected (0.08 sec)

mysql> CREATE TRIGGER checkage_bu BEFORE UPDATE ON mytable FOR EACH ROW
    -> BEGIN
    ->     DECLARE dummy,baddata INT;
    ->     SET baddata = 0;
    ->     IF NEW.age > 20 THEN
    ->         SET baddata = 1;
    ->     END IF;
    ->     IF NEW.age < 1 THEN
    ->         SET baddata = 1;
    ->     END IF;
    ->     IF baddata = 1 THEN
    ->         SELECT CONCAT('Cannot Update This Because Age ',NEW.age,' is Invalid')
    ->         INTO dummy FROM information_schema.tables;
    ->     END IF;
    -> END; $$
Query OK, 0 rows affected (0.07 sec)

mysql> DELIMITER ;
mysql> insert into mytable (age) values (10);
Query OK, 1 row affected (0.06 sec)

mysql> insert into mytable (age) values (15);
Query OK, 1 row affected (0.05 sec)

mysql> insert into mytable (age) values (20);
Query OK, 1 row affected (0.04 sec)

mysql> insert into mytable (age) values (25);
ERROR 1172 (42000): Result consisted of more than one row
mysql> insert into mytable (age) values (35);
ERROR 1172 (42000): Result consisted of more than one row
mysql> select * from mytable;
+----+-----+
| id | age |
+----+-----+
|  1 |  10 |
|  2 |  15 |
|  3 |  20 |
+----+-----+
3 rows in set (0.00 sec)

mysql> insert into mytable (age) values (5);
Query OK, 1 row affected (0.07 sec)

mysql> select * from mytable;
+----+-----+
| id | age |
+----+-----+
|  1 |  10 |
|  2 |  15 |
|  3 |  20 |
|  4 |   5 |
+----+-----+
4 rows in set (0.00 sec)

mysql>

Lütfen otomatik artış değerlerinin boşa veya kayıp olmadığına dikkat edin.

Bir şans ver !!!


19

CHECK kısıtlamaları MySQL'de uygulanmadı. Gönderen TABLO OLUŞTURMA

CHECK deyimi ayrıştırıldı, ancak tüm depolama motorları tarafından yoksayıldı. Bkz. Bölüm 12.1.17, “CREATE TABLE Syntax”. Sözdizimi maddelerini kabul etmenin, görmezden gelmenin nedeni uyumluluk, diğer SQL sunucularından kod yerleştirmeyi kolaylaştırmak ve referanslarla tablolar oluşturan uygulamalar çalıştırmaktır. Bkz. Bölüm 1.8.5, “Standart SQL'den MySQL Farklılıkları”.

Ayrıca neredeyse 8 yıldır bildirilen bir hata oldu ...


13

@Rolando tarafından sunulan güzel tetikleyici çözümünün yanı sıra, MySQL'de ( CHECKkısıtlamalar uygulanana kadar ) bu sorunun başka bir çözümü vardır .

MySQL'de bazı CHECKkısıtlamaları nasıl taklit edersiniz

Bu nedenle, başvuru bütünlüğü kısıtlamalarını tercih ediyorsanız ve tetikleyicilerden kaçınmak istiyorsanız (her iki tablonuzda da varken MySQL'deki sorunlar nedeniyle), başka bir küçük referans tablosu kullanabilirsiniz:

CREATE TABLE age_allowed
  ( age TINYINT UNSIGNED NOT NULL
  , PRIMARY KEY (age)
  ) ENGINE = InnoDB ;

20 satırla doldurun:

INSERT INTO age_allowed
  (age)
VALUES
  (0), (1), (2), (3), ..., (19) ;

O zaman masan:

CREATE TABLE test 
  ( id SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT
  , age TINYINT UNSIGNED NOT NULL
  , PRIMARY KEY (id)
  , CONSTRAINT age_allowed__in__test 
      FOREIGN KEY (age)
        REFERENCES age_allowed (age)
  ) ENGINE = InnoDB ;

age_allowedYanlışlıkla satır eklemek veya kaldırmaktan kaçınmak için tabloya yazma erişimini kaldırmanız gerekir .

Bu numara FLOAT, maalesef veri türleri sütunlarıyla çalışmayacak ( 0.0ve arasında çok fazla değer var 20.0).


CHECKMySQL (5.7) ve MariaDB'deki keyfi kısıtlamaları nasıl taklit edebilirsiniz (5.2'den 10.1'e kadar)

Yana mariadb onların 5.2 versiyonu hesaplanan sütun eklenmiştir : (GA serbest 2010-11-10 ve) 5.7 MySQL : (GA serbest 2015/10/21 onları aramak -) VIRTUALve GENERATEDsırasıyla - kalıcı olabilir, yani depolanan tablo - bunları çağırır PERSISTENTve STOREDsırasıyla - yukarıdaki çözümü basitleştirmek ve daha da iyisi, keyfi CHECKkısıtlamaları taklit etmek / uygulamak için genişletmek için kullanabiliriz ):

Yukarıdaki gibi, bir yardım masasına ihtiyacımız olacak, ancak bu sefer bir "çapa" masası olarak hareket edecek tek bir satırla. Daha da iyisi, bu tablo herhangi bir sayıda CHECKkısıtlama için kullanılabilir .

Daha sonra TRUE/ FALSE/ ' UNKNOWNyı tam olarak bir CHECKkısıtlama olacak şekilde değerlendiren hesaplanmış bir sütun ekleriz - ancak bu sütunun FOREIGN KEYbağlantı tablomuz için bir kısıtlaması vardır . Koşul / sütun FALSEbazı satırlar için değerlendirilirse , FK nedeniyle satırlar reddedilir.

Koşul / sütun TRUEveya UNKNOWN( NULL) olarak değerlendirilirse , satırlar tam olarak CHECKkısıtlamalarda olduğu gibi reddedilmez :

CREATE TABLE truth
  ( t BIT NOT NULL,
    PRIMARY KEY (t)
  ) ENGINE = InnoDB ;

-- Put a single row:

INSERT INTO truth (t)
VALUES (TRUE) ;

-- Then your table would be:
-- (notice the change to `FLOAT`, to prove that we don't need) 
-- (to restrict the solution to a small type)

CREATE TABLE test 
  ( id SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT,
    age FLOAT NOT NULL,
    age_is_allowed BIT   -- GENERATED ALWAYS  
       AS (age >= 0 AND age < 20)             -- our CHECK constraint
       STORED,
    PRIMARY KEY (id),
    CONSTRAINT check_age_must_be_non_negative_and_less_than_20
      FOREIGN KEY (age_is_allowed)
        REFERENCES truth (t)
  ) ENGINE = InnoDB ;

Örnek MySQL 5.7 versiyonudur. MariaDB'de (5.2+ sürüm 10.1'e kadar), sadece sözdizimini değiştirmemiz ve PERSISTENTyerine sütunu bildirmemiz gerekir STORED. 10.2 sürümünde STOREDanahtar kelime de eklenmiştir, bu nedenle yukarıdaki örnek en son sürümler için her iki tada (MySQL ve MariaDB) çalışır.

Eğer birçok CHECKkısıtlamayı (birçok tasarımda ortak olan) uygulamak istiyorsak, her biri için hesaplanmış bir sütun ve bir yabancı anahtar eklemeliyiz. truthVeritabanında sadece bir tabloya ihtiyacımız var . Bir satır takılı olmalı ve ardından tüm yazma erişimi kaldırılmalıdır.


Bununla birlikte, en son MariaDB'de, 10.2.1 sürümünde kısıtlamalar uygulandığı için tüm bu akrobasi oyunlarını bir daha yapmak zorunda değiliz (alfa sürümü: 2016-Jul-04)!CHECK

Geçerli 10.2.2 sürümü hala bir beta sürümüdür ancak bu özellik MariaDB 10.2 serisinin ilk kararlı sürümünde kullanıma sunulacak gibi görünüyor.


0

Bu makalede açıkladığım gibi , 8.0.16 sürümünden başlayarak, MySQL özel CHECK kısıtlamaları için destek eklemiştir:

ALTER TABLE topic
ADD CONSTRAINT post_content_check
CHECK (
    CASE
        WHEN DTYPE = 'Post'
        THEN
            CASE
                WHEN content IS NOT NULL
                THEN 1
                ELSE 0
            END
        ELSE 1
    END = 1
);

ALTER TABLE topic
ADD CONSTRAINT announcement_validUntil_check
CHECK (
    CASE
        WHEN DTYPE = 'Announcement'
        THEN
            CASE
                WHEN validUntil IS NOT NULL
                THEN 1
                ELSE 0
            END
        ELSE 1
    END = 1
);

Önceden, bu yalnızca INSERT ÖNCESİ ve GÜNCELLEŞTİRME tetikleyicilerinden ÖNCE kullanıldı:

CREATE
TRIGGER post_content_check BEFORE INSERT
ON topic
FOR EACH ROW
BEGIN
   IF NEW.DTYPE = 'Post'
   THEN
       IF NEW.content IS NULL
       THEN
           signal sqlstate '45000'
           set message_text = 'Post content cannot be NULL';
       END IF;
   END IF;
END;

CREATE
TRIGGER post_content_update_check BEFORE UPDATE
ON topic
FOR EACH ROW
BEGIN
   IF NEW.DTYPE = 'Post'
   THEN
       IF NEW.content IS NULL
       THEN
           signal sqlstate '45000'
           set message_text = 'Post content cannot be NULL';
       END IF;
   END IF;
END;

CREATE
TRIGGER announcement_validUntil_check BEFORE INSERT
ON topic
FOR EACH ROW
BEGIN
   IF NEW.DTYPE = 'Announcement'
   THEN
       IF NEW.validUntil IS NULL
       THEN
           signal sqlstate '45000'
           set message_text = 'Announcement validUntil cannot be NULL';
       END IF;
   END IF;
END;

CREATE
TRIGGER announcement_validUntil_update_check BEFORE UPDATE
ON topic
FOR EACH ROW
BEGIN
   IF NEW.DTYPE = 'Announcement'
   THEN
       IF NEW.validUntil IS NULL
       THEN
           signal sqlstate '45000'
           set message_text = 'Announcement validUntil cannot be NULL';
       END IF;
   END IF;
END;

8.0.16 öncesi MySQL sürümleri için veritabanı tetikleyicileri kullanarak CHECK kısıtlamaları öykünme hakkında daha fazla bilgi için bu makaleye göz atın .

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.