mysql add unique multi column with no check actual data - mysql

For an mysql v8.0.18 project with mariaDb 10.4.10
I would like add to my existing table an unique constraint for multi columns
ALTER TABLE 'new_purchasseorder' ADD UNIQUE ('created', 'fk_customer_id', 'fk_removal_id', 'fk_recipient_id')
but would like no check for old datas
something like that:
where id > 3869
i also tried the SET FOREIGN_KEY_CHECKS=0; but nor working in this case.
is it possible ?
My table looks like:

You can't do this with a unique constraint as far as I know, because, as you have already discovered, such a constraint will be applied to the entire table, regardless of id value. One workaround might be to use a before insert trigger, which does the assertion:
DELIMITER //
CREATE TRIGGER contacts_before_insert
BEFORE INSERT ON new_purchasseorder FOR EACH ROW
BEGIN
IF EXISTS (SELECT 1 FROM new_purchasseorder
WHERE created = NEW.created AND
fk_customer_id = NEW.fk_customer_id AND
fk_removal_id = NEW.fk_removal_id AND
fk_recipient_id = NEW.fk_recipient_id)
THEN
signal sqlstate '45000';
END IF;
END; //
DELIMITER ;
This insert trigger would cause any insert incoming with what your unique index defines as duplicate data to fail with an error, effectively blocking that insert.
A better long term (and easier) strategy might be to just fix your old data so that it can pass the requirements of the unique constraint.

Starting version 8.0.13, MySQL supports functional key parts - basically indexes on expression. Assuming that all 4 columns are non-nullable, you can do:
create unique index idx_new_purchaseorder on new_purchaseorder (
(
case when id > 3869
then concat_ws('$$', created, fk_customer_id, fk_removal_id, fk_recipient_id)
end
)
)
The case expression filters on id values, and generates a concatenated string that should be unique for rows that comply to the filter. I used some fancy characters to avoid "fake" duplicates.
Demo on DB Fiddle

Related

Field containing numbers and letters [duplicate]

