MySQL Check Constraints - mysql

I have a database table like so:
-- Table KNOWN_AS
create table `known_as`(
id int not null auto_increment,
person_id int not null,
default_name boolean not null,
first_name varchar(100) not null,
middle_name_1 varchar(100),
middle_name_2 varchar(100),
middle_name_3 varchar(100),
last_name varchar(100),
primary key(id),
foreign key(person_id) references `person`(id)
) engine=innodb;
When inserting values, I want to check that each unique "person_id" has exactly one true "default_name".
All googling etc I've done so far has resulted in pages explaining how to keep a value non-negative, or how to make sure a value is unique - not how to check one value is unique amongst multiple entries (but not ALL).
Any help / pointers much appreciated!!

I want to check that each unique "person_id" has exactly one true "default_name"
Why not store default_name as a NOT NULL column in the person table?
how to check one value is unique amongst multiple entries (but not ALL)
Define a UNIQUE index over the composite (person_id, default_name):
ALTER TABLE known_as ADD UNIQUE INDEX (person_id, default_name);

Related

Can't create table 'student.#sql-f40_3' (errno: 150)

Table 1
create table personal(
id int not null auto_increment unique,
name char(20) not null,
age int not null,
city varchar(20) not null default 'Delhi'
);
insert into personal(name,age,city) values
('anubhav',22,'delhi'),
('rohit',24,'agra');
Table 2
create table applications(
app_id int(5) not null auto_increment unique,
city varchar(10) not null default 'Delhi'
);
insert into applications(city) values
('kolkata'),
('mumbai'),
('mumbai'),
('delhi'),
('agra'),
('agra');
Then i apply foreign key here with the help of Alter command-
alter table personal add foreign key(city) references applications(app_id)
but i am getting an error: ERROR 1005 (HY000): Can't create table 'student.#sql-f40_3' (errno: 150)
MySQL specifies:
Conditions and Restrictions
1.Corresponding columns in the foreign key
and the referenced key must have similar data types. The size and sign
of fixed precision types such as INTEGER and DECIMAL must be the same.
The length of string types need not be the same. For nonbinary
(character) string columns, the character set and collation must be
the same.
2.MySQL requires indexes on foreign keys and referenced keys so that foreign key checks can be fast and not require a table scan. In the
referencing table, there must be an index where the foreign key
columns are listed as the first columns in the same order. Such an
index is created on the referencing table automatically if it does not
exist. This index might be silently dropped later if you create
another index that can be used to enforce the foreign key constraint.
index_name, if given, is used as described previously.
The data type must be the same.
You could do:
alter table personal add foreign key(city) references applications(city)
But, the columns on both tables should be indexed.
See here
you desing in not normalized
your personal table should only reference the id.
City name in the applications should be unique, so i added it in the create table, there is no need for two or more delhis in a table(see normalisation)
If you really want to use in personal the city name, you must like i already made refernece the coty name of appcations or define a KEY for that column.
Further the datatyoes of the columns must always be the saem in both table for the foreign key
create table personal(
id int not null auto_increment unique,
name char(20) not null,
age int not null,
city int not null default 0
);
create table applications(
app_id int not null auto_increment primary key,
city varchar(10) not null unique default 'Delhi'
);
alter table personal add foreign key(city) references applications(app_id)
You have small bugs such as not putting null in the insert for the autoincrement and if it is primary key you should not put not null.
Table personal
create table personal(
id int auto_increment primary key,
name char(20) not null,
age int not null,
city varchar(20) not null default 'Delhi'
);
insert into personal values (null,'anubhav',22,'delhi'),
(null,'rohit',24,'agra');
Table applications
create table applications(
app_id int(5) auto_increment primary key,
city varchar(10) not null default 'Delhi'
);
insert into applications values(null,'kolkata'),
(null,'mumbai'),
(null,'mumbai'),
(null,'delhi'),
(null,'agra'),
(null,'agra');
Alter table
alter table personal add foreign key(city) references applications(app_id)

Unable to establish multiple foreign keys on mysql, specifically tested on mariaDB and w3schools online mysql

