Using a check contraint in MySQL for controlling string length - mysql

I'm tumbled with a problem!
I've set up my first check constraint using MySQL, but unfortunately I'm having a problem. When inserting a row that should fail the test, the row is inserted anyway.
The structure:
CREATE TABLE user (
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
uname VARCHAR(10) NOT NULL,
fname VARCHAR(50) NOT NULL,
lname VARCHAR(50) NOT NULL,
mail VARCHAR(50) NOT NULL,
PRIMARY KEY (id),
CHECK (LENGTH(fname) > 30)
);
The insert statement:
INSERT INTO user VALUES (null, 'user', 'Fname', 'Lname', 'mail#me.now');
The length of the string in the fname column should be too short, but it's inserted anyway.
I'm pretty sure I'm missing something basic here.

MySQL doesn't enforce CHECK constraints, on any engine.
Which leads me to ask, why would you declare the fname column as VARCHAR(50), but want to enforce that it can only be 30 characters long?
That said, the only alternative is to use a trigger:
CREATE TRIGGER t1 BEFORE INSERT ON user
FOR EACH ROW BEGIN
DECLARE numLength INT;
SET numLength = (SELECT LENGTH(NEW.fname));
IF (numLength > 30) THEN
SET NEW.col = 1/0;
END IF;
END;

As mentioned above you have to use a trigger, MySQL doesn't support check, also when you have multiple statements inside your trigger block, like declaring variables or control flows, you need to start it with begin and end and enclose your trigger inside two delimiters:
Note: If you use MariaDB use // after the first delimiter and before the second delimiter, otherwise if you use MySQL use $$ instead.
delimiter //
create trigger `user_insert_trigger` before insert on `user` for each row
begin
declare maximumFnameLength int unsigned;
declare fNameLength int unsigned;
set maximumFnameLength = 30;
set fNameLength = (select length(new.fNameLength));
if (fNameLength > maximumFnameLength) then
signal sqlstate '45000'
set message_text = 'First name is more than 30 characters long.';
end if;
end
//
delimiter ;

Related

MYSQL TRIGGER with IF condition

I'm a noob im MYSQL and I'm trying to make a trigger who will auto-fill 2 fields(customername, customersurname) in an invoice table. Can anyoane explain me please what I do wrong in that trigger? Thank you very much! :)
CREATE TABLE customer(
customerID INT(6) PRIMARY KEY AUTO_INCREMENT,
customername VARCHAR(20) NOT NULL,
customersurname VARCHAR(20) NOT NULL,
customeraddress VARCHAR(200)
);
CREATE TABLE invoice(
invoiceID INT (6) Primary KEY AUTO_INCREMENT,
customerID INT (6) REFERENCES customer(customerID),
customername VARCHAR(20) REFERENCES customer(customername),
invoicedate DATE,
orderID INT(6) REFERENCES orders(orderID),
products VARCHAR(200) REFERENCES orderlines(productname),
FOREIGN KEY(orderID) REFERENCES orders(orderID)
);
CREATE TRIGGER autofill_invoice BEFORE INSERT ON invoice FOR EACH ROW
BEGIN
IF (new.customerID = customer.customerID,
new.customersurname = customer.customersurname )
THEN
SET new.customername = customer.customername,
new.customersurname = customer.customersurname;
END IF;
END;
As mysql documentation on defining stored programs explains:
If you use the mysql client program to define a stored program containing semicolon characters, a problem arises. By default, mysql itself recognizes the semicolon as a statement delimiter, so you must redefine the delimiter temporarily to cause mysql to pass the entire stored program definition to the server.
To redefine the mysql delimiter, use the delimiter command. The
following example shows how to do this for the dorepeat() procedure
just shown. The delimiter is changed to // to enable the entire
definition to be passed to the server as a single statement, and then
restored to ; before invoking the procedure. This enables the ;
delimiter used in the procedure body to be passed through to the
server rather than being interpreted by mysql itself.
So, use the delimiter command when you define the stored procedure:
DELIMITER //
CREATE TRIGGER autofill_invoice BEFORE INSERT ON invoice FOR EACH ROW
BEGIN
IF (new.customerID = customer.customerID,
new.customersurname = customer.customersurname )
THEN
SET new.customername = customer.customername,
new.customersurname = customer.customersurname;
END IF;
END//
DELIMITER ;
Next time pls provide exact error description in your question!

