TRIGGER instead CHECK Clause in MySql - mysql

MySql dont supported CHECK Clause so i think i must be to use TRIGGER on a Table
in a simple Table we have two field that opinion field must be in ('normal','bad','good') :
CREATE TABLE `user`.`opinionTable` (
`uid` INT NOT NULL,
`opinion` VARCHAR(10) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
PRIMARY KEY (`uid`,`opinion`),
CHECK (opinion IN ('normal','bad','good'))
) ENGINE = InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci;
i want to check opinion data is a allowed value ('normal','bad','good') or no and when is not allowed i want to Transaction to Rollbacked
i tried this TRIGGER but not worked so what is the correct TRIGGER statement ?
CREATE TRIGGER check_values BEFORE INSERT ON `opinionTable`
FOR EACH ROW
BEGIN
IF (NEW.opinion IN ('normal','bad','good'))
THEN
END IF;
END
i catch this exception :
com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'END IF;
mysql version is 5.1.34 community
jdbc version is 5.1.23

There are two ways to implement check constraint, if the inputs are not matched.
Force to use default values.
SIGNAL an error and abort the transaction.
Example 1: Force to use default values
You can define to use default value to silently use in case an invalid input is encountered.
IF ( LOWER( NEW.opinion ) NOT IN ('normal','bad','good')) THEN
SET default_opinion := 'normal'; -- declare this field first
NEW.opinion := default_opinion; -- change its value as desired
ELSE
NEW.opinion := LOWER( NEW.opinion ); -- change this as desired
END IF;
Example 2: SIGNAL an error and abort the transaction
Define an error state number for the case and relevant error messages.
Use the same to SIGNAL the error.
IF ( LOWER( NEW.opinion ) NOT IN ('normal','bad','good')) THEN
-- don't forget to declare variables first and then use
SET error_message := CONCAT( 'Invalid opinion option: ', NEW.opinion );
-- set proper error state number -- 302478 is just an example
SIGNAL SQLSTATE '302478' SET MESSAGE_TEXT := error_message;
END IF;

Related

SQL Trigger access row's fields dynamically