Suppose I have an attribute called phone number and I would like to enforce certain validity on the entries to this field. Can I use regular expression for this purpose, since Regular Expression is very flexible at defining constraints.
Yes, you can. MySQL supports regex (http://dev.mysql.com/doc/refman/5.6/en/regexp.html) and for data validation you should use a trigger since MySQL doesn't support CHECK constraint (you can always move to PostgreSQL as an alternative:). NB! Be aware that even though MySQL does have CHECK constraint construct, unfortunately MySQL (so far 5.6) does not validate data against check constraints. According to http://dev.mysql.com/doc/refman/5.6/en/create-table.html: "The CHECK clause is parsed but ignored by all storage engines."
You can add a check constraint for a column phone:
CREATE TABLE data (
phone varchar(100)
);
DELIMITER $$
CREATE TRIGGER trig_phone_check BEFORE INSERT ON data
FOR EACH ROW
BEGIN
IF (NEW.phone REGEXP '^(\\+?[0-9]{1,4}-)?[0-9]{3,10}$' ) = 0 THEN
SIGNAL SQLSTATE '12345'
SET MESSAGE_TEXT = 'Wroooong!!!';
END IF;
END$$
DELIMITER ;
INSERT INTO data VALUES ('+64-221221442'); -- should be OK
INSERT INTO data VALUES ('+64-22122 WRONG 1442'); -- will fail with the error: #1644 - Wroooong!!!
However you should not rely merely on MySQL (data layer in your case) for data validation. The data should be validated on all levels of your app.
MySQL 8.0.16 (2019-04-25) and MariaDB 10.2.1 (2016-04-18) now not only parse CHECK constraint but also enforces it.
MySQL: https://dev.mysql.com/doc/refman/8.0/en/create-table-check-constraints.html
MariaDB: https://mariadb.com/kb/en/constraint/
Actually, we can can set regular expression within check constraints in MySQL.
Eg.,:
create table fk
(
empid int not null unique,
age int check(age between 18 and 60),
email varchar(20) default 'N/A',
secondary_email varchar(20) check(secondary_email RLIKE'^[a-zA-Z]#[a-zA-Z0-9]\.[a-z,A-Z]{2,4}'),
deptid int check(deptid in(10,20,30))
)
;
This INSERT query will work:
insert into fk values(1,19,'a#a.com','a#b.com', 30);
This INSERT query will not work:
insert into fk values(2,19,'a#a.com','a#bc.com', 30);

"Not include" Constraint?

Thanks for reading this.
I have tables like this in MySQL:
Device table has a list of name. And ReservedName table has a list of "reserved" name list.
As you may see, my design concept is to make Name value of Device table SHOULD NOT one of Name in ReservedName.
I could easily implement this relation by add a few SQL statement when I do INSERT operation to Device table. But I am wondering if there is something like "Not one of" constraint in the table schema? Maybe opposite meaning of FOREIGN KEY? It is also welcome if there is any other way to make that relationship.
You can create a BEFORE INSERT triggers which can either cause an error or set the field to its default value when the requirements of the data are not met.
In your case you can create a trigger, which will raise error, if your validation fails, something like following:
CREATE TRIGGER `validate_before_insert` BEFORE INSERT ON `Device`
FOR EACH ROW
BEGIN
IF EXISTS (SELECT* FROM ReservedName WHERE Name = new.Name) THEN
SIGNAL SQLSTATE '12345'
SET MESSAGE_TEXT := 'check constraint on Device.Name failed';
END IF;
END
You can read more about MySQL triggeres in documentation.

How to create a CHECK constraint in MySQL that requires negative values?

I want to alter an integer column on a table to add a check constraint preventing the value from being zero or a positive number. For example:
CREATE TABLE example (id INTEGER)
ALTER TABLE example ADD CONSTRAINT chk_negID CHECK (id<0)
MySQL happily complies with these yet then allows the following:
INSERT INTO example VALUES (-1);
INSERT INTO example VALUES (1);
Are my constraints not actually being added? Is there a way to list constraints that have been added to a table after it was created?
It's not supported by mysql (even though it accepts it as a valid syntax)
The CHECK clause is parsed but ignored by all storage engines
http://dev.mysql.com/doc/refman/5.5/en/create-table.html
Here is something that would work, but may be hard to debug. This is a trigger:
DELIMITER $$;
CREATE TRIGGER my_trigger AFTER INSERT ON my_table
FOR EACH ROW
BEGIN
IF(OLD.id <= 0)
THEN
DELETE FROM my_table where id = OLD.id;
END IF;
END

Is it Possible to Enforce Data Checking in MySQL using Regular expression

Suppose I have an attribute called phone number and I would like to enforce certain validity on the entries to this field. Can I use regular expression for this purpose, since Regular Expression is very flexible at defining constraints.
Yes, you can. MySQL supports regex (http://dev.mysql.com/doc/refman/5.6/en/regexp.html) and for data validation you should use a trigger since MySQL doesn't support CHECK constraint (you can always move to PostgreSQL as an alternative:). NB! Be aware that even though MySQL does have CHECK constraint construct, unfortunately MySQL (so far 5.6) does not validate data against check constraints. According to http://dev.mysql.com/doc/refman/5.6/en/create-table.html: "The CHECK clause is parsed but ignored by all storage engines."
You can add a check constraint for a column phone:
CREATE TABLE data (
phone varchar(100)
);
DELIMITER $$
CREATE TRIGGER trig_phone_check BEFORE INSERT ON data
FOR EACH ROW
BEGIN
IF (NEW.phone REGEXP '^(\\+?[0-9]{1,4}-)?[0-9]{3,10}$' ) = 0 THEN
SIGNAL SQLSTATE '12345'
SET MESSAGE_TEXT = 'Wroooong!!!';
END IF;
END$$
DELIMITER ;
INSERT INTO data VALUES ('+64-221221442'); -- should be OK
INSERT INTO data VALUES ('+64-22122 WRONG 1442'); -- will fail with the error: #1644 - Wroooong!!!
However you should not rely merely on MySQL (data layer in your case) for data validation. The data should be validated on all levels of your app.
MySQL 8.0.16 (2019-04-25) and MariaDB 10.2.1 (2016-04-18) now not only parse CHECK constraint but also enforces it.
MySQL: https://dev.mysql.com/doc/refman/8.0/en/create-table-check-constraints.html
MariaDB: https://mariadb.com/kb/en/constraint/
Actually, we can can set regular expression within check constraints in MySQL.
Eg.,:
create table fk
(
empid int not null unique,
age int check(age between 18 and 60),
email varchar(20) default 'N/A',
secondary_email varchar(20) check(secondary_email RLIKE'^[a-zA-Z]#[a-zA-Z0-9]\.[a-z,A-Z]{2,4}'),
deptid int check(deptid in(10,20,30))
)
;
This INSERT query will work:
insert into fk values(1,19,'a#a.com','a#b.com', 30);
This INSERT query will not work:
insert into fk values(2,19,'a#a.com','a#bc.com', 30);

Unique index not based on words order?

Is it possibile to have a mysql UNIQUE index on a varchar field not based on the words order?
I mean if there is a row with key1 key2, and I try to insert key2 key1 mysql should throw an error.
You could achieve this effect by adding before insert and before update triggers to the table; they could check whether a row with the fields reversed exists or not before inserting/updating if it doesn't, or forcing an error if it does.
See here for more information.
If you set the indexed to UNIQUE, you will not be able to enter the identical data twice - numbers, words or otherwise.
If you wish to achieve something otherwise, you'll have to set a new non-unique field in your database then you'll be able achieve it using external code and queries (PHP, C#).
Create a trigger to do this.
i.e.
CREATE TRIGGER trigger_name BEFORE INSERT ON table_name
FOR EACH ROW
BEGIN
IF ((SELECT COUNT(*) FROM table WHERE col2=NEW.col1 OR col1=NEW.col2) <> 0)
THEN
CALL this_procedure_does_not_exist();
END IF
END