migrate data from one table to another with parent child association - mysql

I am migrating data from table A to table B.
Table A has fields id, parent_id,title, credit.
Table B has fields id, parent_id, title, credit. where id is auto incremental field.
Table A has self association, where parent_id refers to a row in Table A itself.
From a rake task I need to migrate data from table A to table B.
sample data in table A:
id | parent_id | title | credit
12 | nil | ABC | 1
13 | 12 | XYZ | 1
14 | 12 | PQR | 0
15 | 13 | NOP | 1
after migrating data to table B, it should be like this:
id | parent_id | title | credit
1 | nil | ABC | 1
2 | 1 | XYZ | 1
3 | 1 | PQR | 0
4 | 2 | NOP | 1
When migrating data from table A to table B using ruby script, I can update title, credit with new id. How can I update parent_id?
Thanks for the support.

Update parent id after create. Something like this,
a = A.all
a.each { |v|
B.create!({parent_id: v.parent_id,title: v.title,credit: v.credit})
A.update_all({parent_id: B.last.id},{parent_id: v.parent_id})
}

I think so you need to update your ids as well.
table_b_obj.id = table_a_obj.id
table_b_obj.save!
table_b_obj.reload

Related

Update unique column of multiple rows , skip the duplicates

Here is my main table Contacts
ID | Name | Mobile
1 | Jai | 123
2 | Dave | 456
3 | Peter| 789
ID -> Primary Key Column
Mobile -> an unique column
I have a new update request. The data resides in another table (say table dummy).
ID | Name | Mobile
1 | Jai | 456
2 | Dave | 789
3 | Peter| 123
To update data from table dummy to Contacts, I can easily fire an update query by joining these two tables and update the Mobile of Contacts. But it is possible only if the data to be updated from Mobile column of dummy table is unique.
That is, if the dummy table content is like below,
ID | Name | Mobile | Status
1 | Jai | 789 |
2 | Dave | 456 |
3 | Peter| 456 |
In this case, I want to skip the rows 1 & 3 and update only the second row.
And I need to update the STATUS column (Skipped or Updated).
Is it possible by few queries?

Is it possible to normalize the table so that it can contain one Value in one row?

I have a table containing three column BusNo, BusRoute & BusStop where BusStop column contain multiple comma separated values. I want to normalize it so that the table contain one stop in one Row. Ex.
BusNo BusRoute BusStop
1 Rajendra Nagar to Noida Apsara,Shahadara,Shakarpur,Mother Dairy
I want to make the stops in multiple row would it be good approach I have more that 1000 BusNo here.
My suggestion would be to have two new tables: BusStops and BusRouteBusStops.
BusStops will have one line for each bus stop, containing at least two columns: StopNumber and StopName.
BusRouteBusStops will be the table that links the BusRoute table with the BusStops table. Each line in this table will have a primary key from BusRoutes and from BusStops.
The idea is to keep the bus stops in a table, regardless of if and where they are used. That way you can use a single stop in however many routes you want. Also, if you decide to remove a stop from all the routes, it is still kept and is available for use for new routes.
If you want to represent the order of the bus stops in the route, it can be added as a column to the BusRouteBusStops table.
Tables example:
Table BusRoutes - primary-Key(BusNo)
===============
BusNo | BusRoute
1 | Rajendra Nagar to Noida
Table BusStops - primary-Key(StopNumber)
===============
StopNumber | StopName
1 | Apsara
2 | Shahadara
3 | Shakarpur
4 | Other Stop
5 | Mother Dairy
Table BusRouteBusStops - primary-Key(BusNo+StopNumber)
===============
BusNo | StopNumber | stpoOrder
1 | 1 | 1
1 | 2 | 2
1 | 3 | 3
1 | 5 | 4
A query to get all the bus numbers that go through a given stop (say: Apsara), using MySql syntax, will be:
SELECT BR.*
FROM BusRoutes BR, BusStops BS, BusRouteBusStops BRBS
WHERE BR.BusNo=BRBS.BusNo
AND BS.StopNumber=BRBS.StopNumber
AND BS.StopName="Apsara"
To resolve a m:n relation, you normally use an additional table. As you have everything in one table right now, that means two additional tables for you.
Table structure
bus_stop: id, name
bus_route: id, description
stop_to_route_relation: bus_route, bus_stop
Example
bus_stop
--------------------
| id | name |
--------------------
| 1 | CityA |
--------------------
| 2 | CityB |
--------------------
| 3 | CityC |
--------------------
bus_route
-----------------------------
| id | bus_no | description |
-----------------------------
| 1 | 5 | CityA to B |
-----------------------------
| 2 | 5 | CityA to C |
-----------------------------
stop_to_route_relation
------------------------
| bus_route | bus_stop |
------------------------
| 1 | 1 |
------------------------
| 1 | 2 |
------------------------
| 2 | 1 |
------------------------
| 2 | 3 |
------------------------
Example query
select
br.bus_no,
bs.name
from
bus_route br
left join stop_to_route_relation str on (br.id = str.bus_route)
left join bus_stop bs on (str.bus_stop = bs.id);
If you want to normalize BusStop field then you need to make a new table for it. Like this:
Table: Bus
===================================
| BusNo | BusRoute
===================================
| 1 | Rajendra Nagar to Noida
===================================
Table: BusStop
--------------------------
| BusNo | BusStop
--------------------------
| 1 | Apsara
--------------------------
| 1 | Shahadara
--------------------------
| 1 | Shakarpur
--------------------------
| 1 | Mother Dairy
--------------------------
In the BusStop table the BusNo is the Foreign Key that links it to Bus table.
You mentioned that you have 1000 BusNo so I guess it will require a lot of resources since normalizing it will need more rows for saving the BusStop for each BusNo. For instance, each BusNo has 5 BusStops then your new table for BusStop will approximately have 1000 x 5 rows (Your saving every BusStop of Bus in the table). The advantage that I see here is you can do more queries in normalizing it. You weigh the pros and cons in deciding. Goodluck.

