Lansana Camara
Lansana Camara

Reputation: 9873

MySQL: Move data from one table to another with different schema

I have a table with a bad design:

CREATE TABLE token (
    id INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
    user_id INT(10) UNSIGNED NOT NULL,

    token VARCHAR(191) NOT NULL,
    expiration TIMESTAMP NOT NULL,

    created_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
    deleted_at TIMESTAMP NULL DEFAULT NULL,

    PRIMARY KEY (id),
    FOREIGN KEY (user_id) REFERENCES user (id) ON DELETE CASCADE
);

The reason I see it as bad now even though it worked well before is because now I cannot reuse that token table if I want a token related to an email or something else. I could just add email_id column but then I would need ugly logic to determine if the token is meant for a user or an email, respectively.

I want to normalize this design such that I can have a single token table that isn't coupled to any user_id or email_id, and have user_token and email_token pivot tables for the relations.

But I am also using migrations so I can't just rewrite my schema. I need to modify this on the fly with data present in the database.

I need to do the following:

  1. Create new user_token and email_token tables (COMPLETE)
  2. Copy token.id to user_token.token_id column, and copy token.user_id value to user_token.user_id column. The copying needs to be done in an INSERT statement since the user_token table will be brand new with no data (TODO)
  3. Remove token.user_id column (COMPLETE)

Step 2 is the part I need help on. Any help would be appreciated to write that query.

This is what the new schema will look like if it helps to create a query for step 2:

CREATE TABLE token (
    id INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,

    token VARCHAR(191) NOT NULL,
    expiration TIMESTAMP NOT NULL,

    created_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
    deleted_at TIMESTAMP NULL DEFAULT NULL,

    PRIMARY KEY (id)
);

CREATE TABLE user_token (
    id INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
    user_id INT(10) UNSIGNED NOT NULL,
    token_id INT(10) UNSIGNED NOT NULL,

    created_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
    deleted_at TIMESTAMP NULL DEFAULT NULL,

    PRIMARY KEY (id),
    FOREIGN KEY (user_id) REFERENCES user (id) ON DELETE CASCADE,
    FOREIGN KEY (token_id) REFERENCES token (id) ON DELETE CASCADE
);

CREATE TABLE email_token (
    id INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
    email_id INT(10) UNSIGNED NOT NULL,
    token_id INT(10) UNSIGNED NOT NULL,

    created_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
    deleted_at TIMESTAMP NULL DEFAULT NULL,

    PRIMARY KEY (id),
    FOREIGN KEY (email_id) REFERENCES user (id) ON DELETE CASCADE,
    FOREIGN KEY (token_id) REFERENCES token (id) ON DELETE CASCADE
);

Upvotes: 0

Views: 1176

Answers (1)

iSR5
iSR5

Reputation: 3498

IF you haven't delete token.user_id and token.email_id columns from token table, then you can copy them by using a simple INSERT:

INSERT INTO user_token (user_id, token_id)
SELECT user_id, id
FROM token

INSERT INTO email_token (email_id, token_id)
SELECT email_id, id
FROM token

if you want to copy created_at, updated_at, and deleted_at columns along with them, you could just add them into the query:

INSERT INTO user_token (user_id, token_id, created_at, updated_at, deleted_at)
SELECT user_id, id, created_at, updated_at, deleted_at
FROM token

INSERT INTO email_token (email_id, token_id, created_at, updated_at, deleted_at)
SELECT email_id, id, created_at, updated_at, deleted_at
FROM token

this will copy them into the new tables. Then, you only need to delete user_id and email_id columns from token table by a simple ALTER :

FIRST DELETE FOREIGN KEY:

ALTER TABLE token DROP FOREIGN KEY user_id

THEN DELETE THE COLUMNS:

ALTER TABLE token DROP COLUMN user_id;
ALTER TABLE token DROP COLUMN email_id;

Upvotes: 1

Related Questions