I have a mysql table which I need to split into two. Currently the table contains info about two loosely related entities.
For example the entities can be Employee of a firm and the firm's laptop he is tagged to.
The following table explains my example. I have a table employee which has the following columns
employee_id,employee_name,employee_detail,join_date,laptop_id,laptop_type,laptop_tagged_date,laptop_details,laptop_make.
I need to split this table into 2 as shown below.
employee table without the laptop columns
employee_id,employee_name,employee_detail,join_date.
A new laptop table with employee_id as the key.
employee_id,laptop_id,laptop_type,laptop_tagged_date,laptop_details,laptop_make
The below statements are true for the current database design
The employee table is heavily used by the back end code. The back
end code is written in java and php.
employee table cannot be renamed.Implies I do not want to create 2
new tables.I want to retain the employee table, but remove all the
laptop info from it.
New rows are added/existing rows are updated on a daily basis.
My question is
Is there a design approach which I can follow for a smooth
transition from the current design of a single table to the newly
suggested design?
Are there any best practices to follow to ensure a smooth
transition.
Could you suggest/re-direct me the steps to complete this task.
Backup your existing database:
mysqldump my_db > backup.sql
Create a new, empty table laptop (I'm not defining indexes or foreign key constraints in my example below, but you should do whatever is appropriate to your data structure):
CREATE TABLE laptop
SELECT employee_id,
laptop_id,
laptop_type,
laptop_tagged_date,
laptop_details,
laptop_make
FROM employee
WHERE FALSE;
Define triggers on the original table to forward each type of write operation (insert/update/delete) to the new table:
CREATE TRIGGER employee_insert AFTER INSERT ON employee FOR EACH ROW
INSERT INTO laptop VALUES (
NEW.employee_id,
NEW.laptop_id,
NEW.laptop_type,
NEW.laptop_tagged_date,
NEW.laptop_details,
NEW.laptop_make
);
CREATE TRIGGER employee_update AFTER UPDATE ON employee FOR EACH ROW
UPDATE laptop SET
employee_id = NEW.employee_id,
laptop_id = NEW.laptop_id,
laptop_type = NEW.laptop_type,
laptop_tagged_date = NEW.laptop_tagged_date,
laptop_details = NEW.laptop_details,
laptop_make = NEW.laptop_make
WHERE
employee_id <=> OLD.employee_id,
laptop_id <=> OLD.laptop_id,
laptop_type <=> OLD.laptop_type,
laptop_tagged_date <=> OLD.laptop_tagged_date,
laptop_details <=> OLD.laptop_details,
laptop_make <=> OLD.laptop_make;
CREATE TRIGGER employee_delete AFTER DELETE ON employee FOR EACH ROW
DELETE FROM laptop WHERE
employee_id <=> OLD.employee_id,
laptop_id <=> OLD.laptop_id,
laptop_type <=> OLD.laptop_type,
laptop_tagged_date <=> OLD.laptop_tagged_date,
laptop_details <=> OLD.laptop_details,
laptop_make <=> OLD.laptop_make;
Empty the new table (of anything the trigger has since inserted) and then, within the same transaction, use INSERT ... SELECT to copy into it all incumbent data from the original table:
START TRANSACTION;
DELETE FROM laptop;
INSERT INTO laptop
SELECT employee_id,
laptop_id,
laptop_type,
laptop_tagged_date,
laptop_details,
laptop_make
FROM employee;
COMMIT;
Thoroughly search your codebase (including database-stored programs) for all operations that access the laptop columns in the original table. Note whether each operation:
only reads from those columns;
only writes to those columns; or
both reads from and writes to those columns (e.g. UPDATE employee SET laptop_tagged_date = laptop_tagged_date + INTERVAL 1 WEEK WHERE ...).
Modify read operations to use the new table, splitting operations that both read and write into separate steps (e.g. UPDATE employee JOIN laptop ON ... SET employee.laptop_tagged_date = laptop.laptop_tagged_date + INTERVAL 1 WEEK WHERE ...).
This change does not need to be effected atomically, as the original and new tables will be kept in sync by the triggers: some parts of your application can therefore read from the new table whilst others continue using the original table.
Do not proceed to the next step until you are satisfied that this step has been completed, as the next step will result in the tables becoming desynchronised. You could even use MySQL user permissions to prevent writing to the new table (except by the triggers) until you are satisfied that this step is complete.
Modify write operations to use the new table.
This change does not need to be effected atomically, as any writes to the original table will be forwarded to the new one by the triggers: some parts of your application can therefore write to the new table whilst others continue writing to the original table.
Drop the columns from the original table:
ALTER TABLE employee
DROP COLUMN laptop_id,
DROP COLUMN laptop_type,
DROP COLUMN laptop_tagged_date,
DROP COLUMN laptop_details,
DROP COLUMN laptop_make;
if you want to have 2 physical different tables you can adapt Sebastien M. answer as such :
create a laptop table with corresponding data to externalize them
CREATE TABLE laptop AS
SELECT DISTINCT employee_id,laptop_id,laptop_type,laptop_tagged_date,laptop_details,laptop_make
FROM employee
WHERE ...
provide a employee_laptop view to imitate the behaviour of employee and get backward compatibility
create view employee_laptop as
select employee_id, e.employee_name,e.employee_detail,e.join_date,
l.laptop_id,l.laptop_type,l.laptop_tagged_date,l.laptop_details,l.laptop_make
from employee e join laptop l using(employee_id);
then you have all the latitude to drop the unnecessary columns from the employee table
I can suggest you a possible way , when you are creating laptop table use the following query type :-
create table laptop select employee_id,laptop_id,laptop_type,
laptop_tagged_date,laptop_details,laptop_make from
employee;
After creating the laptop table in this above process, you can remove the specified columns from employee table for getting your new employee table with relevant fields.
Delete column from employee table.
alter table employee
drop column laptop_id,laptop_type,laptop_tagged_date,laptop_details,laptop_make;
Now the new employee table has following fields :
employee_id,employee_name,employee_detail,join_date
Now the laptop table has following fields :
mployee_id,laptop_id,laptop_type,
laptop_tagged_date,laptop_details,laptop_make
Hope it will help you.
Related
Scenario
1. Two tables exist called 'score_log' and 'games_played_count'
2. Computing the "count" of rows from score_log that uniquely describe the number of games that have been played by a given member. Each row is unique based on a combination of playerid, teamid and leagueid
3. The 'games_played_count' table contains a unique row based on playerid, teamid and leagueid with a variable column 'games_played_total' which is designed to be updated automatically when an INSERT OR DELETE occurs on the score_log table.
Current Solution
a. Two triggers defined on 'score_log' table. One trigger executes a stored procedure (defined below) using AFTER INSERT condition. The other trigger defined executes the stored procedure using AFTER DELETE condition.
b. The stored procedure that achieves the objective is defined below:
CREATE TEMPORARY TABLE temp_table AS
SELECT playerid, leagueid, COUNT(*) AS games_played_count
FROM score_log
GROUP BY playerid, leagueid;
UPDATE games_played_by_league
INNER JOIN temp_table ON games_played_by_league.playerid = temp_table.playerid AND games_played_by_league.leagueid = temp_table.leagueid
SET games_played_by_league.games_played_count = temp_table.games_played_count
WHERE games_played_by_league.playerid = temp_table.playerid AND games_played_by_league.leagueid = temp_table.leagueid;
Is this the most optimal way to achieve the desired outcome?
I use mysql 5.7 and the structure of database is as the picture below.
I want the colum "Course_left1" on table Course to drop by 1 whenever a course log is inserted into the table Course_history,but it's not working.
CREATE TRIGGER Course_reduce
AFTER INSERT ON Course_history
FOR EACH ROW
UPDATE Course SET Course.Course_left1 = (Course.Course_left1 - 1) where NEW.Sid = Course.Sid
Sid stands for the id of Student while Course_left1 and Course_left2 stand for the course remain for the specific student.
Now, if I insert one tuple into table Course_history I want the Course_left1 in the table Course to drop by 1 which makes it 31.
I am currently testing change tracking mechanism in sql server 2008 and noticed something:
When to the base table (change tracked) I insert a new record and delete it using the same
key, select with the changes returns to me the information that it should be deleted in the remote table however that record doesn't exist at all in that table ..
Why is it functioning that way?
SAMPLE CODE:
CREATE TABLE TEST (
ID UNIQUEIDENTIFIER primary key,
value int
)
ALTER TABLE [dbo].Test
ENABLE CHANGE_TRACKING
SELECT CHANGE_TRACKING_CURRENT_VERSION()
SELECT CT.SYS_CHANGE_OPERATION, CT.ID, IV.value
FROM CHANGETABLE(CHANGES TEST, 51374) CT
LEFT JOIN TEST IV ON IV.ID = CT.ID
--zero changes now for: 51374
insert into Test VALUES ('54C1D80E-ACB0-433F-94DF-7D06FE809E22', 1)
delete from Test where id = '54C1D80E-ACB0-433F-94DF-7D06FE809E22'
select * from Test -- table is empty (insert and delete)
SELECT CT.SYS_CHANGE_OPERATION, CT.ID, IV.value
FROM CHANGETABLE(CHANGES TEST, 51374) CT
LEFT JOIN TEST IV ON IV.ID = CT.ID
--however for Anchor: 51374 it claims I should delete the record ...
My base and the remote table were in sync at 51374 anchor.
Adding and deleting the record shouldn't give me the info for deleting
of something I don't have in my remote table ...
I think Damien's answer is right. Not sure why he answered in a comment.
It's all there in the documentation, really "Only the fact that a row
has changed is required, not how many times the row has changed or the
values of any intermediate changes.", "If an application requires
information about all the changes that were made and the intermediate
values of the changed data, using change data capture, instead of
change tracking."
Imagine if there's a row with ID 1 that you know about. Then in some
period, someone goes in and deletes that row, and then adds a new row
with the same ID. Change tracking will give you an insert, even though
you already knew about a row with ID 1 and haven't seen a delete. It's
the nature of the beast - you only get the last change, and you have
to reconcile that with your version of reality. If you see a Delete
for a row you don't know about, ignore it.
I could have the wording 'wrong' here (new to mysql) but i hope i've explained what I'm trying to do well.
i have a table called submissions with 4 fields submitId, studentName, submitDate, status
status refers to whether they got admitted or not.
submitId is auto incremented
Now i wanted to create another table based on that, but only if the status is true, this new table would have the submitId, studentName, submitDate, plus additional fields.
this table would have a new auto increment studentId
how would i do that so it automatically updates any new entry to the first table on the second table, but not overwrite the additional content of table 2.
i thought of using a view, but u can't add new columns on the view, right?
do i have the logic wrong here or what are the options, could someone please point me in the right direction, thanks
You want to use a trigger. See:
http://dev.mysql.com/doc/refman/5.6/en/triggers.html
You can create the trigger so that when a row is inserted into submissions with status=true, it inserts a row into your new student table. It would look something like this:
delimiter //
CREATE TRIGGER sub_ins_check AFTER INSERT ON submissions
FOR EACH ROW
BEGIN
IF NEW.status = 1 THEN
INSERT INTO your_new_table (student_name, submit_date, submit_id) VALUES (NEW.student_name, NEW.submit_date, NEW.submit_id);
END IF;
END;//
delimiter ;
Then create another trigger so that when a row is updated in submissions, it updates the row with the same submit_id in your new table, like this:
delimiter //
CREATE TRIGGER sub_ins_check AFTER UPDATE ON submissions
FOR EACH ROW
BEGIN
IF NEW.status = 1 THEN
UPDATE your_new_table SET student_name = NEW.student_name, submit_date = NEW.submit_date, (etc..)) WHERE submit_id = NEW.submit_id;
END IF;
END;//
delimiter ;
I think your data model is wrong. Remember that student my have several submissions and there may be number of students with the same name. You must distinguish them.
Is there any reason you want to duplicate student data in both tables?
If you're new to SQL, read about table normalization first.
In you Student table you should store students data and in Submission table - guess what :)
The first thing you need to do is step back and consider the problem from the perspective of logical entities.
You've identified two entities that I can see - student and submission. "Student" is an obvious entity which you may choose NOT to store in your database, but it may be better that you do. "Submission" is a more obvious one, but what is not so obvious is what a "submission" actually is. Let's assume it is some sort of transaction.
You've mentioned a "second table" without a clear indication of its role in the solution. The best I could infer is that it is meant to be some sort of historical trail on activity against a submission. If true, then I could envision a physical schema sketched out as follows:
Student table. One row per student; contains information about a student (name, id, etc.). Primary key would probably be an auto-incremented number.
Submission table. One row per submission; includes a foreign key to the student table (referencing the primary key); has its own primary key, also an auto-incremented integer. Also has triggers defined for INSERT and UPDATE. INSERT trigger causes INSERT into submission_log table; UPDATE trigger also causes INSERT into submission_log table.
Submission_log table. One row per event against the submission table. Includes all the fields of submission plus its own primary key (submission's primary key is a foreign key here), and includes an indicator field for whether it represents an insert or update on submission.
The purpose of the above is not to supply a solution, or even the framework of a solution, but rather to get you to think in terms of the logical entities you want to model in your solution, and their relationships to each other. When you have a clear picture of the logical model, it will be much easier to determine what tables are required, what their roles are, and how they will be used and how they will relate to each other.
I have a scenario that requires a value in a row of a table to be updated automatically whenever a row has been added or deleted in another table. I'm not sure how to do it.BTW I'm using phpmyadmin in order to manage my database. Thanks in advance.
pages Table
------------
page_no
no_of_choices
choices Table
-------------
page_no
choice_no
When I add a choice with choice number 1 and page_no, then the table page which has the row, page_no=1 should be updated with no_of_choices=no_of_choices+1
You can use triggers.
For example:
CREATE TRIGGER `test1`
AFTER INSERT ON `tbl1`
FOR EACH ROW SET NEW.upd_fld = new_value
Similarly could be done for delete.
You can also create triggers from phpMyAdmin
TABLE A: page_no, no_of_choices
TABLE B: page_no, choice_no...
With a relational database you very rarely want to have duplicate data. If something breaks at some point, you won't know which to trust - the rows in Table B, or the no_of_choices in Table A. A better solution is to do one of the following (depending on which table you are querying):
SELECT COUNT(no_of_choices) FROM B WHERE page_no = 1
or
SELECT A.*, COUNT(choice_no) AS choice_no FROM A LEFT JOIN B USING(page_no)
You get the same result, but now you have one record to go off of, so you won't have inconsistent data.