syntax error : 1064 , when creating a stored procedure

CREATE table parent_user
( userid int auto_increment PRIMARY KEY,
Username varchar(100) NOT NULL,
Password varchar(200) NOT NULL,
Email varchar(200) NOT NULL
);
EDIT : OK so I made some changes:
CREATE PROCEDURE `parent_reg` (
pUserName varchar(100)
pPassword varchar(200)
pEmail varchar(200)
)
as
Begin
Declare Count int
Declare ReturnCode int
Select Count = Count(Username)
from parent_user where Username = #Username
If Count > 0
Begin
Set ReturnCode = -1
End
Else
Begin
Set ReturnCode = 1
insert into parent_user values
(pUserName, pPassword, pEmail)
End
Select pReturnCode as ReturnValue
End
But I still got the same error-
Error 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 'pPassword varchar(200) pEmail varchar(200) ) ....'
The syntax error is at 'pPassword varchar(200)'
The code in the question is invalid syntax for MySQL Stored Procedure. It looks more like Microsoft SQL Server (Transact SQL) syntax.
Some observations:
MySQL procedure variables cannot start with # because that character is reserved for user-defined variables.
MySQL doesn't use a NVARCHAR type. The setting of the character_set_client variable in the session (at the time the procedure is created) is what controls the characterset of the procedure variables.
The line select * from parent_user, before the CREATE PROCEDURE looks entirely out of place.
Missing semicolons (statement terminators).
The INSERT is for a table with four columns; there are only three values and no column list.
If the goal is to create a stored procedure in MySQL, we'd need syntax closer to this:
DELIMITER $$
CREATE PROCEDURE parent_reg(p_username VARCHAR(100),
p_password VARCHAR(200), p_email VARCHAR(200)
)
BEGIN
DECLARE mycount INT;
DECLARE myreturncode INT;
SELECT COUNT(pu.username)
INTO mycount
FROM `parent_user` pu
WHERE pu.username = p_username;
IF (mycount > 0 ) THEN
SET myreturncode = -1;
ELSE
SET myreturncode = 1;
INSERT INTO `parent_user` (`username`, `password`, `email`)
VALUES (p_username, p_password, p_email);
END IF;
SELECT myreturncode AS `ReturnValue`;
END$$
DELIMITER ;
Maybe it's your database's collation. When installing SQL Server and choose your default collation, there's a "case sensitivity" checkbox. Certain collations are case sensitive and will affect your queries (and stored procedures).
A lot of vendors don't test their products on servers with case sensitive collations, which leads to runtime errors.
So just try to choose between "Username" and "UserName"

MySQL Insert/Update Trigger with AUTO_INCREMENT

So, I've got a table roughly as follows:
CREATE TABLE CUSTOMER (
CUSTID INT NOT NULL AUTO_INCREMENT,
NAME CHAR (45),
CONSTRAINT CUSTOMER_PRIMARY_KEY PRIMARY KEY (CUSTID))
AUTO_INCREMENT = 100;
I'm auto incrementing the CUSTID so that it's possible to simply insert a name and have it created with the next available CUSTID. However, I also want to ensure that it isn't possible to set the CUSTID value to zero, either on creation of the row or on update so I've constructed the following trigger:
DELIMITER $$
CREATE TRIGGER `custid_before_insert` BEFORE INSERT ON `CUSTOMER`
FOR EACH ROW
BEGIN
IF (NEW.CUSTID) <= 0 THEN
SIGNAL SQLSTATE '12345'
SET MESSAGE_TEXT = 'Check constraint on CUSTOMER.CUSTID failed';
END IF;
END$$
CREATE TRIGGER `custid_before_update` BEFORE UPDATE ON `CUSTOMER`
FOR EACH ROW
BEGIN
IF (NEW.CUSTID) <= 0 THEN
SIGNAL SQLSTATE '12345'
SET MESSAGE_TEXT = 'Check constraint on CUSTOMER.CUSTID failed';
END IF;
END$$
DELIMITER ;
Unfortunately in my blissful ignorance of how AUTO_INCREMENT worked, I've come to the conclusion that this is the wrong way to go about this. Trying to insert a customer with no CUSTID value is tripping the trigger causing the insert to fail which I presume is due to the value being a zero before insertion when AUTO_INCREMENT assigns it a value.
Would the best way to do this really be to change the trigger to occur after the insert and delete the row or is there a better way to do this to just throw an error?
The insert trigger is not needed.
From Auto_Increment
No value was specified for the AUTO_INCREMENT column, so MySQL
assigned sequence numbers automatically. You can also explicitly
assign 0 to the column to generate sequence numbers.
E.G.
create table t(id int auto_increment, primary key(id));
insert into t(id) values (0);
select id from t;
# 1
Update:
To allow the insert to complete when CUSTID is not specified,
INSERT INTO customer(name) VALUES('Chuck');
check for null in the trigger:
IF NEW.CUSTID IS NOT NULL AND NEW.CUSTID <= 0 THEN
Inserting '0' into an auto-increment column causes it to increment the same as inserting NULL, so you really neither need nor want the INSERT trigger. Try it with just the UPDATE trigger.