Sort table records in special order

I have table:
+----+--------+----------+
| id | doc_id | next_req |
+----+--------+----------+
| 1 | 1 | 4 |
| 2 | 1 | 3 |
| 3 | 1 | 0 |
| 4 | 1 | 2 |
+----+--------+----------+
id - auto incerement primary key.
nex_req - represent an order of records. (next_req = id of record)
How can I build a SQL query get records in this order:
+----+--------+----------+
| id | doc_id | next_req |
+----+--------+----------+
| 1 | 1 | 4 |
| 4 | 1 | 2 |
| 2 | 1 | 3 |
| 3 | 1 | 0 |
+----+--------+----------+
Explains:
record1 with id=1 and next_req=4 means: next must be record4 with id=4 and next_req=2
record4 with id=5 and next_req=2 means: next must be record2 with id=2 and next_req=3
record2 with id=2 and next_req=3 means: next must be record3 with id=1 and next_req=0
record3 with id=3 and next_req=0: means that this is a last record
I need to store an order of records in table. It's important fo me.
If you can, change your table format. Rather than naming the next record, mark the records in order so you can use a natural SQL sort:
+----+--------+------+
| id | doc_id | sort |
+----+--------+------+
| 1 | 1 | 1 |
| 4 | 1 | 2 |
| 2 | 1 | 3 |
| 3 | 1 | 4 |
+----+--------+------+
Then you can even cluster-index on doc_id,sort for if you need to for performance issues. And honestly, if you need to re-order rows, it is not any more work than a linked-list like you were working with.
Am able to give you a solution in Oracle,
select id,doc_id,next_req from table2
start with id =
(select id from table2 where rowid=(select min(rowid) from table2))
connect by prior next_req=id
fiddle_demo
I'd suggest to modify your table and add another column OrderNumber, so eventually it would be easy to order by this column.
Though there may be problems with this approach:
1) You have existing table and need to set OrderNumber column values. I guess this part is easy. You can simply set initial zero values and add a CURSOR for example moving through your records and incrementing your order number value.
2) When new row appears in your table, you have to modify your OrderNumber, but here it depends on your particular situation. If you only need to add items to the end of the list then you can set your new value as MAX + 1. In another situation you may try writing TRIGGER on inserting new items and calling similar steps to point 1). This may cause very bad hit on performance, so you have to carefully investigate your architecture and maybe modify this unusual construction.

mysql: how to split list field

