Struggling to figure out how to set these constraints - mysql

I am currently setting up a few different tables for which I have to use certain constraints. I have been getting on okay but I am stuck with the following:
Limiting the 'Country' column to a choice between UK, USA and Australia
Creating the 'ImageFilename' column so that each record must have an extension of .JPG
Is there a specific constraint for these examples or is it a case of thinking outside the box here? I've tried to think of ways to use the current constraints I know but I'm stumped.
Any help would be much appreciated! Thanks in advance.
edit: would it perhaps be a CHECK constraint?

As of MySQL 8.0, MySQL doesn't support CHECK constraints.
Even if MySQL were to add support for CHECK constraints, I wouldn't use that as a solution, because when you eventually add New Zealand and Canada, or if you support .PNG in addition to .JPG, you'd have to redefine the constraints.
For the countries I would create a lookup table, and a foreign key constraint to restrict your country column.
CREATE TABLE Countries ( Country VARCHAR(75) PRIMARY KEY );
ALTER TABLE DifferentTable ADD FOREIGN KEY (Country) REFERENCES Countries(Country);
This allows you to support new countries simply by INSERTing a new country name into the Countries table.
For the image extension, I can think of two alternatives:
Define a trigger that throws a SIGNAL if the filename doesn't match the right extension.
CREATE TRIGGER ImageFilenameIns BEFORE INSERT ON DifferentTable
FOR EACH ROW
BEGIN
IF (SUBSTRING_INDEX(NEW.ImageFilename, -1) <> '.JPG') THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'You must use a .JPG filename';
END IF;
END
CREATE TRIGGER ImageFilenameUpd BEFORE UPDATE ON DifferentTable
FOR EACH ROW
BEGIN
IF (SUBSTRING_INDEX(NEW.ImageFilename, -1) <> '.JPG') THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'You must use a .JPG filename';
END IF;
END
Remember to set DELIMITER if you create this trigger using the mysql client or SQL script.
Using the trigger solution requires redefining the trigger if you someday want to support .PNG or other extensions.
Define a generated column to represent the file extension, and make sure that references a lookup table of allowed file extensions.
CREATE TABLE ImageFileExtensions ( Extension VARCHAR(3) PRIMARY KEY );
INSERT INTO ImageFileExtensions (Extension) VALUES ('JPG');
ALTER TABLE DifferentTable
ADD COLUMN ImageFilenameExtension VARCHAR(3) AS (SUBSTRING_INDEX(ImageFilename, -1)) STORED,
ADD FOREIGN KEY(ImageFilenameExtension) REFERENCES ImageFileExtensions(Extension);
With the latter solution, you can add support for a new file extension simply by INSERTing a new extension into the ImageFileExtensions table.

I'm not sure what is that you try to pull off but you can use IF in mysql:
* (for Creating):
CREATE TABLE IF NOT EXISTS
:row(row_id varchar(8) NOT NULL,
:country varchar(25) NOT NULL CHECK (country IN ('USA','UK','Australia')),
PRIMARY KEY (row_id));
Test whether two strings are the same and return "YES" if they are, or "NO" if not (for Selecting):
SELECT IF(STRCMP(":country","UK") = 0, "YES", "NO");
or:
SELECT :row, CASE :Country
WHEN 'USA' THEN 1
WHEN 'UK' THEN 2
WHEN 'Australia' THEN 3
ELSE 'NO'
END AS :row1, :row2, :row3
FROM :Table
but still you can do the same thing with PHP:
$country = $_GET['country'];
switch($country){
case "UK":
case "USA":
case "Australia":
Model::DB->Post($country, "Country");
break;
default:
App::Error->Err("Invalid input - " . $country);
}

Related

On mySql INSERT, how to throw an error only when existing duplicate differs

