From bbbb371bd127c01fd2decf88d0eddf76b428834f Mon Sep 17 00:00:00 2001 From: Lucas Vaz Date: Tue, 10 Feb 2026 23:46:07 -0300 Subject: [PATCH 1/2] =?UTF-8?q?remo=C3=A7=C3=A3o=20de=20duplicidade=20de?= =?UTF-8?q?=20autores=20da=20base=20de=20dados?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...211011159_fix_remove_autores_duplicados.ts | 112 ++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 src/database/migration/20260211011159_fix_remove_autores_duplicados.ts diff --git a/src/database/migration/20260211011159_fix_remove_autores_duplicados.ts b/src/database/migration/20260211011159_fix_remove_autores_duplicados.ts new file mode 100644 index 0000000..37416c0 --- /dev/null +++ b/src/database/migration/20260211011159_fix_remove_autores_duplicados.ts @@ -0,0 +1,112 @@ +import { Knex } from 'knex' + +export async function run(knex: Knex): Promise { + await knex.transaction(async (trx) => { + const dupGroups = await trx('autores') + .select([ + 'nome', + 'iniciais', + trx.raw('COUNT(*) as qtde'), + trx.raw('MIN(id) as keep_id'), + trx.raw("GROUP_CONCAT(id ORDER BY id SEPARATOR ',') as ids"), + ]) + .groupBy(['nome', 'iniciais']) + .havingRaw('COUNT(*) > 1') + + if (!dupGroups.length) return + const pairs: Array<{ keep_id: number; drop_id: number }> = [] + for (const g of dupGroups as any[]) { + const keepId = Number(g.keep_id) + const ids = String(g.ids) + .split(',') + .map((s) => Number(s.trim())) + .filter((n) => Number.isFinite(n)) + + for (const id of ids) { + if (id !== keepId) pairs.push({ keep_id: keepId, drop_id: id }) + } + } + + if (!pairs.length) return + await trx.raw(` + DROP TEMPORARY TABLE IF EXISTS autor_merge; + `) + + await trx.raw(` + CREATE TEMPORARY TABLE autor_merge ( + keep_id INT NOT NULL, + drop_id INT NOT NULL, + PRIMARY KEY (drop_id), + KEY (keep_id) + ); + `) + await trx('autor_merge').insert(pairs) + await trx.raw(` + UPDATE especies e + JOIN autor_merge m ON e.autor_id = m.drop_id + SET e.autor_id = m.keep_id; + `) + + await trx.raw(` + UPDATE sub_especies se + JOIN autor_merge m ON se.autor_id = m.drop_id + SET se.autor_id = m.keep_id; + `) + + await trx.raw(` + UPDATE variedades v + JOIN autor_merge m ON v.autor_id = m.drop_id + SET v.autor_id = m.keep_id; + `) + + const hasSubFamiliasAutorId = await trx.schema.hasColumn('sub_familias', 'autor_id') + if (hasSubFamiliasAutorId) { + await trx.raw(` + UPDATE sub_familias sf + JOIN autor_merge m ON sf.autor_id = m.drop_id + SET sf.autor_id = m.keep_id; + `) + } + const [[rest1]] = await trx.raw(` + SELECT COUNT(*) AS c + FROM especies e + JOIN autor_merge m ON e.autor_id = m.drop_id; + `) + + const [[rest2]] = await trx.raw(` + SELECT COUNT(*) AS c + FROM sub_especies se + JOIN autor_merge m ON se.autor_id = m.drop_id; + `) + + const [[rest3]] = await trx.raw(` + SELECT COUNT(*) AS c + FROM variedades v + JOIN autor_merge m ON v.autor_id = m.drop_id; + `) + + const restantes = + Number(rest1?.c ?? 0) + Number(rest2?.c ?? 0) + Number(rest3?.c ?? 0) + + if (restantes > 0) { + throw new Error(`Merge abortado: ainda existem ${restantes} referências para autores duplicados (DROP).`) + } + + if (hasSubFamiliasAutorId) { + const [[rest4]] = await trx.raw(` + SELECT COUNT(*) AS c + FROM sub_familias sf + JOIN autor_merge m ON sf.autor_id = m.drop_id; + `) + if (Number(rest4?.c ?? 0) > 0) { + throw new Error(`Merge abortado: ainda existem ${rest4.c} referências em sub_familias para autores duplicados (DROP).`) + } + } + + await trx.raw(` + DELETE a + FROM autores a + JOIN autor_merge m ON a.id = m.drop_id; + `) + }) +} From 503778bae7c2be6d034e859d8648e5be7411d4dc Mon Sep 17 00:00:00 2001 From: Lucas Vaz Date: Wed, 11 Feb 2026 00:00:01 -0300 Subject: [PATCH 2/2] fix: lint --- ...211011159_fix_remove_autores_duplicados.ts | 123 ++++++++++++------ 1 file changed, 82 insertions(+), 41 deletions(-) diff --git a/src/database/migration/20260211011159_fix_remove_autores_duplicados.ts b/src/database/migration/20260211011159_fix_remove_autores_duplicados.ts index 37416c0..976d921 100644 --- a/src/database/migration/20260211011159_fix_remove_autores_duplicados.ts +++ b/src/database/migration/20260211011159_fix_remove_autores_duplicados.ts @@ -1,26 +1,38 @@ import { Knex } from 'knex' +type DupGroupRow = { + nome: string + iniciais: string | null + qtde: number + keep_id: number + ids: string +} + +type CountRow = { c: number } + export async function run(knex: Knex): Promise { - await knex.transaction(async (trx) => { + await knex.transaction(async trx => { const dupGroups = await trx('autores') .select([ 'nome', 'iniciais', trx.raw('COUNT(*) as qtde'), trx.raw('MIN(id) as keep_id'), - trx.raw("GROUP_CONCAT(id ORDER BY id SEPARATOR ',') as ids"), + trx.raw('GROUP_CONCAT(id ORDER BY id SEPARATOR \',\') as ids') ]) .groupBy(['nome', 'iniciais']) .havingRaw('COUNT(*) > 1') if (!dupGroups.length) return + const pairs: Array<{ keep_id: number; drop_id: number }> = [] - for (const g of dupGroups as any[]) { + + for (const g of dupGroups as unknown as DupGroupRow[]) { const keepId = Number(g.keep_id) const ids = String(g.ids) .split(',') - .map((s) => Number(s.trim())) - .filter((n) => Number.isFinite(n)) + .map(s => Number(s.trim())) + .filter(n => Number.isFinite(n)) for (const id of ids) { if (id !== keepId) pairs.push({ keep_id: keepId, drop_id: id }) @@ -28,85 +40,114 @@ export async function run(knex: Knex): Promise { } if (!pairs.length) return - await trx.raw(` - DROP TEMPORARY TABLE IF EXISTS autor_merge; - `) - await trx.raw(` + await trx.raw('DROP TEMPORARY TABLE IF EXISTS autor_merge') + + await trx.raw( + ` CREATE TEMPORARY TABLE autor_merge ( keep_id INT NOT NULL, drop_id INT NOT NULL, PRIMARY KEY (drop_id), KEY (keep_id) - ); - `) + ) + ` + ) + await trx('autor_merge').insert(pairs) - await trx.raw(` + + await trx.raw( + ` UPDATE especies e JOIN autor_merge m ON e.autor_id = m.drop_id - SET e.autor_id = m.keep_id; - `) + SET e.autor_id = m.keep_id + ` + ) - await trx.raw(` + await trx.raw( + ` UPDATE sub_especies se JOIN autor_merge m ON se.autor_id = m.drop_id - SET se.autor_id = m.keep_id; - `) + SET se.autor_id = m.keep_id + ` + ) - await trx.raw(` + await trx.raw( + ` UPDATE variedades v JOIN autor_merge m ON v.autor_id = m.drop_id - SET v.autor_id = m.keep_id; - `) + SET v.autor_id = m.keep_id + ` + ) const hasSubFamiliasAutorId = await trx.schema.hasColumn('sub_familias', 'autor_id') + if (hasSubFamiliasAutorId) { - await trx.raw(` + await trx.raw( + ` UPDATE sub_familias sf JOIN autor_merge m ON sf.autor_id = m.drop_id - SET sf.autor_id = m.keep_id; - `) + SET sf.autor_id = m.keep_id + ` + ) } - const [[rest1]] = await trx.raw(` + + const [rest1Rows] = await trx.raw( + ` SELECT COUNT(*) AS c FROM especies e - JOIN autor_merge m ON e.autor_id = m.drop_id; - `) + JOIN autor_merge m ON e.autor_id = m.drop_id + ` + ) as unknown as [CountRow[], unknown] - const [[rest2]] = await trx.raw(` + const [rest2Rows] = await trx.raw( + ` SELECT COUNT(*) AS c FROM sub_especies se - JOIN autor_merge m ON se.autor_id = m.drop_id; - `) + JOIN autor_merge m ON se.autor_id = m.drop_id + ` + ) as unknown as [CountRow[], unknown] - const [[rest3]] = await trx.raw(` + const [rest3Rows] = await trx.raw( + ` SELECT COUNT(*) AS c FROM variedades v - JOIN autor_merge m ON v.autor_id = m.drop_id; - `) + JOIN autor_merge m ON v.autor_id = m.drop_id + ` + ) as unknown as [CountRow[], unknown] - const restantes = - Number(rest1?.c ?? 0) + Number(rest2?.c ?? 0) + Number(rest3?.c ?? 0) + const rest1 = rest1Rows[0] + const rest2 = rest2Rows[0] + const rest3 = rest3Rows[0] + + const restantes = Number(rest1?.c ?? 0) + Number(rest2?.c ?? 0) + Number(rest3?.c ?? 0) if (restantes > 0) { throw new Error(`Merge abortado: ainda existem ${restantes} referências para autores duplicados (DROP).`) } if (hasSubFamiliasAutorId) { - const [[rest4]] = await trx.raw(` + const [rest4Rows] = await trx.raw( + ` SELECT COUNT(*) AS c FROM sub_familias sf - JOIN autor_merge m ON sf.autor_id = m.drop_id; - `) + JOIN autor_merge m ON sf.autor_id = m.drop_id + ` + ) as unknown as [CountRow[], unknown] + + const rest4 = rest4Rows[0] + if (Number(rest4?.c ?? 0) > 0) { - throw new Error(`Merge abortado: ainda existem ${rest4.c} referências em sub_familias para autores duplicados (DROP).`) + throw new Error(`Merge abortado: ainda existem ${rest4?.c} referências em sub_familias para autores duplicados (DROP).`) } } - await trx.raw(` + await trx.raw( + ` DELETE a FROM autores a - JOIN autor_merge m ON a.id = m.drop_id; - `) + JOIN autor_merge m ON a.id = m.drop_id + ` + ) }) }