How to add records in self referencing table? - mysql

I am a noob in MySql. I want to create the following self-referencing table:
EMPLOYEE
+-----+------+------+
|Name |E-ID |M-ID |
+-----+------+------+
|ABC |12345 |67890 |
|DEF |67890 |12345 |
+-----+------+------+
I use the following commands:
CREATE TABLE EMPLOYEE (
NAME VARCHAR(20) ,
E-ID CHAR(6) NOT NULL ,
M-ID CHAR(6) NULL ,
PRIMARY KEY (E-ID) ,
FOREIGN KEY (M-ID) REFERENCES EMPLOYEE(E-ID)
);
Now my problem is, how do I enter the two records? I mean, each time the foreign constraint will fail. I tried entering:
INSERT INTO EMPLOYEE VALUES('12345','67890');
I also tried :
INSERT INTO EMPLOYEE VALUES('12345','67890'),('67890','12345');
Both of the above commands fail. Giving error:
ERROR 1452 (23000): Cannot add or update a child row: a foreign key
constraint fails BLAH BLAH
Guys, actually I was trying to implement the tables given in slide number 25 of the following ppt: The Relational Data Model and Relational Database Constraints
The constraints are:
SUPERSSN Of EMPLOYEE references SSN of EMPLOYEE.
MGRSSN of DEPARTMENT references SSN of EMPLOYEE.
DNO of EMPLOYEEE references DNumber of DEPARTMENT.
After I have created the tables, how do I add records? It will always fail the foreign key constraints.

As MySQL does not support deferrable constraints (which are the "natural solution" to such a problem) you will need to do this in two steps;
INSERT INTO employee (name, `E-ID`) values ('Arthur', '123456');
INSERT INTO employee (name, `E-ID`) values ('Ford', '67890');
UPDATE employee
SET `M-ID` = '67890'
WHERE `E-ID` = '123456';
UPDATE employee
SET `M-ID` = '123456'
WHERE `E-ID` = '67890';
You circular reference does sound strange to me though. An employee being the manager of an employee who is in turn his manager?
Allow me two comments on your table definition:
avoid column (or table names) with special characters that need quoted identifiers. Using E_ID instead of E-ID will save you some trouble in the long run
If your employee ID can be shorter than 6 characters than you most probably want to use VARCHAR(6) instead of CHAR(6) due to the padding of the values with the CHAR datatype.

There are good answers here, but figured I'd point out the quick-and-dirty:
SET FOREIGN_KEY_CHECKS = 0;
INSERT INTO EMPLOYEE VALUES('12345','67890'),('67890','12345');
SET FOREIGN_KEY_CHECKS = 1;

It will obviously fail because the table is empty.
INSERT INTO EMPLOYEE VALUES('12345','67890');
Since M-ID depends on E-ID. Remove the constraint so you can insert record. The best thing you do is to create another table for M-ID and reference it to Employee table.

Related

Error: Duplicate entry '1' for key 'students.PRIMARY' Error Code: ER_DUP_ENTRY

CREATE TABLE IF NOT EXISTS students (
student_id INT,
name VARCHAR(24),
major VARCHAR(24),
PRIMARY KEY(student_id)
);
SELECT * FROM student;
INSERT INTO students VALUES(1,'Jack','Biology');
You're specifying the primary key (student_id) and from the error it already exists. You have a few options:
Don't specify the primary key. It should be set to autoincrement anyway, assuming that this is the primary table that students are entered into, and from the name of the table (students) it seems like it is. Then the query will be:
INSERT INTO students VALUES('Jack','Biology');
and then the table will autoincrement the primary key to the next pointer.
Use INSERT IGNORE. This will silently fail if you try to insert a student ID that already exists (or on any query that violates unique keys).
INSERT IGNORE INTO students VALUES(1, 'Jack','Biology');
This will not cause table changes, but it will also not cause an error that interrupts the script, and it will insert any rows that don't fail, say if you had multiple values inserted. The plain INSERT will fail for the entire list, not just the erroneous value.
Use ON DUPLICATE KEY UPDATE. This will update a list of values if it encounters a duplicate key.
INSERT INTO students VALUES(1, 'Jack','Biology')
ON DUPLICATE KEY UPDATE name = values(name), major = values(major);
In this case, you will change the values in the table that match the key. In this case, whichever student is student_id 1 will have its name and major updated to the supplied values. For instance, let's say that Jack changed his major to Chemistry. This would update student_id 1 to Jack, Chemistry and reflect his new major.
Use REPLACE INTO. I avoid this one. It is similar to ON DUPLICATE KEY UPDATE, but it removes the old entry and replaces it with a new one with a new ID. This can cause you problems with foreign keys, and also if you have a small primary key and you constantly replace into it, you can end up with a primary id that's bigger than the limits you set.
Well, your student_id is primary key, clearly that table is already exist with some data with student_id=1 hence you cannot insert another row with the same primary key value.

MySQL: INSERT or UPDATE if exists, but not based on key column