I have a table in my database with two columns, A and B.
A is the primary key, unique
B cannot be null
When I insert (newA, newB), I want to:
insert if newA doesn't exist
ignore if (newA, newB) already exists (no errors, no effect or overwrite same/existing pair)
return an error if a different couple exists with A = newA
This looks really simple, but I don't really see how to do that! I guess one way would be to set the primary key as (A,B), would that do it?
You can try this:
Create unique key (A, B). This will not allow the record with the same (A, B) combination to be inserted
Use INSERT IGNORE instead of INSERT so unique constrain you created on step 1 will not cause an error but will be silently ignored.
Manually check existence record with the same A value in before insert/update triggers and throw an error if such record already there. Something like this:
CREATE TRIGGER `tbl_before_insert` BEFORE INSERT ON `tbl` FOR EACH ROW BEGIN
IF EXISTS(SELECT * FROM `tbl` WHERE `tbl`.`A` = NEW.`A` AND `tbl`.`B` != NEW.`B`) THEN
SET #msg = CONCAT('Record with A=', NEW.A, ' already exists');
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = #msg;
END IF;
END
You can keep A as the Primary Key while making the B field UNIQUE.
Existing A Insert => It will throw an error (as it is the primary key).
If new A, new B => It won't be a problem.
If new A, old B => Because we made the B column unique, It'll throw an error.
Looking back at my old pending questions, the answer here is quite simple:
make (A,B) the key
create an index on A to make it unique
This will enforce the desired rules.

MySQL insert new value when no other like that

I am trying to solve the problem with insert new value into table. Primary key is ID not email... Before insert i need check other records for email value. If no one match inserted email, data is inserted. But i canot write correct code for that...
IF EXIST (SELECT email FROM users WHERE email = "email")
THEN
BEGIN
'We Have Records of this Customer'
END
ELSE
BEGIN
INSERT INTO users
VALUES (null,'email9','password9','forename9','lastname9')
END
END IF
Or:
IF SELECT COUNT(*) FROM users WHERE email = 'email' > 0
THEN
BEGIN
//email exist
END
ELSE
BEGIN
//inserting
END
END IF
This looks like a good use case for MySQL INSERT ... ON DUPLICATE KEY UPDATE syntax.
First of all, you want to create a UNIQUE constraint on column email. This is the proper way to represent your business rule:
ALTER TABLE users ADD CONSTRAINT users_unique_email UNIQUE (email);
Now, if the record that is about to be inserted would generate a duplicate on the primary key or on that UNIQUE constraint, then MySQL lets you turn the operation to an UPDATE instead.
In your case, since you want not to update anything in that case, you can simply re-assign the email column (which we know is the same here).
Consider:
INSERT INTO users
VALUES (null,'email9','password9','forename9','lastname9')
ON DUPLICATE KEY UPDATE SET email = 'mail9';
I cant comment, but I post this. I recommend making an external script like in python or java to do this for you by selecting * and parsing through the output because SQL itself does not have the power to do this.

MYSQL: data in one column depends on another column

I need to create a table where I have columns country and city.
If I put the country as 'USA', the city must be 'New York'. How to impose this constraint?
I tried but this affects data in other rows too:
UPDATE table1 SET city = IF(country = "USA", 'New York', '');
Also, if possible the constraint should be added while creating the table.
You can either do with the table definition like this.
CREATE TABLE table_name
(
country varchar(30),
state varchar(30),
CONSTRAINT check_state
CHECK ( state = CASE WHEN country ='usa' THEN 'new york' ELSE '' END )
);
Or you can add constraint using ADD CONSTRAINT after creating the table.
As per #Tim's comment, adding the code for trigger to achieve the same
DELIMITER |
CREATE TRIGGER `update_state`
AFTER INSERT ON `table_name` FOR EACH ROW
BEGIN
SET status = CASE WHEN new.country= 'usa' THEN 'New York' else ''
END;
DELIMITER ;
If we go by the logic of updating the table after each insert into the table, then what out need to do is :
UPDATE table1
SET city="new york"
WHERE country="usa";
Here the WHERE clause will take care that only tuples having country as usa are selected.
One of the best way to have the constraint would be using CHECK, but unfortunately CHECK constraints are ignored by MySQL as explained in a miniscule comment in the docs: CREATE TABLE
The CHECK clause is parsed but ignored by all storage engines.

