चेक बाधा काम नहीं करता है?


23

मेरे पास निम्न तालिका है।

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

समस्या यह है कि CHECKबाधा उम्र के कॉलम पर काम नहीं करती है। उदाहरण के लिए, जब मैं 222 आयु क्षेत्र के लिए सम्मिलित करता हूं तो MySQL इसे स्वीकार करता है।

जवाबों:


16

अमान्य आयु स्थिति को पकड़ने के लिए आपको दो ट्रिगर चाहिए होंगे

  • पहले से ही
  • आगे बढ़ें

निम्नलिखित अध्याय 11 से MySQL ट्रिगर के लिए एक जेरी-धांधली त्रुटि फँसाने पद्धति पर आधारित है, पुस्तक के पेज 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>

कृपया यह भी ध्यान दें कि ऑटो इंक्रीमेंट वैल्यू बर्बाद या गुम नहीं हुई हैं।

कोशिश तो करो !!!


19

MySQL में CHECK की कमी को लागू नहीं किया गया है। से टेबल बनाएं

चेक क्लॉज़ को पार्स किया जाता है लेकिन सभी स्टोरेज इंजनों द्वारा इसे अनदेखा किया जाता है। धारा 12.1.17, "सृजन तालिका सिंटैक्स" देखें। सिंटैक्स क्लॉज़ को अनदेखा करने के लिए स्वीकार करने का कारण संगतता के लिए है, जिससे अन्य SQL सर्वर से पोर्ट कोड को आसान बनाने के लिए, और उन अनुप्रयोगों को चलाने के लिए जो संदर्भों के साथ तालिकाओं का निर्माण करते हैं। खंड 1.8.5, "मानक SQL से MySQL अंतर" देखें।

यह भी लगभग 8 वर्षों के लिए एक बग बताया गया है ...


13

@ रोलैंडो द्वारा अच्छा ट्रिगर समाधान के अलावा, MySQL में इस समस्या का एक और समाधान है (जब तक कि CHECKबाधाओं को लागू नहीं किया जाता है)।

CHECKMySQL में कुछ बाधाओं का अनुकरण कैसे करें

इसलिए, यदि आप संदर्भात्मक अखंडता बाधाओं को पसंद करते हैं और ट्रिगर से बचना चाहते हैं (क्योंकि 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)।


CHECKMySQL (5.7) और मारियाबीडी में मनमाने ढंग से अवरोधों का अनुकरण कैसे करें (5.2 से 10.1 तक)

चूंकि मारबीडीबी ने अपने 5.2 संस्करण (जीए रिलीज: 2010-11-10 ) और MySQL में 5.7 (जीए रिलीज: 2015-10-21 ) में गणना किए गए कॉलम जोड़े हैं - जो वे उन्हें VIRTUALऔर GENERATEDक्रमशः कहते हैं - जिसे बरकरार रखा जा सकता है, अर्थात इसमें संग्रहीत तालिका - वे उन्हें PERSISTENTऔर STOREDक्रमशः कहते हैं - हम उन्हें उपरोक्त समाधान को सरल बनाने के लिए उपयोग कर सकते हैं और इससे भी बेहतर, इसे मनमाना CHECKबाधाओं को लागू करने / लागू करने के लिए बढ़ा सकते हैं ):

ऊपर के रूप में, हमें एक सहायता तालिका की आवश्यकता होगी लेकिन इस बार एक एकल पंक्ति के साथ जो "लंगर" तालिका के रूप में काम करेगी। इससे भी बेहतर, इस तालिका का उपयोग किसी भी संख्या में CHECKबाधाओं के लिए किया जा सकता है ।

हम फिर एक गणना कॉलम जोड़ते हैं जो या तो TRUE/ FALSE/ का मूल्यांकन करता है UNKNOWN, बिल्कुल एक CHECKबाधा के रूप में - लेकिन यह कॉलम FOREIGN KEYहमारी लंगर तालिका के लिए एक बाधा है। यदि FALSEकुछ पंक्तियों के लिए स्थिति / स्तंभ का मूल्यांकन किया जाता है , तो एफके के कारण पंक्तियों को अस्वीकार कर दिया जाता है।

यदि स्थिति / स्तंभ ( TRUEया ) का मूल्यांकन करता है , तो पंक्तियों को अस्वीकार नहीं किया जाता है, ठीक वैसे ही जैसा कि बाधाओं के साथ होना चाहिए :UNKNOWNNULLCHECK

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 संस्करण है। मारियाबीडी (संस्करण 5.2+ से 10.1 तक) में, हमें सिंटैक्स को संशोधित करने और PERSISTENTइसके बजाय कॉलम को घोषित करने की आवश्यकता है STORED। संस्करण 10.2 में STOREDकीवर्ड को भी जोड़ा गया था, इसलिए ऊपर का उदाहरण नवीनतम संस्करणों के लिए दोनों फ्लेवर (MySQL और MariaDB) में काम करता है।

यदि हम कई CHECKबाधाओं (जो कई डिजाइनों में आम है) को लागू करना चाहते हैं , तो हमें बस उनमें से प्रत्येक के लिए एक गणना कॉलम और एक विदेशी कुंजी जोड़ना होगा। हमें truthडेटाबेस में केवल एक टेबल चाहिए । इसमें एक पंक्ति सम्मिलित होनी चाहिए और फिर सभी लिखी गई पहुंच हटा दी जानी चाहिए।


हालांकि, मारियाडीबी के ताजा आंकड़ों में, हमें इन सभी कलाबाजियों को और अधिक करने की आवश्यकता नहीं है, क्योंकि CHECKबाधाओं को 10.2.1 संस्करण में लागू किया गया है (अल्फा रिलीज: 2016-जुलाई -04)!

वर्तमान 10.2.2 संस्करण अभी भी एक बीटा संस्करण है, लेकिन ऐसा लगता है कि यह सुविधा मारियाडीबी 10.2 श्रृंखला के पहले स्थिर रिलीज में उपलब्ध होगी।


0

जैसा कि मैंने इस लेख में समझाया , संस्करण 8.0.16 से शुरू होकर, MySQL ने कस्टम CHECK बाधाओं के लिए समर्थन जोड़ा है:

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;

8.0.16 से पहले MySQL संस्करणों के लिए डेटाबेस ट्रिगर का उपयोग करके CHECK बाधाओं का अनुकरण करने के बारे में अधिक जानकारी के लिए, फिर इस लेख को देखें

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.