I have a table which only contains id and a field whose data is a list of data. e.g.
--------------
| id | data |
| 1 | a,b,c,d|
| 2 | a,b,k,m|
---------------
I guess it's not a good design that put a list data in a field, so I want to know how can I redesign it?
As per me you need two tables i.e. Master and Transaction tables only when some details are gonna be same for every records and some are gonna be changing. In your case if there are not any other thing related to your id field is gonna be same you can carry on with one table and with following structure.
--------------
| id | data |
| 1 | a |
| 1 | b |
| 1 | c |
| 1 | d |
| 2 | a |
| 2 | b |
| 2 | k |
| 2 | m |
---------------
BUT if there are any other things related to the id fields that is gonna be same for same id records you will have to use two tables.
like following case. there are 3 fields id, name and data.
and you current table looks something like
--------------------------
| id | name | data |
| 1 | testname | a,b,c,d|
| 2 | remy | a,b,c,d|
--------------------------
your new table structure should look like.
table 1 Master
-----------------
| id | name |
| 1 | testname |
| 2 | remy |
-----------------
Table 2 Transaction
--------------
| id | data |
| 1 | a |
| 1 | b |
| 1 | c |
| 1 | d |
| 2 | a |
| 2 | b |
| 2 | k |
| 2 | m |
---------------
For better database management we might need to normalize the data.
Database normalization is the process of organizing the fields and tables of a relational database to minimize redundancy and dependency. Normalization usually involves dividing large tables into smaller (and less redundant) tables and defining relationships between them. The objective is to isolate data so that additions, deletions, and modifications of a field can be made in just one table and then propagated through the rest of the database via the defined relationships. You can find more on below links
3 Normal Forms Database Tutorial
Database normalization
If you have only those two fields in your table then you should have only 1 table as below
id | data
with composite primary key as PRIMARY KEY(id,data) so that there won't be any duplicate data for the respective ID.
The data would be like this
id | data
1 | a
1 | b
1 | c
1 | d
2 | a
2 | b
2 | k
2 | m
You will need another table which can be of the ONE to MANY type.
For e.g. you could have another table datamapping which would have data and ID column where the ID column is a FOREIGN KEY to the ID column of the data table.
So according to your example there would be 4 entries for ID = 1 in the datamapping table.
You will need two tables with a foreign key.
Table 1
id
Table 2
id
datavalue
So the data looks like:
Table 1:
id
1
2
3
Table 2:
id | data
1 | a
1 | b
1 | c
1 | d
2 | a
2 | b
2 | k
2 | m
You are correct, this this is not a good database design. The data field violates the principle of atomicity and therefore the 1NF, which can lead to problems in maintaining and querying the data.
To normalize your design, split the original table in two. There are 2 basic strategies to do it: using non-identifying and using identifying relationship.
NOTE: If you only have id in the parent table, and no other FKs on it, and parent cannot exist without at least one child (i.e. data could not have been empty in the original design), you can dispense with the parent table altogether.

Is it possible to share a set between two tables in MySQL?

I am currently in the process of designing a database.
I have a table of 20,000+ records, which has a set in it (4 values). I also am making another table (100+ records) which will have an enum over the same set (1 value from the same set)
Example of current:
tbl1 tbl2
ID | Letters | Stuff ID | Letter | Stuff
---------------------- ---------------------
0 | A,B,C,D | ... 0 | D | ...
1 | A,B,C,D | 1 | C |
2 | C,D | 2 | A |
3 | B,C,D | 3 | D |
...
Is there a way to make sure that the sets are the same, and can I compare the enum and the set?
I also might need to add more options to the set as our data changes. Would a separate table for that set be necessary, and then an association table for that?
Example of what I just said:
tbl1 tbl2
ID | Stuff ID | LetterID | Stuff
------------ ------------------------
0 | ... 0 | 3 | ...
1 | 1 | 2 |
2 | 2 | 0 |
3 | 3 | 3 |
...
tblLetters tblLetters1 (Association table)
ID | Letter tbl1Id | letterId
------------ ------------------
0 | A 0 | 0
1 | B 0 | 1
2 | C 0 | 2
3 | D 0 | 3
...? ...
My only major concern with this is whether the size of the association table would be too big (most of the rows in tbl1 will have all 4 elements of the set).
Thank you! Sorry if I didn't explain my problem very well. I'm very green when it comes to SQL.
Your second solution seems fine, comma separated values in 1 column should normally be avoided. You might not need an ID, and I'd drop the ENUM type for the column, and use the actual type / column definition of the letter in tblLetters:
tbl1 tbl2
ID | Stuff ID | Letter | Stuff
------------ ------------------------
0 | ... 0 | D | ...
1 | 1 | C |
2 | 2 | A |
3 | 3 | D |
tblLetters tblLetters1 (Association table)
Letter tbl1Id | letter
------------ ------------------
A 0 | A
B 0 | B
C 0 | C
D 0 | D
Possibly add a FOREIGN KEY constraint to tblLetters1.letter & tbl2.letter to enforce an existing letter from tblLetters.
And 80K rows in total is not many by any standard, it should be fine (use the proper indexes though)
I'm going to take a stab at your question....
So from what I understand, you just want to make sure the tables have the "options" or "variables" in the enum and set fields.
What you can do is:
Show create table tbl1;
What you should see is
Create table tbl1
(id int unsigned,
stuff set('A','B','C','D'),
.....)
Show create table tbl2;
Create table tbl2
(id int unsigned,
stuff enum('A','B','C','D'),
.....)
All you would need to to, technically, is make sure both tables have the same variables. You can do this with a script or just be aware of it when you do an ALTER TABLE.