What I have is a table of completed training. Each user has a username. Each user may completed numerous courses.
The table has the following headers:
+-------------------------+----------+---------+---------+---------+---------+-----------+
| recordnumber (KEY - AI) | username | type | course | status | started | completed |
+-------------------------+----------+---------+---------+---------+---------+-----------+
| int | varchar | varchar | varchar | varchar | date | date |
+-------------------------+----------+---------+---------+---------+---------+-----------+
And I have a PHP script set up to populate the db from a CSV upload.
What I'm trying to achieve is for it to add new rows, and to update existing ones.
The problem is that recordnumber (they key, unique field) is not constant. So instead of doing a "ON DUPLICATE KEY" query, I want to do it based on whether username and course already exist as a row.
Basically to say "If this username already has this course, update the other fields. If the username does not have this course, add this as a new row".
The query that I have at the moment (which works based on key) is:
INSERT into table(recordnumber, username,type,course,status,started,completed) values('$data[0]','$data[1]','$data[2]','$data[3]','$data[4]','$data[5]','$data[6]')
ON DUPLICATE KEY UPDATE username='$data[1]',type='$data[2]',course='$data[3]',status='$data[4]',started='$data[5]',completed='$data[6]'
Any thoughts on how I could amend the query to get it to check based on username and course instead of duplicate key?
Thank you. :-)
The most correct way would be to create a unique index on username - course columns and use on duplicate key update.
Obviously, you can issue a select before the insert checking for existing record with same user name and course and issue an insert or an update as appropriate.
create a key on the username and course column and then use on duplicate key
CREATE TABLE test (
username varchar(255) NOT NULL,
course varchar(255),
num_entries INT DEFAULT 0,
UNIQUE KEY (username, course)
);
insert into test (username, course) values
('billybob', 'math'),
('billy', 'math'),
('billybob', 'math'),
('bob', 'math')
ON DUPLICATE KEY UPDATE num_entries = num_entries + 1;
this is a simple example, but you should understand what to do from here
SAMPLE FIDDLE
so putting this to work on your table
ALTER TABLE `courses` -- assuming the table is named courses
ADD CONSTRAINT `UK_COURSE_USERNAME` UNIQUE (username, course);
then your insert should just be the same as what you have
Example query in reference to my comment above.
IF EXISTS(SELECT id FROM Table WHERE username = '$data[1]' AND course <> '$data[3]')
(
UPDATE username='$data[1]',type='$data[2]',course='$data[3]',status='$data[4]',started='$data[5]',completed='$data[6]'
)
(
INSERT into table(recordnumber, username,type,course,status,started,completed) values('$data[0]','$data[1]','$data[2]','$data[3]','$data[4]','$data[5]','$data[6]')
)
You might use ON DUPLICATE KEY UPDATE if you added unique constraint for username and course value pair like this:
ALTER TABLE `table` ADD CONSTRAINT `UK_table_username_course` UNIQUE (username, course);

MySql replace with multiple primary keys

I have a table which has three primary keys and references three other tables
Here is the table scheema:
CREATE TABLE IF NOT EXISTS training_matrix_reference(
employee INT NOT NULL,
training_matrix INT NOT NULL,
training_record INT UNSIGNED NOT NULL,
PRIMARY KEY (employee, training_matrix,training_record),
FOREIGN KEY (employee) REFERENCES employees(id),
FOREIGN KEY (training_matrix) REFERENCES training_matrix_courses(id),
FOREIGN KEY (training_record) REFERENCES training_records(m_id)
)
I'm trying to craft a REPLACE statement which updates the training_record column or training_matrix column or both columns or creates a new row if not exists, but I also need to check that the employee belongs to the same company.
Here's what I tried so far:
REPLACE INTO `training_matrix_reference`
( employee, training_matrix, training_record ) (
SELECT id, '5', '100'
FROM employees
WHERE id =22
AND company =64
)
So my theory was that this should have replaced the first row in the table, updating training_record to 100 but in fact it actually created a new row:
22 | 5 | 100
My guess is that this happened because training_record is a primary key?
But I'm not sure that removing the primary keys/references is the right way to go as this table is used as a many to many table in other queries.
Effectively what I'm trying to do is:
REPLACE INTO `training_matrix_reference`
( employee, training_matrix, training_record )
VALUES
(22,33,18)
WHERE
employee = 22
and training_matrix = 5
and training_record = 2189
But obviously a replace statement doesn't have a where clause.
I did check out these similar questions:
MySQL REPLACE INTO on multiple keys?
mysql REPLACE query with multiple primary keys
But unfortunately MySql is not my strong suit and I could really use some help.
I hope I explained things clearly, Thanks
The PRIMARY KEY of the training_matrix_reference table is the combination of three columns. The table doesn't have multiple primary keys, it has a single PRIMARY KEY.
The REPLACE syntax you have is equivalent to performing:
DELETE FROM training_matrix_reference
WHERE employee = 22
AND training_matrix = 5
AND training_record = 100
;
INSERT INTO training_matrix_reference (employee, training_matrix, training_record)
VALUES (22, 5, 100);
;
The DELETE action only removes rows where the entire primary key is matched. Given the information you provided, we'd expect a row to be added to the table.
Did you have a question?
you should make a joining table between (employee, training_matrix_reference)
or dispense at lest one relation

