hello I have a datetime column and I would like to put a time restriction on it how would I do this?
For example a range of time from 3:00:00 to 15:00:00 all data that fits this criteria is stored if not throw and error up and stop the entering of data in the column
In MySQL, you'd have to do this with a trigger on INSERT and UPDATE, so if someone tries to enter a value that doesn't meet your criteria, you raise a SIGNAL.
mysql> CREATE TABLE MyTable (
my_datetime DATETIME
);
mysql> DELIMITER ;;
mysql> CREATE TRIGGER MyTable_ins BEFORE INSERT ON MyTable
FOR EACH ROW BEGIN
IF (NOT TIME(NEW.my_datetime) BETWEEN '03:00:00' AND '15:00:00') THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'Time does not fall in the range allowed.'
END IF;
END;;
mysql> DELIMITER ;
I get the error if try to do something I shouldn't:
mysql> INSERT INTO MyTable SET my_datetime = '2017-01-13 18:00:00';
ERROR 1644 (45000): time does not fall in the range allowed
But it works if I choose a time that's allowed:
mysql> INSERT INTO MyTable SET my_datetime = '2017-01-13 11:00:00';
Query OK, 1 row affected (0.00 sec)
I did some digging and some reading. I tested some stuff out on my own server. It doesn't work. Then I found this answer:
CHECK constraint in MySQL is not working
Yep, it accepts a CHECK constraint as valid syntax, then completely ignores it.
And while I was testing and writing up, Bill has posted the correct answer for MySQL. Do what he says.
Related
I have an existing longtext column in a huge table (100MM rows) which includes json strings. Changing the type to json would lock the table. As an alternative I thought I could add json validation to the longtext column on create and update.
I do not have much experience with triggers. how would that work? I know it should be something like this:
TRIGGER `before_insert_user`
BEFORE INSERT
ON `users`
FOR EACH ROW
BEGIN
...
END;
TRIGGER `before_update_user`
BEFORE UPDATE
ON `users`
FOR EACH ROW
BEGIN
...
END;
Demo:
mysql> create table users (id serial primary key, properties longtext);
mysql> delimiter //
mysql> create trigger before_insert_user before insert on users for each row
-> begin
-> if not json_type(NEW.properties) = 'OBJECT' then
-> signal sqlstate '45000' set message_text = 'properties must be a valid JSON object';
-> end if;
-> end//
mysql> delimiter ;
mysql> insert into users set properties = 'yadda yadda';
ERROR 3141 (22032): Invalid JSON text in argument 1 to function json_type: "Invalid value." at position 0.
mysql> insert into users set properties = '["yadda yadda"]';
ERROR 1644 (45000): properties must be a valid JSON object
mysql> insert into users set properties = '{"yadda": "yadda"}';
Query OK, 1 row affected (0.01 sec)
However, I would change the data type to JSON. I would use pt-online-schema-change to make the alteration without locking the table.
That example at least ensures the longtext column is valid JSON format, and also is a JSON object (not an array or a scalar).
If you need the JSON to comply with a more precise format, read about JSON_SCHEMA_VALID().
How do I limit the total column to 10 columna and total rows to 500 data per table in Mysql
Like P.Salmon suggested,we can restrict users from altering table structures. This can be done by revoking their ALTER privilege on said tables. e.g revoke alter on testdb.thistable from thisuser; As to limit the total rows , we can use a trigger to check the existing row number every time a new row is to be inserted. If the limit is reached, abort the INSERT as well as raise a warning. The trigger looks like this:
delimiter //
drop trigger if exists check_row_limit ;
create trigger check_row_limit before insert on thistable for each row
begin
if (select count(*) from thistable) >= 500 then
signal SQLSTATE VALUE '99999' SET MESSAGE_TEXT = 'The number of rows has reached the limit. INSERT fails. ';
end if;
end//
delimiter ;
I want to create a trigger that rejects the insert of an underage employee, by underage I mean that his year of birth is 2004 or more.
I wrote the following code, it runs without errors but then it doesn't let me insert any employee because it says :
ERROR: Unknown column 'BIRTHDATE' IN 'field list'
When I drop the trigger everything works fine.
DELIMITER $$
CREATE TRIGGER REJECT_EMP
BEFORE INSERT ON EMPLOYEE
FOR EACH ROW
BEGIN
IF YEAR(BIRTHDATE) > 2003 THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'An error occurred';
END IF;
END$$
DELIMITER ;
You should be testing NEW.BIRTHDATE
'Within the trigger body, the OLD and NEW keywords enable you to access columns in the rows affected by a trigger.' https://dev.mysql.com/doc/refman/8.0/en/trigger-syntax.html
If you are running MySQL 8.0, I would recommend a check constraint rather than trigger logic.
alter table employee
add constraint chk_birthdate
check(year(birthdate) > 2003)
;
This enforces the same check that the trigger does, but the syntax is much shorter, and the logic is bundled directly in the definition of the table (so it is somehow easier to maintain). When an attempt is made to insert an offending row, you get the following error message:
Check constraint 'chk_birthdate' is violated.
I have to advice 2 changes in your trigger:
fist as #P.Salmon answered you should to use NEW.BIRTHDATE parameter
second use age instead birth-year in comparison
DELIMITER $$
CREATE TRIGGER REJECT_EMP
BEFORE INSERT ON EMPLOYEE
FOR EACH ROW
BEGIN
IF TIMESTAMPDIFF(YEAR, NEW.BIRTHDATE, NOW()) < 17 THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'An error occurred';
END IF;
END$$
DELIMITER ;
In general, the right way to check year() is on a day-by-day basis. So the logic that you want is:
BEGIN
IF BIRTHDATE > CURDATE() - INTERVAL 16 YEAR THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'An error occurred';
END IF;
END$$
This actually checks the date of birth. So someone can become an employee on their birthday.
I am not sure "16" is the right value, but your question doesn't explain it. It makes sense given the logic you have presented and that the year when you asked the question is 2020.
Hello everyone.
I have something weird with MySQL.
(Oh, first of all, sorry for my poor English.)
I've made the simple table and stored procedure. Table definition is...
CREATE TABLE numTable (
firstNum INT,
secondNum INT
);
INSERT INTO numTable (firstNum, secondNum) VALUES (1, 2);
and the SP is...
DELIMITER ;;
CREATE PROCEDURE updateNum (
IN num1 INT,
IN num2 INT
)
BEGIN
START TRANSACTION;
UPDATE numTable
SET firstNum = num1, secondNum = num2;
COMMIT;
END ;;
DELIMITER ;
Then I executed the SP like this...
CALL updateNum (3, 4);
And MySQL returns ...
0 row(s) affected
When I saw this result, I thought 'Is there any syntax error?' But, no. UPDATE query worked fine. numTable's data changed from (1, 2) to (3, 4).
And I also found this. I removed the 'START TRANSACTION;' and 'COMMIT' statement. and execute updateNum SP again. The result was ...
1 row(s) affected
This time, UPDATE query worked fine too. numTable's data has successfully changed.
Why this difference has happen? Is there a way I can get a right affected rows with using TRANSACTION statement?
I tested this at MySQL 5.6.27, MariaDB 10.0.21 and MariaDB 10.1.8 and results are same as above.
Thank you for read my question.
You will get the affected rows using
ROW_COUNT()
so, SELECT ROW_COUNT()
at the end
similar to #Leow's, but I have found that without a transaction, and without it happening immediately after the update, all is lost. So:
START TRANSACTION;
update ...
select row_count() into #theCount; -- saved for posterity, perhaps for use at end
COMMIT;
select #theCount;
I want to do the following query:
UPDATE `users` SET balance = (balance - 10) WHERE id=1
But if the balance will become a negative number I want an error to be returned. Any ideas on if this is possible?
If you do
UPDATE `users` SET balance = (balance - 10) WHERE id=1 and balance >=10
You should be able to detect that a row was not modified.
Note that while another answer suggests using an unsigned int column, this may not work:
Create a test table
create table foo(val int unsigned default '0');
insert into foo(val) values(5);
Now we attempt to subtract 10 from our test row:
update foo set val=val-10;
Query OK, 1 row affected, 1 warning (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 1
mysql> select * from foo;
+------------+
| val |
+------------+
| 4294967295 |
+------------+
This was on mysql 5.0.38
You can make the balance field of the users table an unsigned int:
ALTER TABLE `users` CHANGE `balance` `balance` INT UNSIGNED;
This sort of things is done by triggers. MySql have support for triggers only since 5.0.2.
DELIMITER $$
CREATE TRIGGER balance_check BEFORE INSERT ON user FOR EACH ROW
BEGIN
IF new.balance < #limit_value THEN
-- do something that causes error.
-- mysql doesn't have mechanism to block action by itself
END IF;
END $$
DELIMITER ;
Triggers in MySql are quite rudimentary. You have to hack things around to do some things (e.g. cause error).
I dont think you can do this with a simple query. you should use a mysql user defined function that manage that before update the row. or a trigger
Just a tip that wouldn't fit as a comment. I was just trying to subtract 32000 from 32047 (not a negative result) and was getting errors. Also confusing, I was getting BIGINT errors but my subtraction was on a SMALLINT column! (Which still makes no sense.)
If you're getting "out of range" errors even when your "balance" is positive, try adding "limit 1" to the end of your query. Maybe this is a bug in MySQL?
mysql> update posts set cat_id=cat_id-32000 where timestamp=1360870280;
ERROR 1690 (22003): BIGINT UNSIGNED value is out of range in '(`xxxxx`.`posts`.`cat_id` - 32000)'
mysql> update posts set cat_id=cat_id-32000 where timestamp=1360870280 limit 1;
Query OK, 1 row affected (6.45 sec)
Rows matched: 1 Changed: 1 Warnings: 0
In my case the timestamp is unique (I just checked to be sure) but not explicitly defined as unique when I created the table. So why is the "limit 1" here necessary? But who cares, it works!