2008-10-23 46 views
14

Trong khi dọn dẹp this answer Tôi đã học được một chút về các thủ tục được lưu trữ trong MySQL, nhưng đã choáng váng rằng, trong khi các trình kích hoạt BEFORE INSERTBEFORE UPDATE có thể sửa đổi dữ liệu, chúng dường như không thể chèn/cập nhật không thành công (ví dụ: xác thực) . Trong trường hợp cụ thể này, tôi có thể làm việc này bằng cách thao tác dữ liệu theo cách sao cho tạo ra một khóa chính trùng lặp, trong trường hợp cụ thể này có ý nghĩa, nhưng không nhất thiết phải có ý nghĩa theo nghĩa chung.TRIGGERs khiến INSERT bị lỗi? Khả thi?

Đây có phải là loại chức năng có thể có trong MySQL không? Trong bất kỳ RDBMS nào khác (kinh nghiệm của tôi bị giới hạn trong MySQL đáng buồn)? Có lẽ cú pháp kiểu THROW EXCEPTION?

+1

từ MySQL 5.5, có thể là trước đó, bạn có thể sử dụng tín hiệu, [xem câu trả lời của tôi ở đây] [1] [1]: http://stackoverflow.com/questions/24/throw-error- in-mysql-trigger/7189396 # 7189396 – RuiDC

Trả lời

19

Từ này blog post

MySQL Triggers: How do you abort an INSERT, UPDATE or DELETE with a trigger? On EfNet’s #mysql someone asked:

How do I make a trigger abort the operation if my business rule fails?

In MySQL 5.0 and 5.1 you need to resort to some trickery to make a trigger fail and deliver a meaningful error message. The MySQL Stored Procedure FAQ says this about error handling:

SP 11. Do SPs have a “raise” statement to “raise application errors”? Sorry, not at present. The SQL standard SIGNAL and RESIGNAL statements are on the TODO.

Perhaps MySQL 5.2 will include SIGNAL statement which will make this hack stolen straight from MySQL Stored Procedure Programming obsolete. What is the hack? You’re going to force MySQL to attempt to use a column that does not exist. Ugly? Yes. Does it work? Sure.

CREATE TRIGGER mytabletriggerexample 
BEFORE INSERT 
FOR EACH ROW BEGIN 
IF(NEW.important_value) < (fancy * dancy * calculation) THEN 
    DECLARE dummy INT; 

    SELECT Your meaningful error message goes here INTO dummy 
     FROM mytable 
     WHERE mytable.id=new.id 
END IF; END; 
+5

Tôi thực sự đã cười khi đọc ví dụ này ... Đó là một hack tuyệt vời phát trên báo cáo lỗi của MySQL ... Đây là hy vọng cho SIGNAL đôi khi sớm. –

+0

Điều đó thật tuyệt vời. Tôi đã được thiết lập một số cột NOT NULL để NULL, nhưng giải pháp này ném một thông báo lỗi có ý nghĩa. +1! –

+1

