* Extended functionalities
* Added different UIs
This commit is contained in:
135
database/scripts/007_remove_duplicate_recipes_by_name.sql
Normal file
135
database/scripts/007_remove_duplicate_recipes_by_name.sql
Normal file
@@ -0,0 +1,135 @@
|
||||
-- 007_remove_duplicate_recipes_by_name.sql
|
||||
-- Removes duplicate recipes that share the same "Name" (trimmed, exact match).
|
||||
-- Keeps the row with the lowest "Id" for each name.
|
||||
--
|
||||
-- Analysis of Recipes-2.csv export (196 rows):
|
||||
-- 168 unique names, 28 duplicate groups, 28 rows to delete (Ids 113–140).
|
||||
-- Those duplicate Ids mirror Ids 29–56 one-to-one.
|
||||
--
|
||||
-- Run inside a transaction. Review the preview queries before COMMIT.
|
||||
|
||||
BEGIN;
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- 1) Preview duplicates
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- SELECT
|
||||
-- r."Name",
|
||||
-- array_agg(r."Id" ORDER BY r."Id") AS ids,
|
||||
-- min(r."Id") AS keep_id,
|
||||
-- array_agg(r."Id" ORDER BY r."Id") FILTER (WHERE r."Id" <> min(r."Id")) AS remove_ids
|
||||
-- FROM diet."Recipes" r
|
||||
-- GROUP BY r."Name"
|
||||
-- HAVING count(*) > 1
|
||||
-- ORDER BY min(r."Id");
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- 2) Build duplicate map: remove_id -> keep_id
|
||||
-- ---------------------------------------------------------------------------
|
||||
CREATE TEMP TABLE recipe_dedup_map ON COMMIT DROP AS
|
||||
SELECT
|
||||
r."Id" AS remove_id,
|
||||
d.keep_id
|
||||
FROM diet."Recipes" r
|
||||
INNER JOIN (
|
||||
SELECT btrim("Name") AS name_key, MIN("Id") AS keep_id
|
||||
FROM diet."Recipes"
|
||||
GROUP BY btrim("Name")
|
||||
HAVING COUNT(*) > 1
|
||||
) d ON btrim(r."Name") = d.name_key
|
||||
WHERE r."Id" <> d.keep_id;
|
||||
|
||||
-- Optional: restrict to known duplicate ids from export
|
||||
-- DELETE FROM recipe_dedup_map WHERE remove_id NOT BETWEEN 113 AND 140;
|
||||
|
||||
DO $$
|
||||
DECLARE
|
||||
dup_count int;
|
||||
BEGIN
|
||||
SELECT count(*) INTO dup_count FROM recipe_dedup_map;
|
||||
RAISE NOTICE 'Duplicate recipe rows to remove: %', dup_count;
|
||||
END $$;
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- 3) Re-point foreign keys to the canonical (lowest-id) recipe
|
||||
-- ---------------------------------------------------------------------------
|
||||
|
||||
-- Meal planner / diet generator drafts
|
||||
UPDATE diet."MealPlanEntries" e
|
||||
SET "RecipeId" = m.keep_id,
|
||||
"UpdatedAt" = now()
|
||||
FROM recipe_dedup_map m
|
||||
WHERE e."RecipeId" = m.remove_id;
|
||||
|
||||
UPDATE diet."DietGeneratorDraftMeals" d
|
||||
SET "RecipeId" = m.keep_id
|
||||
FROM recipe_dedup_map m
|
||||
WHERE d."RecipeId" = m.remove_id;
|
||||
|
||||
-- AI recipe generator saved draft pointer
|
||||
UPDATE diet."RecipeGeneratorSessions" s
|
||||
SET "SavedRecipeId" = m.keep_id,
|
||||
"UpdatedAt" = now()
|
||||
FROM recipe_dedup_map m
|
||||
WHERE s."SavedRecipeId" = m.remove_id;
|
||||
|
||||
-- Diet tracking (nullable FK)
|
||||
UPDATE diet."MealConsumptions" c
|
||||
SET "RecipeId" = m.keep_id
|
||||
FROM recipe_dedup_map m
|
||||
WHERE c."RecipeId" = m.remove_id;
|
||||
|
||||
-- Favorites: drop rows that would collide after merge, then re-point the rest
|
||||
DELETE FROM diet."UserFavoriteRecipes" f
|
||||
USING recipe_dedup_map m
|
||||
WHERE f."RecipeId" = m.remove_id
|
||||
AND EXISTS (
|
||||
SELECT 1
|
||||
FROM diet."UserFavoriteRecipes" f2
|
||||
WHERE f2."UserId" = f."UserId"
|
||||
AND f2."RecipeId" = m.keep_id
|
||||
);
|
||||
|
||||
UPDATE diet."UserFavoriteRecipes" f
|
||||
SET "RecipeId" = m.keep_id
|
||||
FROM recipe_dedup_map m
|
||||
WHERE f."RecipeId" = m.remove_id;
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- 4) Delete duplicate recipes (Ingredients + RecipeSteps cascade)
|
||||
-- ---------------------------------------------------------------------------
|
||||
DELETE FROM diet."Recipes" r
|
||||
USING recipe_dedup_map m
|
||||
WHERE r."Id" = m.remove_id;
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- 5) Verify
|
||||
-- ---------------------------------------------------------------------------
|
||||
DO $$
|
||||
DECLARE
|
||||
remaining_dup_names int;
|
||||
recipe_count int;
|
||||
BEGIN
|
||||
SELECT count(*) INTO remaining_dup_names
|
||||
FROM (
|
||||
SELECT btrim("Name")
|
||||
FROM diet."Recipes"
|
||||
GROUP BY btrim("Name")
|
||||
HAVING count(*) > 1
|
||||
) d;
|
||||
|
||||
SELECT count(*) INTO recipe_count FROM diet."Recipes";
|
||||
|
||||
IF remaining_dup_names > 0 THEN
|
||||
RAISE EXCEPTION 'Duplicate recipe names still present: % groups', remaining_dup_names;
|
||||
END IF;
|
||||
|
||||
RAISE NOTICE 'Done. Active recipe count: %', recipe_count;
|
||||
END $$;
|
||||
|
||||
COMMIT;
|
||||
|
||||
-- Optional hardening (uncomment if every recipe name must be unique going forward):
|
||||
-- CREATE UNIQUE INDEX IF NOT EXISTS "UX_Recipes_Name"
|
||||
-- ON diet."Recipes" (btrim("Name"))
|
||||
-- WHERE "IsActive" AND NOT "IsDraft";
|
||||
Reference in New Issue
Block a user