-- +goose Up -- +goose StatementBegin -- A CPV code has one official description, so it is now one row whatever -- description it arrives with: EU Supply cuts descriptions to 64 characters, -- which made a second row for each code it sent. The duplicates are merged -- into the row with the longest description, and the upsert keeps the -- longest from now on. Other schemes still get a row per distinct content. -- The row each CPV row merges into. CREATE TEMP TABLE "cpv_merge" AS SELECT "c"."id" AS "id", ( SELECT "k"."id" FROM "classification_codes" "k" WHERE "k"."is_cpv" AND "k"."code" = "c"."code" ORDER BY length("k"."classification") DESC, "k"."id" LIMIT 1 ) AS "keep_id" FROM "classification_codes" "c" WHERE "c"."is_cpv"; -- An opportunity linked to both rows keeps only its link to the kept one. DELETE FROM "opportunity_classifications" WHERE "classification_id" IN ( SELECT "id" FROM "cpv_merge" WHERE "id" <> "keep_id" ) AND EXISTS ( SELECT 1 FROM "cpv_merge" "m" JOIN "opportunity_classifications" "kept" ON "kept"."classification_id" = "m"."keep_id" WHERE "m"."id" = "opportunity_classifications"."classification_id" AND "kept"."opportunity_uuid" = "opportunity_classifications"."opportunity_uuid" ); UPDATE "opportunity_classifications" SET "classification_id" = ( SELECT "keep_id" FROM "cpv_merge" WHERE "id" = "opportunity_classifications"."classification_id" ) WHERE "classification_id" IN ( SELECT "id" FROM "cpv_merge" WHERE "id" <> "keep_id" ); DELETE FROM "classification_codes" WHERE "id" IN (SELECT "id" FROM "cpv_merge" WHERE "id" <> "keep_id"); DROP TABLE "cpv_merge"; CREATE UNIQUE INDEX IF NOT EXISTS "classification_codes_cpv" ON "classification_codes" ("code") WHERE "is_cpv"; -- +goose StatementEnd -- +goose Down -- +goose StatementBegin -- The merged duplicates aren't restored: they only differed by a cut-off -- description. DROP INDEX IF EXISTS "classification_codes_cpv"; -- +goose StatementEnd