Merge tables and assign new primary keys where necessary - mysql

I am merging two tables that have the potential to have the same primary key (id) which will produce a Duplicate entry error.
I am OK with having new primary keys generated for the inserted data or even the existing data, so if the example databases are:
table_a
Id | Name
----------
1 | Jack
----------
2 | Jill
----------
3 | John
----------
table_b
Id | Name
----------
1 | Jim
----------
2 | Jenny
----------
3 | Joy
----------
And the Import script is INSERT INTO table_a SELECT * FROM table_b;
How do I get the script to assign new ids when they are detected as duplicate?
I realise I could do INSERT INTO table_a (name) SELECT name FROM table_b, in a similar way to that described on Merge and update primary key, but I have nearly 100 tables of different structures that I need to merge and would prefer to be able to use SELECT * and assign new ids.

You need to add another column which specifies the type of the result table.
For example in the table_a (Result table) you should add a column named type of INT type (Or of BIT type here), then you need to specify for example 0 means the data for table_a and 1 means the data for table_b.
So in the result table (table_a) you have following fields:
Id (INT - P.K. AutoIncrement)
Type (INT)
TableRelatedId (INT as you already defined)
Name (VARCHAR as you already defined)
Before inserting record you first update the table 'table_a' by type 0
UPDATE table_b SET type = 0
and then merge table_b with table_a by following INSERT:
INSERT table_a([Type], TableRelatedId, Name) SELECT 1, Id, Name FROM table_b

Related

Move one column to another table & update that column by foreign ID

I got a big table and I need to modify the structure.
Here is the table structure.
Table Name : "Articles"
+---------+---------------+-------------------+--------------+
| id(int) | details(text) | category(varchar) | tag(varchar) |
+---------+---------------+-------------------+--------------+
Important points:
There are around 250k (250,000) rows in this table
There are around 10k unique categories
What I need to change
I created table called "category" and then make the category column as a foreign key for that table.
This is what I expect:
I need to move category names to category table and then need to update articles table with category ID.
Table Name : "Articles"
+---------+---------------+-------------------+--------------+
| id(int) | details(text) | category(int) | tag(varchar) |
+---------+---------------+-------------------+--------------+
Table Name : "Category"
+---------+-------------------+
| id(int) | category(varchar) |
+---------+-------------------+
Is it possible to do just using MySQL? (I know that how to do that using some PHP scripts and MySQL commands). But I asked it here because if I can do it with just MySQL I can save lot of time.
Assumming that category(id) column is automatically filled (it is auto_increment column):
SET FOREIGN_KEY_CHECKS=0;
INSERT INTO Category( category )
SELECT DISTINCT category(varchar)
FROM Articles;
UPDATE Articles a, Category c
SET a.category(int) = c.id
WHERE a.category(varchar) = c.category
;
SET FOREIGN_KEY_CHECKS=1;
I use category(varchar) and category(int) to distinguish one (int) category column from the other (varchar) category in the same table. This is of course syntactically incorrect, you must use proper and distinct column names instead of category(xxx)

Mysql Foreign key which has a reference to two columns which are primary keys in two different table

Lets say that there are three tables called teachers, students and messages.
teachers:
-----------
ID | name |
-----------
t_1| Dani |
-----------
t_2| Billy|
-----------
students:
-------------
ID | name |
-------------
s_1| Luckas |
-------------
s_2| Oliver |
-------------
messages:
--------------------------------
| ID | sender_ID | receiver_ID |
--------------------------------
| 1 | s_1 | s_2 |
--------------------------------
| 2 | s_1 | t_1 |
--------------------------------
| 3 | s_1 | t_2 |
--------------------------------
| 4 | t_1 | s_2 |
--------------------------------
sorry about how the tables looks I did not know how to do it better.
I want that only values from teachers or from students will be permitted to be stored in messages.sender_id and in messages.receiver_Id like in cases of FK.
I need to know whether is it possible and if so, how?
You have two different choices.
First is to create a Person table, and another one PersonType. PersonType will contain "Student" and "Teacher" as values. Add a PersonTypeId to Person and create a foreign key to PersonType, this will act as discriminator between studens and teachers. Add the foreign key to Messages table from Person.
The second is to create "database inheritance". Create the Person table with common data between students and teachers. Then, create Students and Teachers table with the specific data for each, and add a foreign key to Person on both. Add your foreign key to Messages from Person table.
First solution is more performant.
MySQL doesn't support declarative foreign key constraint to enforce this integrity rule. With the tables as shown (i.e. with no changes to the tables), we could use triggers to enforce these kinds of referential integrity rules. It would require a combination of BEFORE INSERT, BEFORE UPDATE, BEFORE DELETE triggers on all three tables.
We don't see any rule that will prevent the same value (for example, k_9) from appearing the both the student and teacher table. So a row in messages that has a sender_ID is k_9, we wouldn't have a way to determine which that refers to, the student, the teacher or both.
If it is possible to change the table definitions, we could implement a person table as a superclass of student and teacher. A couple patterns for implementing that. One possibility is adding a discriminator column.
person
ID type name
--- ------- ------
s_1 student Lukas
s_2 student Oliver
t_1 teacher Dani
t_2 teacher Billy
k_9 student Rover
With that table, declaring foreign key constraints from the messages would be straightforward.
We can return the equivalent of the students table and the teachers table in the original design:
students
SELECT ID, name FROM person WHERE type = 'student'
teachers
SELECT ID, name FROM person WHERE type = 'teacher'
But given only the three tables shown in the question, it's not possible to declare foreign key constraints to enforce the specified referential integrity rules. The only way to enforce those kind of integrity rules would be through triggers.
BEFORE INSERT ON messages
FOR EACH ROW
BEGIN
-- if NEW.sender_ID is non-NULL, verify the value appears
-- as value in `ID` column of either `students` or `teachers`
-- if not, throw an error
-- if NEW.receiver_ID is non-NULL, verify the value appears
-- as value in `ID` column of `students` or `teachers`
-- if not, throw an error
END
We would also need triggers for UPDATE ON messages, as well as UPDATE and DELETE ON students and teachers