I was about to create two tables (1st table: fooditem_tbl & 2nd table: orderitem_tbl). I was planning to create 2 foreign keys (ITEM_NAME,UNIT_PRICE) on the 2nd table. I wasn't able to run the query of the 2nd table(orderitem_tbl), due to an error which is near at "INDEX". I kept looking at my query, and I still don't know what's the cause of the error.
My first table , this one works
CREATE TABLE FOODITEM_TBL
(ITEM_ID INT AUTO_INCREMENT,
ITEM_NAME VARCHAR(50) UNIQUE,
UNIT_PRICE DOUBLE UNSIGNED,
ITEM_QUANTITY INT UNSIGNED,
IN_STOCK BOOLEAN,
PRIMARY KEY (ITEM_ID, ITEM_NAME, UNIT_PRICE));
The 2nd table, which is below fails to create
CREATE TABLE ORDERITEM_TBL(
ORDER_ID INT AUTO_INCREMENT,
ITEM_NAME VARCHAR(50) UNIQUE,
UNIT_PRICE DOUBLE UNSIGNED,
ITEM_QUANTITY INT UNSIGNED,
CUSTOMER_NAME VARCHAR(50),
ADDRESS VARCHAR(50),
CONTACT_NUMBER VARCHAR(50),
PRIMARY KEY (ORDER_ID),
INDEX (ITEM_NAME,UNIT_PRICE),
FOREIGN KEY (ITEM_NAME,UNIT_PRICE) REFERENCES
FOODITEM_TBL(ITEM_NAME,UNIT_PRICE)
) ENGINE = InnoDB;
P.S Please help
Q: What is causing the error? How can I fix it?
For InnoDB, there must be an index on the target table, on the target column(s). Datatypes of the referencing foreign key column(s) must match the datatypes of the target column(s).
Before creating the foreign key constraint, make sure a suitable index exists on the target table, e.g.
CREATE UNIQUE INDEX `FOODITEM_TBL_UX3` ON `FOODITEM_TBL` (`ITEM_NAME`, `UNIT_PRICE`) ;
Given that ITEM_NAME is unique in the target table, we know that the combination of ITEM_NAME and UNIT_PRICE will also be unique. I'm not sure why we wouldn't just define a foreign key constraint on just ITEM_NAME, but that doesn't really address the question that was asked.
Personally, I would avoid floating point datatypes (e.g. DOUBLE) for columns involved in foreign key constraints.

phpmyadmin shows error double increment with two column not possible

So I tried to make these following two tables with phpmyadmin.
create table category (
catId int identity(1,1),
catName varchar(20),
Constraint pk_category
PRIMARY KEY(catId))
create table subcategory (
subCatId INT IDENTITY(1,1),
catId INT,
subCatName VARCHAR(20),
CONSTRAINT pk_subcategory
PRIMARY KEY(catId,subCatId),
CONSTRAINT fk_subcat_cat
FOREIGN KEY(catID)
REFERENCES category(catId))
When creating the subcategory it shows this query error:
1075 - Incorrect table definition; there can be only one auto column and it must be defined as a key .
There aren't two auto incremented columns in subcategory table, only the 'subCatId' is. What should be done?
It is only available for the MyISAM Storage Engine Only one numeric
auto_increment value is allowed. Each auto_increment must be have
associated column to define uniqueness from other auto_increment
values within the same table.
This ref link may hep you more about this.
Note:
Don't do that, don't have a second auto incremented column at all. Do you really need a second auto incremented value? What for? A
description of the actual problem you are trying to solve would help
others help you better. I think you have only told here how you tried
to solve a problem and not what the actual problem is.
This is what MySQL states
There can be only one AUTO_INCREMENT column per table, it must be
indexed, and it cannot have a DEFAULT value. An AUTO_INCREMENT column
works properly only if it contains only positive values. Inserting a
negative number is regarded as inserting a very large positive number.
This is done to avoid precision problems when numbers “wrap” over from
positive to negative and also to ensure that you do not accidentally
get an AUTO_INCREMENT column that contains 0.
So according to your requirement you've following choices.
Make subCatId as Primary Key
Or Make the column as Unique
You appear to be using SQL Server syntax, but run against MySQL. Use the correct syntax and it should work:
CREATE TABLE category (
catId INT NOT NULL AUTO_INCREMENT,
catName VARCHAR(20),
PRIMARY KEY (catId)
)
CREATE TABLE subcategory (
subCatId INT NOT NULL AUTO_INCREMENT,
catId INT,
subCatName VARCHAR(20),
FOREIGN KEY (catId) REFERENCES category (catId),
PRIMARY KEY (subCatId)
);
IDENTITY in SQL Server roughly corresponds to AUTO_INCREMENT in MySQL.
You have
Constraint pk_category
PRIMARY KEY(catId)
Instead, just say
PRIMARY KEY(catId)

