using Microsoft.EntityFrameworkCore.Migrations; #nullable disable namespace Flaro.Data.Migrations { /// public partial class Dubletten : Migration { /// protected override void Up(MigrationBuilder migrationBuilder) { migrationBuilder.AddColumn( name: "VersionLabel", table: "MediaFiles", type: "TEXT", maxLength: 64, nullable: true); // **Erst aufräumen, dann die Regel aufstellen.** Andersherum // scheitert diese Migration auf jeder Datenbank, in der schon // Dubletten stehen — und zwar beim Start im Container, wo die // Meldung niemand sieht und der Server einfach nicht hochkommt. // // In SQL und nicht über die Anwendung, weil Migrationen vor allem // anderen laufen: zu diesem Zeitpunkt gibt es noch keine Dienste. // // Der älteste Eintrag gewinnt. Die Kennungen sind Guid v7 und // damit zeitlich geordnet, `MIN` liefert also den zuerst // angelegten — den, auf den vorhandene Verläufe schon zeigen. migrationBuilder.Sql(""" CREATE TEMP TABLE FilmDubletten AS SELECT m.Id AS Verlierer, (SELECT MIN(s.Id) FROM Movies s WHERE s.LibraryId = m.LibraryId AND s.TmdbId = m.TmdbId) AS Sieger FROM Movies m WHERE m.TmdbId IS NOT NULL AND m.Id <> (SELECT MIN(s.Id) FROM Movies s WHERE s.LibraryId = m.LibraryId AND s.TmdbId = m.TmdbId); """); // Die Dateien zuerst — sie sind das Einzige, was sich nicht // wiederherstellen lässt. migrationBuilder.Sql(""" UPDATE MediaFiles SET MovieId = (SELECT Sieger FROM FilmDubletten WHERE Verlierer = MediaFiles.MovieId) WHERE MovieId IN (SELECT Verlierer FROM FilmDubletten); """); // **Sehfortschritt: erst die konfliktfreien umhängen.** Hat // derselbe Benutzer beide Einträge angefangen, ginge das nicht — // die Eindeutigkeitsregel auf UserItemStates verböte die zweite // Zeile. migrationBuilder.Sql(""" UPDATE UserItemStates SET MovieId = (SELECT Sieger FROM FilmDubletten WHERE Verlierer = UserItemStates.MovieId) WHERE MovieId IN (SELECT Verlierer FROM FilmDubletten) AND NOT EXISTS ( SELECT 1 FROM UserItemStates z WHERE z.UserId = UserItemStates.UserId AND z.MovieId = (SELECT Sieger FROM FilmDubletten WHERE Verlierer = UserItemStates.MovieId)); """); // Für die übrigen gewinnt der weitere Stand — niemand will // Sehfortschritt verlieren, nur weil zwei Einträge verschmelzen. migrationBuilder.Sql(""" UPDATE UserItemStates SET Position = MAX(Position, COALESCE(( SELECT a.Position FROM UserItemStates a JOIN FilmDubletten d ON a.MovieId = d.Verlierer WHERE a.UserId = UserItemStates.UserId AND d.Sieger = UserItemStates.MovieId LIMIT 1), 0)), Watched = MAX(Watched, COALESCE(( SELECT a.Watched FROM UserItemStates a JOIN FilmDubletten d ON a.MovieId = d.Verlierer WHERE a.UserId = UserItemStates.UserId AND d.Sieger = UserItemStates.MovieId LIMIT 1), 0)), PlayCount = MAX(PlayCount, COALESCE(( SELECT a.PlayCount FROM UserItemStates a JOIN FilmDubletten d ON a.MovieId = d.Verlierer WHERE a.UserId = UserItemStates.UserId AND d.Sieger = UserItemStates.MovieId LIMIT 1), 0)) WHERE MovieId IN (SELECT Sieger FROM FilmDubletten); """); migrationBuilder.Sql( "DELETE FROM UserItemStates WHERE MovieId IN (SELECT Verlierer FROM FilmDubletten);"); // Genres und Mitwirkende sind abgeleitet — sie entstehen beim // nächsten Auffrischen neu. Umhängen wäre mehr Aufwand für // dasselbe Ergebnis. migrationBuilder.Sql( "DELETE FROM GenreLinks WHERE MovieId IN (SELECT Verlierer FROM FilmDubletten);"); migrationBuilder.Sql( "DELETE FROM Credits WHERE MovieId IN (SELECT Verlierer FROM FilmDubletten);"); migrationBuilder.Sql( "DELETE FROM Movies WHERE Id IN (SELECT Verlierer FROM FilmDubletten);"); migrationBuilder.Sql("DROP TABLE FilmDubletten;"); // --- Dasselbe für Serien --------------------------------------- migrationBuilder.Sql(""" CREATE TEMP TABLE SerienDubletten AS SELECT x.Id AS Verlierer, (SELECT MIN(s.Id) FROM Series s WHERE s.LibraryId = x.LibraryId AND s.TmdbId = x.TmdbId) AS Sieger FROM Series x WHERE x.TmdbId IS NOT NULL AND x.Id <> (SELECT MIN(s.Id) FROM Series s WHERE s.LibraryId = x.LibraryId AND s.TmdbId = x.TmdbId); """); // Folgen, deren Platz beim Sieger frei ist, wandern mit. migrationBuilder.Sql(""" UPDATE Episodes SET SeriesId = (SELECT Sieger FROM SerienDubletten WHERE Verlierer = Episodes.SeriesId) WHERE SeriesId IN (SELECT Verlierer FROM SerienDubletten) AND NOT EXISTS ( SELECT 1 FROM Episodes e WHERE e.SeriesId = (SELECT Sieger FROM SerienDubletten WHERE Verlierer = Episodes.SeriesId) AND e.SeasonNumber = Episodes.SeasonNumber AND e.EpisodeNumber = Episodes.EpisodeNumber); """); // Die übrigen sind doppelt belegt: die Datei wandert zur Folge des // Siegers, die doppelte Folge fällt weg. Sonst stünde S01E01 // zweimal in der Staffelliste. migrationBuilder.Sql(""" UPDATE MediaFiles SET EpisodeId = ( SELECT e.Id FROM Episodes e JOIN Episodes alt ON alt.Id = MediaFiles.EpisodeId JOIN SerienDubletten d ON d.Verlierer = alt.SeriesId WHERE e.SeriesId = d.Sieger AND e.SeasonNumber = alt.SeasonNumber AND e.EpisodeNumber = alt.EpisodeNumber) WHERE EpisodeId IN ( SELECT alt.Id FROM Episodes alt JOIN SerienDubletten d ON d.Verlierer = alt.SeriesId); """); migrationBuilder.Sql(""" DELETE FROM UserItemStates WHERE EpisodeId IN ( SELECT alt.Id FROM Episodes alt JOIN SerienDubletten d ON d.Verlierer = alt.SeriesId); """); migrationBuilder.Sql(""" DELETE FROM Episodes WHERE SeriesId IN (SELECT Verlierer FROM SerienDubletten); """); migrationBuilder.Sql( "DELETE FROM GenreLinks WHERE SeriesId IN (SELECT Verlierer FROM SerienDubletten);"); migrationBuilder.Sql( "DELETE FROM Credits WHERE SeriesId IN (SELECT Verlierer FROM SerienDubletten);"); migrationBuilder.Sql( "DELETE FROM Series WHERE Id IN (SELECT Verlierer FROM SerienDubletten);"); migrationBuilder.Sql("DROP TABLE SerienDubletten;"); migrationBuilder.CreateIndex( name: "IX_Series_LibraryId_TmdbId", table: "Series", columns: new[] { "LibraryId", "TmdbId" }, unique: true, filter: "\"TmdbId\" IS NOT NULL"); migrationBuilder.CreateIndex( name: "IX_Movies_LibraryId_TmdbId", table: "Movies", columns: new[] { "LibraryId", "TmdbId" }, unique: true, filter: "\"TmdbId\" IS NOT NULL"); } /// protected override void Down(MigrationBuilder migrationBuilder) { migrationBuilder.DropIndex( name: "IX_Series_LibraryId_TmdbId", table: "Series"); migrationBuilder.DropIndex( name: "IX_Movies_LibraryId_TmdbId", table: "Movies"); migrationBuilder.DropColumn( name: "VersionLabel", table: "MediaFiles"); } } }