Currently trying to have a generic activity log table that stores which table, field, value changed (+ necessary primary key)
DELIMITER $$
CREATE TRIGGER tr_customers_insert_activity_log AFTER INSERT ON `customers`
FOR EACH ROW
BEGIN
DECLARE curr_column CHAR(255);
DECLARE finished INT DEFAULT false;
DECLARE column_name_cursor CURSOR FOR SELECT column_name FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name = 'customers' ORDER BY ordinal_position;
DECLARE CONTINUE HANDLER FOR SQLSTATE '02000' SET finished = 1;
OPEN column_name_cursor;
column_loop: LOOP
IF finished THEN
LEAVE column_loop;
END IF;
FETCH column_name_cursor INTO curr_column;
INSERT INTO activity_log(`cid`, `table`, `field`, `value`, `modified_by`, `modified_at`)
VALUES (NEW.cid, 'customers', curr_column, NEW.#curr_column, NEW.modified_by, NEW.modified_at);
END LOOP column_loop;
CLOSE column_name_cursor;
END$$
DELIMITER ;
The problem I have is in here:
INSERT INTO activity_log(`cid`, `table`, `field`, `value`, `modified_by`, `modified_at`)
VALUES (NEW.cid, 'customers', curr_column, NEW.#curr_column, NEW.modified_by, NEW.modified_at);
Since I am dynamically looping through each field by name I don't know how I can get the NEW.#curr_column value. How can you access a property of the NEW/OLD objects using the value of a variable?
To clarify the syntax error is:
#1064 - You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '#curr_column, NEW.modified_by, NEW.modified_at); END LOOP column_loop' at line 17
Thanks!
It's not possible to dynamically address the NEW and OLD values within a TRIGGER.
We can use a CASE expression. But this probably isn't what you were looking for. It's not really "dynamic". We need to statically address each column name we're interested in.
CASE curr_column
WHEN 'cid' THEN NEW.cid
WHEN 'foo' THEN NEW.foo
WHEN 'othercol' THEN NEW.othercol
END
Also problematic is the various datatypes of the columns you might want to store in activity_log table value column... DATE, INTEGER, DECIMAL, ENUM, VARCHAR, ... those are all going to need to be cast to a single datatype of the value column.
Some alternatives to consider:
have the trigger save a copy of the entire row
rather than making the trigger "dynamic", make the creation of the trigger more dynamic... i.e. use a SELECT from information_schema.columns to assist in producing the contents needed in the trigger definition

Not null if other entry is not null

I am writing a table and I need A to not be null if B is not null (see below). I am trying to write a CHECK however I am not sure how to write it. Any advise is much appreciated.
CREATE TABLE `Test`
`A` varchar(4) CHECK(?),
`B` varchar(4)
);
Don't bother with a CHECK constraint. According to the manual:
The CHECK clause is parsed but ignored by all storage engines.
You can instead use a trigger, as suggested in this thread. The test itself would be
(B is null) or (A is not null)
EDIT: Assuming you are using MySQL version 5.5 or later, here's how you can trigger an error signal when the condition is violated:
mysql> DELIMITER //
mysql> DROP TRIGGER IF EXISTS before_insert_trigger//
mysql> CREATE TRIGGER before_insert_trigger
-> BEFORE INSERT ON Customer FOR EACH ROW
-> BEGIN
-> DECLARE msg VARCHAR(255);
-> IF (NEW.B is not null) and (NEW.A is null) THEN
-> SET msg = concat('Error: Trying to insert a NULL A when B is not null: ',
-> cast(NEW.B as char));
-> SIGNAL sqlstate '45000' SET message_text = msg;
-> END IF;
-> END//
mysql> delimiter ;
(Note that I inverted the test because I want to test when it is violated, not when it is satisfied.) If you are using an earlier version of MySQL, you can use the suggestion provided in this answer or here.
MySQL does not enforce check constraints. So, the only way you can do this in MySQL (using one table) is to use triggers.
I think the check constraint syntax you want is:
check ((a is null and b is null) or (a is not null and b is not null))
although it might be:
check ((b is null) or (a is not null))
depending on whether you really mean "if" or "if and only if".

Mysql Error when creating trigger

error note:Error : You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '#sum int default 0;
set #sum=(select count(*) from inserted);
if #sum>1 then
' at line 5
and this is the code:
delimiter //
create trigger insert_only_one
after insert on sc
for each row
begin
declare #sum int default 0;
set #sum=(select count(*) from inserted);
if #sum>1 then
print('dont insert more than one record');
rollback transaction
end
The error note shows I have error at line 5.
I tried int(5) or just 'int', with or without default 0 still can't work.
You don't need to declare variable because you are using #sum variable that is initialized automatically..
you can't print anything inside a trigger...
Also your command rollback transaction is Invalid for MySQL.
Use only rollback with semicolon..

How to make MySQL produce an error if value not specified on NOT NULL Columns?

Let's assume that I have a NOT NULL column in a table,
How can I make MySQL to produce an error if such statement is used?
INSERT INTO tableName () VALUES ();
Thank you.
To set a column to not null use this syntax :
ALTER TABLE table_name
MODIFY column_name [data type] NOT NULL;
If you column is declared not null an error will be produced !!
If you want a customized error msg then you need to create trigger action !
Here is a trigger that can help you :
DELIMITER $$
CREATE TRIGGER trgBEFORE UPDATE ON `tbl`
FOR EACH ROW BEGIN
declare msg varchar(255);
IF (NEW.col1IS NULL ) THEN
set msg = concat('MyTriggerError: Trying to insert a null value );
signal sqlstate '45000' set message_text = msg;
ELSE
SET NEW.col1= NEW.col1);
END IF;
END$$
DELIMITER ;
I know this is late but it might help someone.
You can set the SQL mode, either in the configuration file or at runtime for current session.
To produce the error you need to enable strict sql mode with:
SET GLOBAL sql_mode = 'STRICT_ALL_TABLES'; or SET GLOBAL sql_mode = 'STRICT_TRANS_TABLES';
Here is a link for more information on sql modes.
How to make MySQL produce an error, when inserting a row to a table containing NOT NULL column, when not specifying a value to that column like in "INSERT INTO tableName () VALUES()".
Is there a way without a trigger?
This is possible without a trigger only when you define no default when defining a column. Also the same is applicable for alter ... column ...
Example 1:
create table ck_nn( i int not null );
insert into ck_nn values();
The above insert throws an error as no default is defined on the column.
Message can be something like Field 'i' doesn't have a default value.
Example 2:
create table ck_nn2( i2 int not null default 999 );
insert into ck_nn2 values();
The above insert won't throw any error as default value is defined on the column.
select * from ck_nn2;
+-----+
| i2 |
+-----+
| 999 |
+-----+
Example # SQL Fiddle

MySQL Syntax error near ''

I'm a lot more used to T-SQL than MySQL and it seems as though there are slight syntax issues that I just can't quite figure out. I'm getting an error message that seems quite meaningless to me and I would really appreciate it if someone could just tell me what I'm doing wrong here. Am I perhaps not allowed to do an UPDATE in an UPDATE TRIGGER?
The idea is that I want to just keep track of whether or not my current German log has been corrected or not and record the times based on whether or not I'm updating my portion of the log or my tutor is updating their portion.
My Code is:
DELIMITER //
CREATE TRIGGER updateTimes
AFTER UPDATE ON Logs
FOR EACH ROW
BEGIN
IF (Old.TimGerman <> New.TimGerman OR
Old.TimComment <> New.TimComment)
THEN
UPDATE Logs
SET DateUpdated = CURRENT_TIMESTAMP, Corrected = 0
WHERE LogID = New.LogID;
ELSE IF (Old.TutorGerman <> New.TutorGerman OR
Old.TutorComment <> New.TutorComment)
THEN
UPDATE Logs
SET DateMarked = CURRENT_TIMESTAMP, Corrected = 1
WHERE LogID = New.LogID;
END IF;
END //
DELIMITER ;
Me error message says:
SQL Error (1064): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax near '' at line 21.
Line 21 is 4th from the bottom: WHERE LogID = New.LogID;
Thanks very much for the help!
Syntactically your trigger is correct except on ELSE IF part. You need an extra END IF if you want to use it. Otherwise modify it as ELSEIF.
And also it seems you need a BEFORE trigger but not AFTER trigger.
Apart from that, calling the explicit update in an update trigger on the same table is meaning less as it would cause a circular event. Which is not supported and throws an error.
Change your trigger definition as below:
DELIMITER //
CREATE TRIGGER updateTimes BEFORE UPDATE ON Logs
FOR EACH ROW
BEGIN
IF ( OLD.LogID = New.LogID ) THEN
IF ( Old.TimGerman <> New.TimGerman OR
Old.TimComment <> New.TimComment )
THEN
SET NEW.DateUpdated = CURRENT_TIMESTAMP, NEW.Corrected = 0;
ELSEIF ( Old.TutorGerman <> New.TutorGerman OR
Old.TutorComment <> New.TutorComment )
THEN
SET NEW.DateMarked = CURRENT_TIMESTAMP, NEW.Corrected = 1;
END IF;
END IF;
END;
//
DELIMITER ;