One field with two references in MySQL

I have three tables:
CREATE TABLE Address (
ResidentID CHAR(5) NOT NULL,
Location varchar(255) NOT NULL,
KEY ResidentID(ResidentID)
);
CREATE TABLE Customer (
CustomerID CHAR(5) NOT NULL,
ContactName varchar(40) NOT NULL,
PRIMARY KEY (CustomerID)
);
CREATE TABLE Supplier (
SupplierID CHAR(5) NOT NULL,
SupplierName varchar(40) NOT NULL,
PRIMARY KEY (SupplierID)
);
I want to store CustomerID and SupplierID in the Address.ResidentID field with using of foreign keys:
ALTER TABLE Address ADD CONSTRAINT fk_CustomerID1 FOREIGN KEY(ResidentID) REFERENCES Customer(CustomerID);
ALTER TABLE Address ADD CONSTRAINT fk_SupplierID1 FOREIGN KEY(ResidentID) REFERENCES Supplier(SupplierID);
But second 'ALTER TABLE' raises Error: relation already exists
Any suggestions?
Data example:
CustomerID ContactName
C0001 Den
SupplierID ContactName
S0001 John
So Address table should contains:
ResidentID Location
C0001 Alaska
S0001 Nevada
You need to either reference addresses from the Customer / Supplier (if they only have one) or two different columns.
The reason you see in this SQLFiddle You cannot INSERT the required columns into the Address table if the ResidentID references BOTH tables. You could only insert lines that would match the contents of Customer AND Supplier but you want an OR connection that you can't create that way.
(Note: In my solutions I assume addresses to be optional. As Tom pointed out in the comments that may not be what you wanted, or expected. Make sure to mark the FK Columns in the first solution as NOT NULL if you want addresses to be mandatory, its more complicated for the second one. You have to mind the correct insertion order then.)
Either:
CREATE TABLE Address (
AddressID CHAR(5) NOT NULL,
Location varchar(255) NOT NULL,
PRIMARY KEY (AddressID)
);
CREATE TABLE Customer (
CustomerID CHAR(5) NOT NULL,
AddressID CHAR(5),
ContactName varchar(40) NOT NULL,
PRIMARY KEY (CustomerID)
);
CREATE TABLE Supplier (
SupplierID CHAR(5) NOT NULL,
AddressID CHAR(5),
SupplierName varchar(40) NOT NULL,
PRIMARY KEY (SupplierID)
);
ALTER TABLE Customer ADD CONSTRAINT fk_AddressID_Cust FOREIGN KEY(AddressID) REFERENCES Address(AddressID);
ALTER TABLE Supplier ADD CONSTRAINT fk_AddressID_Supp FOREIGN KEY(AddressID) REFERENCES Address(AddressID);
or
CREATE TABLE Address (
CustomerID CHAR(5),
SupplierID CHAR(5),
Location varchar(255) NOT NULL,
PRIMARY KEY (CustomerID, SupplierID)
);
CREATE TABLE Customer (
CustomerID CHAR(5) NOT NULL,
ContactName varchar(40) NOT NULL,
PRIMARY KEY (CustomerID)
);
CREATE TABLE Supplier (
SupplierID CHAR(5) NOT NULL,
SupplierName varchar(40) NOT NULL,
PRIMARY KEY (SupplierID)
);
ALTER TABLE Address ADD CONSTRAINT fk_CustomerID1 FOREIGN KEY(CustomerID) REFERENCES Customer(CustomerID);
ALTER TABLE Address ADD CONSTRAINT fk_SupplierID1 FOREIGN KEY(SupplierId) REFERENCES Supplier(SupplierID);
The approach you're trying is (a) not possible and (b) undesirable even if it was possible.
The best approach is to have a CustomerAddress table and a SupplierAddress table, each with a single FK to the matching base table; or if you must, a cross-reference table with appropriate constraints.
If your motivation for having a single Address table was code reuse, you can still do that ... think in terms of a template xxxAddress table design that can refer to any base xxx table. You can write non-database code that treats the base table name as a parameter and then could handle any number of xxxAddress tables as you add more base tables over time.
Or if your motivation for having a single Address table was to simplify reporting, you can always create a view or stored proc that returns a union of all such tables + an added field to indicate the base table for each address row.
Angelo I am revising this a bit based on your comment ---
Angelo, I ran your sample code in a local MySQL instance (not SQLFiddle) and observed an error.
I was surprised (you learn something every day) that MySQL did allow two foreign key constraints to be defined on the same field; however when you attempt to insert data, when trying to point the FK to the Customer table, I get an error saying a foreign key constraint fails referencing the Supplier table; and vice versa for the insert trying to point the FK to the Supplier table.
So my revised statement is (a) it is possible to create the hydra-headed FK in at least some DBMSs -- verified in MySQL, MS SQL Server and Oracle -- although (b) this only makes sense to use when the foreign key can refer to the same logical entity by ID across multiple tables (e.g. to ensure there is a corresponding record in all required tables, for example); and (c) if used to refer to multiple tables where the primary key is NOT the same logical entity, only works if by chance the same primary key value just happens to exist in all referenced tables, which is likely to lead to subtle, hard-to-find errors.
In other words, your example would work when attempting to insert a record referring to Customer ID=3 only if there was also a Supplier ID=3, which are really logically unrelated.
So my slightly revised answer to the OP is, what you're trying to do is not possible (or logical) when the foreign key is referring to different ENTITIES, as in the OP example of Customers and Suppliers.

