it-swarm.asia

التحقق من القيد لا يعمل؟

لدي الجدول التالي.

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

المشكلة هي أن القيد CHECK لا يعمل على عمود العمر. على سبيل المثال ، عندما أقوم بإدخال 222 في حقل العمر ، تقبل MySQL ذلك.

23
ALH

ما تحتاجه هو محفزين للقبض على حالة العمر غير الصالحة

  • قبل الإدراج
  • قبل التحديث

ما يلي يعتمد على طريقة تعويض الأخطاء المعززة بالجيري لمشغلات MySQL من الفصل 11 ، الصفحات 254-256 من الكتاب برمجة الإجراءات المخزنة MySQL تحت العنوان الفرعي "التحقق من صحة البيانات باستخدام المُشغلات" :

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;

ها هي النتيجة:

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>

يرجى أيضًا ملاحظة أن قيم الزيادة التلقائية لا تضيع أو تفقد.

جربها !!!

16
RolandoMySQLDBA

لا يتم تنفيذ قيود التحقق في MySQL. من إنشاء جدول

يتم تحليل جملة CHECK ولكن يتم تجاهلها بواسطة جميع محركات التخزين. راجع القسم 12.1.17 ، "إنشاء بنية الجدول". السبب وراء قبول عبارات بناء الجملة ولكن تجاهلها هو التوافق ، ولتسهيل نقل التعليمات البرمجية من خوادم SQL الأخرى ، ولتشغيل التطبيقات التي تنشئ جداول بمراجع. انظر القسم 1.8.5 ، "اختلافات MySQL من SQL القياسية".

لقد كانت أيضًا تم الإبلاغ عن خطأ لمدة 8 سنوات تقريبًا ...

19
gbn

بالإضافة إلى حل الزناد الجميل منRolando ، هناك حل آخر لهذه المشكلة في MySQL (حتى يتم تنفيذ قيود CHECK).

كيفية محاكاة بعض قيود CHECK في MySQL

لذلك ، إذا كنت تفضل قيود التكامل المرجعي وتريد تجنب المشغلات (بسبب المشكلات في MySQL عندما يكون لديك كلاهما في جداولك) ، يمكنك استخدام جدول مرجعي صغير آخر:

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

املأه بـ 20 صفًا:

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

ثم سيكون الجدول الخاص بك:

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_allowed الجدول ، لتجنب إضافة أو إزالة الصفوف عن طريق الخطأ.

لن تعمل هذه الحيلة مع أعمدة نوع البيانات FLOAT ، للأسف (قيم كثيرة جدًا بين 0.0 و 20.0).


كيفية محاكاة القيود التعسفية CHECK في MySQL (5.7) و MariaDB (من 5.2 إلى 10.1)

منذ أضاف MariaDB أعمدة محسوبة في الإصدار 5.2 (إصدار GA: 2010-11-10 ) و MySQL في 5.7 (إصدار GA: 2015-10-21 ) - التي يسمونها VIRTUAL و GENERATED على التوالي - يمكن استمرارها ، أي تخزينها في الجدول - يسمونها PERSISTENT و STORED على التوالي - يمكننا استخدامها لتبسيط الحل أعلاه وحتى أفضل ، تمديدها لمحاكاة/فرض قيود CHECK التعسفية ):

كما هو موضح أعلاه ، سنحتاج إلى جدول مساعدة ولكن مع صف واحد هذه المرة سيكون بمثابة جدول "مرساة". والأفضل من ذلك ، يمكن استخدام هذا الجدول لأي عدد من القيود CHECK.

ثم نضيف عمودًا محسوبًا يتم تقييمه إما إلى TRUE/FALSE/UNKNOWN ، تمامًا مثل القيد CHECK - ولكن هذا العمود له FOREIGN KEY القيد على جدول الربط. إذا تم تقييم الشرط/العمود إلى FALSE لبعض الصفوف ، فسيتم رفض الصفوف بسبب FK.

إذا تم تقييم الشرط/العمود إلى TRUE أو UNKNOWN (NULL) ، فلن يتم رفض الصفوف ، تمامًا كما يجب أن يحدث مع CHECK القيود:

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 ;

المثال خاص بإصدار MySQL 5.7. في MariaDB (الإصدارات 5.2+ حتى 10.1) ، نحتاج فقط إلى تعديل بناء الجملة وإعلان العمود على أنه PERSISTENT بدلاً من STORED. في الإصدار 10.2 تمت إضافة الكلمة الرئيسية STORED أيضًا ، لذلك يعمل المثال أعلاه بكلتا النكهات (MySQL و MariaDB) لأحدث الإصدارات.

إذا أردنا فرض العديد من القيود CHECK (وهو أمر شائع في العديد من التصميمات) ، فكل ما علينا فعله هو إضافة عمود محسوب ومفتاح خارجي لكل واحد منهما. نحتاج فقط إلى جدول truth واحد في قاعدة البيانات. يجب أن يحتوي على صف واحد مدرج ثم تتم إزالة كل حق الوصول للكتابة.


ومع ذلك ، في أحدث MariaDB ، لم يعد علينا أداء جميع هذه الألعاب البهلوانية بعد الآن ، حيث تم تنفيذ قيود CHECK في الإصدار 10.2. 1 (إصدار ألفا: 2016 يوليو 04)!

لا يزال الإصدار 10.2.2 الحالي إصدارًا تجريبيًا ولكن يبدو أن الميزة ستكون متاحة في الإصدار المستقر الأول من سلسلة MariaDB 10.2.

13
ypercubeᵀᴹ

كما أوضحت في هذه المقالة ، بدءًا من الإصدار 8.0.16 ، أضافت MySQL دعمًا لقيود التحقق المخصصة:

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
);

في السابق ، كان هذا متاحًا فقط باستخدام مشغلي BEFORE INSERT و BEFORE UPDATE:

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;

لمزيد من التفاصيل حول محاكاة قيود التحقق باستخدام مشغلات قاعدة البيانات لإصدارات MySQL قبل 8.0.16 ، ثم تحقق من هذه المقالة .

0
Vlad Mihalcea