SQL Foreign key constraints

I have one employee table:
create table employee
(number integer primary key,
name varchar(20),
salary integer,
manager integer,
birthyear integer,
startyear integer);
Where the manager column is the employee number of the employees manager, i.e. a couple of rows would look something like this:
number | name | salary | manager | birthyear | startyear |
32 | Smythe, Carol | 9050 | 199 | 1929 | 1967 |
33 | Hayes, Evelyn | 10100 | 199 | 1931 | 1963 |
35 | Evans, Michael | 5000 | 32 | 1952 | 1974 |
So to clarify, Michael Evans manager is Carol Smythe. And two more things, there are no foreign key constraints on this table and there are a couple of NULL values in the manager column.
Now, I would like to create a Managers table which contains all managers. I would do something like this;
create table Mgr(
Mgr_id INTEGER PRIMARY KEY,
bonus INTEGER,
FOREIGN KEY (Mgr_id) REFERENCES employee(manager));
BUT; this doesn´t work and I get an error. Can someone please explain why? Have searched for an answer but can´t find any good explanation. Thanks in advance.
ERROR:
ERROR 1005 (HY0000): Can´t create table johnson.mgr (errno: 150)
You are trying to create Primary key and Foreign key on same field and table.
You need to change definition of employee table like below,
create table employee
(number integer primary key,
name varchar(20),
salary integer,
manager integer,
birthyear integer,
startyear integer
FOREIGN KEY (manager) REFERENCES Mgr(Mgr_id)
);
And remove Foreign Key clause from Mgr table definition
The Corresponding columns in the foreign key and the referenced key must have similar internal data types so that they can be compared without a type conversion. The size and sign of integer types 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.
So check whether you are giving a proper datatype for managerId table.
These conditions must be satisfied to not get error 150:
The two tables must be ENGINE=InnoDB.
The two tables must have the same charset.
The PK column(s) in the parent table and the FK column(s) must be the same data type.
The PK column(s) in the parent table and the FK column(s), if they have a define collation type, must have the same collation type;
If there is data already in the foreign key table, the FK column value(s) must match values in the parent table PK columns.
One more problem is that you are creating foreign key and primary key on same column
Hope this helps
Refer http://dev.mysql.com/doc/refman/5.6/en/innodb-foreign-key-constraints.html
What you want to do makes sense. How you're doing it doesn't. You need to change the way your database identifies managers. Right now, it identifies managers by the values in employee.manager. When you're finished, it will identify managers by the rows in Mgr.
Make sure you have usable data first. Look for id numbers that are no longer in the employee table. Try this.
select manager
from employee
where manager not in (select number
from employee);
Every row returned represents an error--a manager's id number for which there is no corresponding row in employee. You have to fix those before you can make much progress.
After you fix those errors, create the table Mgr.
create table Mgr (
Mgr_id INTEGER PRIMARY KEY,
bonus INTEGER NOT NULL DEFAULT 0,
FOREIGN KEY (Mgr_id) REFERENCES employee(manager)
);
Populate Mgr with a query. Something along these lines should work.
insert into Mgr (Mgr_id)
select distinct manager
from employee;
Update that table with the correct value for bonus. You might have to do that manually if you don't have that data stored somewhere handy.
As far as the database is concerned now, you could drop the column employee.manager. And you should drop that column, but not now. You have to consider what will happen to application code that thinks you can identify managers by looking at the employee table.
You can just drop the column, and let application programs fail until they're fixed.
You can warn all the application developers that these changes will be made on, say, March 1, and they'd better get their code ready.
You can make the changes, leave the column in place, warn the application developers, and take steps to prevent changes to employee.manager.
You can make these changes, rename the employee table, and create an updatable view having the old structure (you'll need to join employee and Mgr) and the name "employee". This option is close to ideal--it requires no changes to application code--but I'm not sure to what degree MySQL supports updatable views. It might not be possible.

Insert null if violates a constraint in mysql?

I have a country and let's say user tables, and in case a submitted country does not match a constraint I want a null go there instead.
create table countries(
ccode char(2), primary key (ccode)
);
create table users(
ccode char(2), foreign key (ccode) references countries(ccode)
);
insert into countries values('ru'),('us'),('hk');
Now if the user submitted an nonexistent code, eg:
insert into users values('xx');
The query would actually fail due to the fk constaint. But I want it to write a null instead. How can we do this?
Answering my own question it looks like this is the way to do it:
(But if you suggest a better answer I will accept it of course)
insert into users values((select ccode from countries where ccode='x'));
^ This will return a code, and a null if it didn't match - which is what I want.