How do you create a constraint on parent tables that also constrains the child tables?

I am not sure how to phrase the question so I'll illustrate the tables and the explain what I want to achieve.
-- static table of the entity classes supported by the application
create table entity_type (
id integer not null auto_increment,
name varchar(30) not null,
primary key(id)
);
-- static table of statuses supported by the application
create table entity_status (
id integer not null auto_increment,
name varchar(30) not null,
primary key(id)
);
-- table of valid combinations
create table entity_type_entity_status_link (
entity_type_id integer not null,
entity_status_id integer not null,
unique key(entity_type_id, entity_status_id),
foreign key(entity_type_id) references entity_type(id),
foreign key(entity_status_id) references entity_status(id),
);
-- The tables where user types and statuses are defined
create table user_type (
id integer not null auto_increment,
name varchar(30) not null,
entity_type_id integer not null,
primary key(id),
foreign key(entity_type_id) references entity_type(id)
);
create table user_status (
id integer not null auto_increment,
name varchar(30) not null,
entity_status_id integer not null,
primary key(id),
foreign key(entity_status_id) references entity_status(id)
);
-- table of valid pairs
create table user_type_user_status_link (
user_type_id integer not null,
user_status_id integer not null,
unique key(user_type_id, user_status_id),
foreign key(user_type_id) references user_type(id),
foreign key(user_status_id) references user_status(id),
);
The basic premise behind these tables is that the system supports core types and statuses and the user is able to create their own user types and statues that derive from these.
The question I have is that I cannot see a way of creating any database constraints on the user_type_user_status_link table to ensure that the you cannot insert a file_type - file_status pair where the parent entity_type - entity_status is itself not valid. Or is this something that would have to be done with triggers.
The basic premise behind these tables is that the system supports core
types and statuses and the user is able to create their own user types
and statues that derive from these.
Although that sounds like a laudable goal on the surface, the effect is to delegate database design to your users. Database design, because the effect of your desire to set foreign key references to a subset of the rows in entity_type_entity_status_link means each of those subsets is a defacto, unnamed table.
This approach never ends well.
What you've developed is the "One True Lookup Table". Google that for a host of reasons why OTLT is an anti-pattern.
The best solution is to model real things in your tables. (Entity isn't a real thing. It's an abstraction of a real thing.) Something along the lines of either
create table file_status (
file_status varchar(30) primary key
);
or
create table file_status (
file_status_id integer primary key,
file_status varchar(30) not null unique
);
would work well for file statuses.
In the case of the second one, you can set a foreign key reference to either the id number (saves space, requires an additional join) or to the status text (takes more space, eliminates a join). Note that you need the unique constraint on the status text; your original design allows the user to enter the same text multiple times. (You could end up with 30 rows where entity_type.name is 'File'.
You should use triggers for that.
MySQL does not support constraints of the form that will prevent what you want.