Mysql : insert into select && where not exists

I need to insert some data in a table named ‘queue’ which is a patient queue in a particular date . Two fields data will be inserted .Two fields name are ‘PatientID’ and ‘Visiting Date’. Table ‘queue' like
QueueID | PatientID | Visiting_date |
-------------|-------------------|-------------------------|
1 | 4 | Current date |
table:queue
But while inserting the record there are two conditions :
Condition 1 : patitentID comes from patient table (given below)
Condition 2 : one record will be inserted to ‘queue’ table if it does not exist to prevent repeatation.ie PatientID=4 will not be inserted if already inserted.
-------------|-----------------|------------------|
patitentID | Patient Name | Contact no |
-------------|-----------------|------------------|
4 | David | 01245785874 |
table:patient
My SQL is: (it does not work)
INSERT INTO `queue`(`patientID`, `Visiting_date`)
SELECT patient.`patientID`,’CURDATE()’ FROM `patient`
WHERE NOT EXISTS (
SELECT `patientID`, `visiting_date`FROM `queue`
WHERE `patientID` = '4' AND `visting_date`=CURDATE()
) LIMIT 1;
You could set a foreign key to make sure the patients id exists.
In the Queue table you can set patientID as unique, this makes sure you can insert only unique id's in the queue table.
Also if you would like to be able to insert the same userID but with different dates you could specify unique constraint for multiple columns in MySQL.
If you want to solve it with a mysql query only you can use this question.
I would use a separate query to check if there is a user with that ID in that table.
SELECT * FROM queue WHERE PatientID = 4;
and then check the result of that query, if it returns a row, that means that there is a user in there and you don't do anything.
If the query doesn't return a row, that means you can now use a query to inert a user. Like this
INSERT INTO queue (PatientID, VisitingDate);

INSERT query for unique records-Mysql

I have a table like this
id | name | zip
1 | abc | 1234
2 | xyz | 4321
3 | asd | 1234
I want to insert records such that the id and name when inserted may have the same value but if the value of zip is also same for that particular record it is not inserted. If not it should be inserted.
eg: I can insert another row with value for id=1 and value for name= abc but if that record also has zip=1234 it should not be inserted.
How can I achieve this.
Create two unique indexes:
create unique index idx_table_name_zip on table(name, zip)
create unique index idx_table_id_zip on table(id, zip)
The database will then guarantee the uniqueness you want to enforce.
Make a Primary Key from id, name and zip combined
ALTER TABLE table ADD PRIMARY KEY(id, name, zip)
With this a row/record is marked duplicate if all the three columns are same otherwise it is a perfectly fine non-duplicate record.
check this for more here
you need to make a check before inserting the row, for exmpale
select * from table where zip = #zip and name = #name and id = #id
if no rows return you can do the insert...

How to update foreign keys while inserting rows?

I have two tables with the following layout ...
------------- -------------------------
| Master | | History |
------------- -------------------------
| id | name | | id | name | master_id |
------------- -------------------------
The history table contains rows where the master_id is NULL. Those I copy into the master table.
INSERT master (name)
SELECT name
FROM histories h
WHERE h.master_id = NULL
How can I update the master_id in the history table with the id of the associated object in the master table? This update should happen in the same step as the insertion is made. I use MySQL if this is of interest.
It is possible to insert into master table records with IDs based on some information from the other table.
For example, you can use the histories.id + last master.id:
LOCK TABLES master WRITE, histories AS h WRITE;
SELECT id FROM master ORDER BY id DESC LIMIT 1 INTO #last_master_id;
INSERT INTO master (id, name)
SELECT h.id + #last_master_id, h.name FROM histories h;
UPDATE histories h SET h.master_id = h.id + #last_master_id;
UNLOCK TABLES;
This works as long as you use integer IDs and if you don't mind that the new ID is based on the other one.
If you wanted to use some non-deterministic IDs, such as UUIDs, the solution would require a few more steps:
Add a temporary column to the histories table.
Fill with the new IDs (update histories set tmp_uuid = UUID(), something like that).
Insert into master select tmp_uuid from histories...
Update histories
Drop the temporary column.
One advantage of using UUIDs is that you don't need to lock the tables. You don't need to worry about other session inserting a record and taking your ID, because the other session generates a different ID.
The SQL documentation here: http://dev.mysql.com/doc/refman/5.0/en/getting-unique-id.html
suggests that you should be able to do something like the following:
INSERT master (name)
SELECT name, id
FROM history h
WHERE h.master_id = NULL
UPDATE History SET master_id = LAST_INSERT_ID()
WHERE History.id = h.id;
Though I'm not 100% on the syntax as I don't have a SQL instance I can run this against.