Update value each time new rows is created

I have two tables: config(last_inserted_id) and element(id) is there any chance to get the last inserted id any time when new rows are created in element table and execute a update in column last_inserted_id at config table?
I have wrote this:
CREATE TRIGGER UPDATE_CONFIG_VALUES AFTER INSERT ON element
BEGIN
UPDATE config SET last_inserted_id = last_insert_id();
END;
END;
Is that right? What happen if I delete a row in element table? Should the value get updated in config table or not" How I avoid this?
config table
CREATE TABLE IF NOT EXISTS `cmplatform`.`isrl_config` (
`id` INT NOT NULL AUTO_INCREMENT,
`logo_address` VARCHAR(250) NOT NULL,
`name` VARCHAR(250) NOT NULL,
`rif` VARCHAR(20) NOT NULL,
`address` TEXT NOT NULL,
`phone` VARCHAR(14) NOT NULL,
`last_retention_number` INT NOT NULL,
PRIMARY KEY (`id`))
ENGINE = InnoDB
TEST this ( Not tested)
CREATE TRIGGER element_inserted_tg AFTER INSERT ON element
FOR EACH ROW
BEGIN
DECLARE count INT;
SELECT COUNT(1) INTO count FROM config;
IF count == 1
UPDATE config SET last_inserted_id = NEW.id;
ELSE
INSERT INTO config VALUES (NEW.id);
END IF;
END;
CREATE TRIGGER element_deleted_tg AFTER DELETE ON element
FOR EACH ROW
BEGIN
UPDATE config SET last_inserted_id = (SELECT MAX(id) FROM element);
END;
Trigger is traditional way to do this requirement but not unique way. Another way is process it in your data access code. Let say that you have a DAO method to create new Element, you can get the id of element created and update it into last_inserted_id. If you do this way, you have to makesure there is ONLY ONE THREAD calling insert element method at a time.

How to prevent creation of records where the value of two fields is the same?

I have the following table.
CREATE TABLE people(
first_name VARCHAR(128) NOT NULL,
nick_name VARCHAR(128) NULL
)
I would like to prevent people from having their nickname be the same as their firstname if they attempt that insertion. I do not want to create an index on either of the columns just a rule to prevent the insertion of records where the first_name and nick_name are the same.
Is there a way to create a rule to prevent insertion of records where the first_name would equal the nick_name?
CREATE TRIGGER `nicknameCheck` BEFORE INSERT ON `people` FOR EACH ROW begin
IF (new.first_name = new.nick_name) THEN
SET new.nick_name = null;
END IF;
END
Or you can set first_name to NULL which will cause SQL error and you can handle it and show some warning.
You only need triggers for BEFORE INSERT and BEFORE UPDATE. Let these check the values and abort the operation, if they are equal.
Caveat: On older but still widely used versions of MySQL (before 5.5 IIRC) you need to do something bad, such as read from the written table or easier read from an inexistant table/column (in order to abort).
AFTER INSERT trigger to test and remove if same ...
CREATE TABLE ek_test (
id INT PRIMARY KEY NOT NULL AUTO_INCREMENT,
one INT NOT NULL,
two INT NOT NULL
);
delimiter //
CREATE TRIGGER ek_test_one_two_differ AFTER INSERT ON ek_test
FOR EACH ROW
BEGIN
IF (new.one = new.two) THEN
DELETE FROM ek_test WHERE id = new.id;
END IF;
END//
delimiter ;
INSERT INTO ek_test (one, two) VALUES (1, 1);
SELECT * FROM ek_test;
NOTE you will also need AFTER UPDATE trigger.