Điều này không thành công đối với tôi khi tuyên bố khai báo nằm trong điều kiện. Các tài liệu nói, [DECLARE chỉ được phép bên trong BEGIN ... câu lệnh phức hợp END và phải bắt đầu, trước khi có bất kỳ câu lệnh nào khác.] (Http://dev.mysql.com/doc/refman/5.0/en/ declare.html). –

1

này sẽ hủy bỏ INSERT của bạn bằng cách tăng một ngoại lệ (từ http://www.experts-exchange.com/Database/MySQL/Q_23788965.html)

DROP PROCEDURE IF EXISTS `MyRaiseError`$$ 

CREATE PROCEDURE `MyRaiseError`(msg VARCHAR(62)) 
BEGIN 
DECLARE Tmsg VARCHAR(80); 
SET Tmsg = msg; 
IF (CHAR_LENGTH(TRIM(Tmsg)) = 0 OR Tmsg IS NULL) THEN 
SET Tmsg = 'ERROR GENERADO'; 
END IF; 
SET Tmsg = CONCAT('@@MyError', Tmsg, '@@MyError'); 
SET @MyError = CONCAT('INSERT INTO', Tmsg); 
PREPARE stmt FROM @MyError; 
EXECUTE stmt; 
DEALLOCATE PREPARE stmt; 
END$$ 

Cách sử dụng:

call MyRaiseError('Here error message!'); 
8

Dưới đây là cách tôi đã làm nó. Lưu ý SET NEW='some error';. MySQL sẽ cho bạn biết "Biến 'mới' không thể được đặt thành giá trị của 'Lỗi: Không thể xóa mục này. Có các bản ghi trong bảng bán hàng với mục này.'"

Bạn có thể bẫy mã này trong mã của mình và sau đó hiển thị các lỗi dẫn đến :)

DELIMITER $$ 
DROP TRIGGER IF EXISTS before_tblinventoryexceptionreasons_delete $$ 
CREATE TRIGGER before_tblinventoryexceptionreasons_delete 
BEFORE DELETE ON tblinventoryexceptionreasons 
FOR EACH ROW BEGIN 
    IF (SELECT COUNT(*) FROM tblinventoryexceptions WHERE tblinventoryexceptions.idtblinventoryexceptionreasons = old.idtblinventoryexceptionreasons) > 0 
    THEN 
    SET NEW='Error: Cannot delete this item. There are records in the inventory exception reasons table with this item.'; 
    END IF; 
END$$ 
DELIMITER ; 

DELIMITER $$ 
DROP TRIGGER IF EXISTS before_storesalesconfig_delete $$ 
CREATE TRIGGER before_storesalesconfig_delete 
BEFORE DELETE ON tblstoresalesconfig 
FOR EACH ROW BEGIN 
    IF (SELECT COUNT(*) FROM tblstoresales WHERE tblstoresales.idtblstoresalesconfig=old.idtblstoresalesconfig) > 0 
    THEN 
    SET NEW='Error: Cannot delete this item. There are records in the sales table with this item.'; 
    END IF; 
    IF (SELECT COUNT(*) FROM tblinventory WHERE tblinventory.idtblstoresalesconfig=old.idtblstoresalesconfig) > 0 
    THEN 
    SET NEW='Error: Cannot delete this item. There are records in the inventory table with this item.'; 
    END IF; 
    IF (SELECT COUNT(*) FROM tblinventoryexceptions WHERE tblinventoryexceptions.idtblstoresalesconfig=old.idtblstoresalesconfig) > 0 
    THEN 
    SET NEW='Error: Cannot delete this item. There are records in the inventory exceptions table with this item.'; 
    END IF; 
    IF (SELECT COUNT(*) FROM tblinvoicedetails WHERE tblinvoicedetails.idtblstoresalesconfig=old.idtblstoresalesconfig) > 0 
    THEN 
    SET NEW='Error: Cannot delete this item. There are records in the inventory details table with this item.'; 
    END IF; 
END$$ 
DELIMITER ; 

DELIMITER $$ 
DROP TRIGGER IF EXISTS before_tblinvoice_delete $$ 
CREATE TRIGGER before_tblinvoice_delete 
BEFORE DELETE ON tblinvoice 
FOR EACH ROW BEGIN 
    IF (SELECT COUNT(*) FROM tblinvoicedetails WHERE tblinvoicedetails.idtblinvoice = old.idtblinvoice) > 0 
    THEN 
    SET NEW='Error: Cannot delete this item. There are records in the inventory details table with this item.'; 
    END IF; 
END$$ 
DELIMITER ; 
1

việc doen't trong trigger (Dynamic SQL là không được phép vào chức năng lưu trữ hoặc kích hoạt)

tôi sử dụng một giải pháp rất bẩn:

If NEW.test=1 then CALL TEST_CANNOT_BE_SET_TO_1; end if;

Khi test = 1 Mysql ném ngoại lệ sau đây:

THỦ TỤC administratie.TEST_CANNOT_BE_SET_TO_1 không tồn tại

Không phức tạp nhưng nhanh chóng và hữu ích.

3

Vì bài viết này hướng lên trên cùng khi tôi tìm kiếm xử lý lỗi trong trình kích hoạt của MySQL, tôi nghĩ tôi sẽ chia sẻ một số kiến ​​thức.

Nếu có lỗi, bạn có thể buộc MySQL sử dụng SIGNAL, nhưng nếu bạn không chỉ định lớp đó là SQLEXCEPTION, thì sẽ không có gì xảy ra, vì không phải tất cả SQLSTATE đều bị coi là xấu và thậm chí bạn phải đảm bảo rằng bạn có RESIGNAL nếu bạn có bất kỳ khối BEGIN/END lồng nhau nào.

Cách khác, và có lẽ đơn giản hơn, trong trình kích hoạt của bạn, khai báo một trình xử lý thoát và từ chức ngoại lệ.

CREATE TRIGGER `my_table_AINS` AFTER INSERT ON `my_table` FOR EACH ROW 
BEGIN 
    DECLARE EXIT HANDLER FOR SQLEXCEPTION 
     RESIGNAL; 
    DECLARE EXIT HANDLER FOR SQLWARNING 
     RESIGNAL; 
    DECLARE EXIT HANDLER FOR NOT FOUND 
     RESIGNAL; 
    -- Do the work of the trigger. 
END 

Và nếu trong cơ thể của bạn xuất hiện lỗi, nó sẽ được đưa trở lại lên trên và thoát ra với lỗi. Điều này cũng có thể được sử dụng trong các thủ tục được lưu trữ và không có điều gì.

Tính năng này hoạt động với bất kỳ phiên bản 5.5+ nào.

+0

Tăng hạng này vì nó chính xác, nhưng tiếc là không phải tất cả chúng ta đều có sự sang trọng làm việc với 5,5+. Oh well. –

Các vấn đề liên quan