How to set it so that 1 of 2 columns is NULL in MySQL

I am going to open by saying that what's happening appears to not be a good practice but it is what myself and some other engineers have arrived at.
I have a table which has a column containing foreign keys that map to products. I want to add a second column that is foreign keys mapping to product categories. However, I only want the product category to be filled in if the product itself isn't filled in. Likewise, if the product category is filled in, I don't want the product to be filled in.
Is there some sort of way to say:
if(colA is NULL)
colB is NOT NULL
if(colB is NULL)
colA is NOT NULL
Thanks for your help in advance.
Since it's MySQL, I guess creating a BEFORE INSERT OR UPDATE trigger is your best bet. Example code:
CREATE TRIGGER Validate_Trigger BEFORE INSERT OR UPDATE ON MyTable FOR EACH ROW
BEGIN
DECLARE msg VARCHAR(255);
IF new.colA IS NOT NULL AND new.colB IS NOT NULL THEN
-- Disallowed: yhrow exception from trigger
SET msg = 'Validate_Trigger: only one column can be NOT NULL: (colA, colB)';
SIGNAL sqlstate '45000' SET message_text = msg;
END IF;
END;
Usually in such cases you would create a CHECK constraint like this:
ALTER TABLE MyTable
ADD CONSTRAINT ConstraintName
CHECK (colA IS NULL OR colB IS NULL)
The problem here is that as of version 5.7, MySQL ignores CHECK constraints:
The CHECK clause is parsed but ignored by all storage engines.

Create function that checks if ID is unique

I want to make a function that checks if ID that i will insert is unique.
This is what i did:
-- the function has to return true if ID is unique and false if ID is allready used(not unique)
CREATE OR REPLACE FUNCTION Check_uniqueID
( p_ID IN STORE."ID"%TYPE)
RETURN BOOLEAN
AS
v_IsUnique BOOLEAN := FALSE;
BEGIN
ALTER TABLE STORE
ADD CONSTRAINT check_unique_id UNIQUE (p_ID);
-- how can i programm that he returns true if its unique and false if its not?
v_IsUnique := TRUE;
RETURN v_IsUnique;
END;
Thanks in advance!
The only good practice here is to use unique constraint and sequence. Add constraint (you need to do it once):
ALTER TABLE STORE ADD CONSTRAINT check_unique_id UNIQUE (p_ID);
Create sequence for IDs (once):
create sequence my_sequence;
Use it in INSERT statements:
insert into STORE (id, another_field)
values (my_sequence.nextval, 'some value for another field');
or:
insert into STORE (id, another_field)
select my_sequence.nextval, one_more_field
from some_table;
If you don't want to use a sequence, you need to process exception ORA-00001.
Using an ALTER TABLE in a function just to check if a constraint is being violated is really bad practice. You should have the constraint on the table permanently, and handle the ORA-00001: unique constraint (constraint_name) violated exception should it be thrown. You also seem to have misunderstood the syntax of ALTER TABLE since you're trying to add a constraint on the nonexistant column of STORE.p_ID.
However, if you must use a function to reproduce functionality that the database provides via check constraints (for pre-validation in the UI or similar), this would work:
CREATE OR REPLACE FUNCTION check_uniqueid (p_id IN NUMBER)
RETURN BOOLEAN
AS
li_count PLS_INTEGER;
BEGIN
SELECT COUNT(1)
INTO li_count
FROM store s
WHERE s.id = p_id;
RETURN li_count = 0;
END;
You can use a SELECT query to check whether the value already is stored in the table.