Skip to content

Commit

Permalink
Add unique constraints
Browse files Browse the repository at this point in the history
  • Loading branch information
gyusang committed Sep 30, 2024
1 parent b644603 commit 575729f
Show file tree
Hide file tree
Showing 3 changed files with 101 additions and 1 deletion.
5 changes: 4 additions & 1 deletion src/department/department.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export class Department extends BaseEntity {
@PrimaryGeneratedColumn()
id!: number;

@Column()
@Column({ unique: true })
name!: string;

@Column()
Expand Down Expand Up @@ -49,6 +49,7 @@ export class Department extends BaseEntity {
}

@Entity()
@Index('name_dept_idx', ['name', 'department'], { unique: true })
export class Tag extends BaseEntity {
@PrimaryGeneratedColumn()
id!: number;
Expand All @@ -69,6 +70,7 @@ export class Tag extends BaseEntity {
noticeTags!: Relation<NoticeTag[]>;
}

@Index('user_tag_idx', ['user', 'tag'], { unique: true })
@Entity()
export class UserTag extends BaseEntity {
@PrimaryGeneratedColumn()
Expand All @@ -89,6 +91,7 @@ export class UserTag extends BaseEntity {

@Entity()
@Index('notice_cursor', ['noticeCreatedAt', 'notice'])
@Index('notice_tag_idx', ['notice', 'tag'], { unique: true })
export class NoticeTag extends BaseEntity {
@PrimaryGeneratedColumn()
id!: number;
Expand Down
96 changes: 96 additions & 0 deletions src/migration/1727682376978-add_unique_constraints.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import { MigrationInterface, QueryRunner } from 'typeorm';

export class addUniqueConstraints1727682376978 implements MigrationInterface {
name = 'addUniqueConstraints1727682376978';

public async up(queryRunner: QueryRunner): Promise<void> {
// Step 1: Create a temporary table with the IDs of rows to keep (min_id for each name)
await queryRunner.query(`
CREATE TEMPORARY TABLE tmp_to_keep AS
SELECT MIN(id) AS min_id, name
FROM department
GROUP BY name;
`);

// Step 2: Update foreign key references in related tables
// Assuming there are tables named 'foreign_table_1', 'foreign_table_2' that reference 'department'

// Update notice
await queryRunner.query(`
UPDATE notice f
JOIN department d ON f.departmentId = d.id
JOIN tmp_to_keep t ON d.name = t.name
SET f.departmentId = t.min_id
WHERE f.departmentId != t.min_id;
`);

// Step 3: Delete duplicate rows from department, keeping only the row with the lowest id for each name
await queryRunner.query(`
DELETE d
FROM department d
LEFT JOIN tmp_to_keep t ON d.id = t.min_id
WHERE t.min_id IS NULL;
`);

// Step 4: Drop the temporary table
await queryRunner.query(`
DROP TEMPORARY TABLE IF EXISTS tmp_to_keep;
`);
console.log(
await queryRunner.query(`SELECT MIN(id) AS min_id, MAX(id) as max_id, name FROM department GROUP BY name;`),
);
await queryRunner.query(
`ALTER TABLE \`department\` ADD UNIQUE INDEX \`IDX_471da4b90e96c1ebe0af221e07\` (\`name\`)`,
);

// Step 1: Create a temporary table with the IDs of rows to keep (min_id for each (name, departmentId))
await queryRunner.query(`
CREATE TEMPORARY TABLE tmp_tags_to_keep AS
SELECT MIN(id) AS min_id, name, departmentId
FROM tag
GROUP BY name, departmentId;
`);
// Step 2: Update foreign key references in related tables
// Assuming there are tables named 'foreign_table_1', 'foreign_table_2' that reference 'tag'

// Example update for 'foreign_table_1'
await queryRunner.query(`
UPDATE notice_tag f
JOIN tag t ON f.tagId = t.id
JOIN tmp_tags_to_keep tmp ON t.name = tmp.name AND t.departmentId = tmp.departmentId
SET f.tagId = tmp.min_id
WHERE f.tagId != tmp.min_id;
`);

await queryRunner.query(`
UPDATE user_tag f
JOIN tag t ON f.tagId = t.id
JOIN tmp_tags_to_keep tmp ON t.name = tmp.name AND t.departmentId = tmp.departmentId
SET f.tagId = tmp.min_id
WHERE f.tagId != tmp.min_id;
`);

// Step 3: Delete duplicate rows from 'tag', keeping only the row with the lowest id for each (name, departmentId)
await queryRunner.query(`
DELETE t
FROM tag t
LEFT JOIN tmp_tags_to_keep tmp ON t.id = tmp.min_id
WHERE tmp.min_id IS NULL;
`);

// Step 4: Drop the temporary table
await queryRunner.query(`
DROP TEMPORARY TABLE IF EXISTS tmp_tags_to_keep;
`);

await queryRunner.query(`CREATE UNIQUE INDEX \`name_dept_idx\` ON \`tag\` (\`name\`, \`departmentId\`)`);
}

public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`DROP INDEX \`name_dept_idx\` ON \`tag\``);

await queryRunner.query(`ALTER TABLE \`department\` DROP INDEX \`IDX_471da4b90e96c1ebe0af221e07\``);

// Note: The 'down' migration cannot easily restore deleted rows without a backup, as it is a destructive operation.
}
}
1 change: 1 addition & 0 deletions src/notice/notice.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ export class File extends BaseEntity {
notice!: Relation<Notice>;
}

@Index('user_notice_idx', ['user', 'notice'], { unique: true })
@Entity()
export class UserNotice extends BaseEntity {
@PrimaryGeneratedColumn()
Expand Down

0 comments on commit 575729f

Please sign in to comment.