Implemented the targeted Timeline performance pass and applied the SQL locally.
This commit is contained in:
parent
a3ce37c66c
commit
b5b353e80c
@ -102,6 +102,7 @@ public interface ILookupRepository
|
||||
public interface ITimelineRepository
|
||||
{
|
||||
Task<TimelineData> GetByProjectAsync(int projectId, int? bookId, bool includeMetrics, IEnumerable<int> metricTypeIds, bool includePlotLines);
|
||||
Task<IReadOnlyList<TimelineSceneOverviewRow>> GetSceneOverviewsAsync(int projectId, int? bookId);
|
||||
}
|
||||
|
||||
public interface ITimelinePresetRepository
|
||||
@ -154,7 +155,7 @@ public interface IPlotRepository
|
||||
|
||||
public interface IAssetRepository
|
||||
{
|
||||
Task<AssetLookupData> GetLookupsAsync();
|
||||
Task<AssetLookupData> GetLookupsAsync(int? projectId = null);
|
||||
Task<IReadOnlyList<StoryAsset>> ListAssetsAsync(int projectId);
|
||||
Task<StoryAsset?> GetAssetAsync(int storyAssetId);
|
||||
Task<int> SaveAssetAsync(StoryAsset asset);
|
||||
@ -178,7 +179,7 @@ public interface IAssetRepository
|
||||
|
||||
public interface ICharacterRepository
|
||||
{
|
||||
Task<CharacterLookupData> GetLookupsAsync();
|
||||
Task<CharacterLookupData> GetLookupsAsync(int? projectId = null);
|
||||
Task<IReadOnlyList<Character>> ListCharactersAsync(int projectId);
|
||||
Task<Character?> GetCharacterAsync(int characterId);
|
||||
Task<int> SaveCharacterAsync(Character character);
|
||||
@ -419,6 +420,19 @@ public sealed class TimelineData
|
||||
public IReadOnlyList<ThreadEvent> ThreadEvents { get; init; } = [];
|
||||
}
|
||||
|
||||
public sealed class TimelineSceneOverviewRow
|
||||
{
|
||||
public int SceneID { get; set; }
|
||||
public int? CharacterID { get; set; }
|
||||
public string? CharacterName { get; set; }
|
||||
public bool IsPov { get; set; }
|
||||
public int? StoryAssetID { get; set; }
|
||||
public string? AssetName { get; set; }
|
||||
public int? AssetImportance { get; set; }
|
||||
public string? AssetEventTypeName { get; set; }
|
||||
public string? LocationName { get; set; }
|
||||
}
|
||||
|
||||
public sealed class LocationRepository(ISqlConnectionFactory connectionFactory) : ILocationRepository
|
||||
{
|
||||
public async Task<LocationLookupData> GetLookupsAsync()
|
||||
@ -2520,6 +2534,16 @@ public sealed class TimelineRepository(ISqlConnectionFactory connectionFactory)
|
||||
public int PlotEventID { get; set; }
|
||||
public int PlotLineID { get; set; }
|
||||
}
|
||||
|
||||
public async Task<IReadOnlyList<TimelineSceneOverviewRow>> GetSceneOverviewsAsync(int projectId, int? bookId)
|
||||
{
|
||||
using var connection = connectionFactory.CreateConnection();
|
||||
var rows = await connection.QueryAsync<TimelineSceneOverviewRow>(
|
||||
"dbo.Timeline_SceneOverviews",
|
||||
new { ProjectID = projectId, BookID = bookId },
|
||||
commandType: CommandType.StoredProcedure);
|
||||
return rows.ToList();
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class TimelineSettingsRepository(ISqlConnectionFactory connectionFactory) : ITimelineSettingsRepository
|
||||
@ -2902,10 +2926,10 @@ public sealed class PlotRepository(ISqlConnectionFactory connectionFactory) : IP
|
||||
|
||||
public sealed class AssetRepository(ISqlConnectionFactory connectionFactory) : IAssetRepository
|
||||
{
|
||||
public async Task<AssetLookupData> GetLookupsAsync()
|
||||
public async Task<AssetLookupData> GetLookupsAsync(int? projectId = null)
|
||||
{
|
||||
using var connection = connectionFactory.CreateConnection();
|
||||
using var result = await connection.QueryMultipleAsync("dbo.AssetLookup_All", commandType: CommandType.StoredProcedure);
|
||||
using var result = await connection.QueryMultipleAsync("dbo.AssetLookup_All", new { ProjectID = projectId }, commandType: CommandType.StoredProcedure);
|
||||
return new AssetLookupData
|
||||
{
|
||||
AssetKinds = (await result.ReadAsync<AssetKind>()).ToList(),
|
||||
@ -3148,10 +3172,10 @@ public sealed class AssetRepository(ISqlConnectionFactory connectionFactory) : I
|
||||
|
||||
public sealed class CharacterRepository(ISqlConnectionFactory connectionFactory) : ICharacterRepository
|
||||
{
|
||||
public async Task<CharacterLookupData> GetLookupsAsync()
|
||||
public async Task<CharacterLookupData> GetLookupsAsync(int? projectId = null)
|
||||
{
|
||||
using var connection = connectionFactory.CreateConnection();
|
||||
using var result = await connection.QueryMultipleAsync("dbo.CharacterLookup_All", commandType: CommandType.StoredProcedure);
|
||||
using var result = await connection.QueryMultipleAsync("dbo.CharacterLookup_All", new { ProjectID = projectId }, commandType: CommandType.StoredProcedure);
|
||||
return new CharacterLookupData
|
||||
{
|
||||
RoleTypes = (await result.ReadAsync<CharacterRoleInSceneType>()).ToList(),
|
||||
|
||||
@ -2134,8 +2134,8 @@ public sealed class TimelineService(
|
||||
.ToList();
|
||||
var needsPlotTimelineData = settings.ShowPlotLines || filter.PlotLineID.HasValue || filter.PlotThreadID.HasValue || filter.FocusType is "plotline" or "plotthread";
|
||||
var needsMetricTimelineData = settings.ShowMetricShape && selectedMetricIds.Any();
|
||||
var needsAssetTimelineData = settings.ShowSceneCards || settings.ShowStoryAssets || filter.StoryAssetID.HasValue || filter.FocusType == "asset";
|
||||
var needsCharacterTimelineData = settings.ShowSceneCards || settings.ShowCharacterAppearances || filter.CharacterID.HasValue || filter.FocusType == "character";
|
||||
var needsAssetTimelineData = settings.ShowStoryAssets || filter.StoryAssetID.HasValue || filter.FocusType == "asset";
|
||||
var needsCharacterTimelineData = settings.ShowCharacterAppearances || filter.CharacterID.HasValue || filter.FocusType == "character";
|
||||
var needsWarnings = settings.ShowWarnings || filter.WarningSeverityID.HasValue || filter.WarningTypeID.HasValue || filter.ShowWarningsOnly;
|
||||
|
||||
var timeline = await TimeTimelineLoadAsync(
|
||||
@ -2150,8 +2150,8 @@ public sealed class TimelineService(
|
||||
|
||||
var allBooks = await TimeTimelineLoadAsync("dbo.Book_ListByProject", () => books.ListByProjectAsync(projectId), projectId, bookId);
|
||||
var lookupData = await TimeTimelineLoadAsync("dbo.Lookup_All", lookups.GetAllAsync, projectId, bookId);
|
||||
var assetLookupData = await TimeTimelineLoadAsync("dbo.AssetLookup_All", assets.GetLookupsAsync, projectId, bookId);
|
||||
var characterLookupData = await TimeTimelineLoadAsync("dbo.CharacterLookup_All", characters.GetLookupsAsync, projectId, bookId);
|
||||
var assetLookupData = await TimeTimelineLoadAsync("dbo.AssetLookup_All", () => assets.GetLookupsAsync(projectId), projectId, bookId);
|
||||
var characterLookupData = await TimeTimelineLoadAsync("dbo.CharacterLookup_All", () => characters.GetLookupsAsync(projectId), projectId, bookId);
|
||||
var locationLookupData = await TimeTimelineLoadAsync("dbo.LocationLookup_All", locations.GetLookupsAsync, projectId, bookId);
|
||||
var plotLines = await TimeTimelineLoadAsync("dbo.PlotLine_ListByProject", () => plots.ListPlotLinesAsync(projectId), projectId, bookId);
|
||||
var plotThreads = await TimeTimelineLoadAsync("dbo.PlotThread_ListByProject", () => plots.ListPlotThreadsByProjectAsync(projectId), projectId, bookId);
|
||||
@ -2166,10 +2166,14 @@ public sealed class TimelineService(
|
||||
var timelineWarnings = needsWarnings
|
||||
? await TimeTimelineLoadAsync("dbo.ContinuityWarning_List", () => warnings.ListAsync(projectId, bookId, null, filter.WarningTypeID, filter.WarningSeverityID, null, filter.IncludeDismissedWarnings, filter.IncludeIntentionalWarnings), projectId, bookId)
|
||||
: [];
|
||||
var warningsByScene = timelineWarnings
|
||||
.Where(x => x.SceneID.HasValue)
|
||||
.GroupBy(x => x.SceneID!.Value)
|
||||
.ToDictionary(x => x.Key, x => (IReadOnlyList<ContinuityWarning>)x.ToList());
|
||||
var warningsByScene = await TimeTimelineLoadAsync(
|
||||
"TimelineService.PrepareWarnings",
|
||||
() => Task.FromResult(timelineWarnings
|
||||
.Where(x => x.SceneID.HasValue)
|
||||
.GroupBy(x => x.SceneID!.Value)
|
||||
.ToDictionary(x => x.Key, x => (IReadOnlyList<ContinuityWarning>)x.ToList())),
|
||||
projectId,
|
||||
bookId);
|
||||
var warningCounts = warningsByScene.ToDictionary(x => x.Key, x => x.Value.Count);
|
||||
var dependencyCounts = (await TimeTimelineLoadAsync("dbo.SceneDependency_Counts", () => sceneDependencies.GetCountsAsync(projectId, bookId), projectId, bookId)).ToDictionary(x => x.SceneID, x => x.DependencyCount);
|
||||
|
||||
@ -2187,6 +2191,9 @@ public sealed class TimelineService(
|
||||
? await TimeTimelineLoadAsync("dbo.CharacterTimeline_GetByProject", () => characters.GetTimelineAsync(projectId, bookId), projectId, bookId)
|
||||
: ([], []);
|
||||
ApplyTimelineCharacterAvatars(characterTimeline, allCharacters);
|
||||
var sceneOverviewRows = settings.ShowSceneCards
|
||||
? await TimeTimelineLoadAsync("dbo.Timeline_SceneOverviews", () => timelineRepository.GetSceneOverviewsAsync(projectId, bookId), projectId, bookId)
|
||||
: [];
|
||||
var plotThreadDetailsById = plotThreads.ToDictionary(x => x.PlotThreadID);
|
||||
foreach (var threadEvent in timeline.ThreadEvents)
|
||||
{
|
||||
@ -2202,7 +2209,22 @@ public sealed class TimelineService(
|
||||
.ToDictionary(x => x.Key, x => x.First().CharacterName);
|
||||
var matchingSceneIds = await TimeTimelineLoadAsync("TimelineService.BuildMatchingSceneIds", () => BuildMatchingSceneIdsAsync(filter, orderedScenes, timeline.ThreadEvents, assetTimeline.Events, characterTimeline.Appearances, projectId, bookId), projectId, bookId);
|
||||
var focusSceneIds = await TimeTimelineLoadAsync("TimelineService.BuildFocusSceneIds", () => BuildFocusSceneIdsAsync(filter, orderedScenes, timeline.ThreadEvents, assetTimeline.Events, characterTimeline.Appearances, projectId, bookId), projectId, bookId);
|
||||
var focusLabel = BuildFocusLabel(filter, plotLines, plotThreads, allAssets, allCharacters, allLocations);
|
||||
var focusLabel = await TimeTimelineLoadAsync("TimelineService.BuildFocusLabel", () => Task.FromResult(BuildFocusLabel(filter, plotLines, plotThreads, allAssets, allCharacters, allLocations)), projectId, bookId);
|
||||
var sceneOverviews = settings.ShowSceneCards
|
||||
? await TimeTimelineLoadAsync("TimelineService.BuildSceneOverviews", () => Task.FromResult(BuildSceneOverviews(orderedScenes, sceneOverviewRows)), projectId, bookId)
|
||||
: new Dictionary<int, TimelineSceneOverviewViewModel>();
|
||||
var metricGraphs = settings.ShowMetricShape
|
||||
? await TimeTimelineLoadAsync("TimelineService.BuildMetricGraphs", () => Task.FromResult(BuildMetricGraphs(timeline, orderedScenes)), projectId, bookId)
|
||||
: [];
|
||||
var plotLanes = settings.ShowPlotLines
|
||||
? await TimeTimelineLoadAsync("TimelineService.BuildPlotLanes", () => Task.FromResult(BuildPlotLanes(timeline, orderedScenes)), projectId, bookId)
|
||||
: [];
|
||||
var assetLanes = settings.ShowStoryAssets
|
||||
? await TimeTimelineLoadAsync("TimelineService.BuildAssetLanes", () => Task.FromResult(BuildAssetLanes(assetTimeline.Assets, assetTimeline.Events, orderedScenes)), projectId, bookId)
|
||||
: [];
|
||||
var characterLanes = settings.ShowCharacterAppearances
|
||||
? await TimeTimelineLoadAsync("TimelineService.BuildCharacterLanes", () => Task.FromResult(BuildCharacterLanes(characterTimeline.Characters, characterTimeline.Appearances, orderedScenes)), projectId, bookId)
|
||||
: [];
|
||||
var selectedScene = selectedSceneId.HasValue
|
||||
? await TimeTimelineLoadAsync("SceneService.GetEditAsync", () => scenes.GetEditAsync(selectedSceneId.Value), projectId, bookId)
|
||||
: null;
|
||||
@ -2277,11 +2299,11 @@ public sealed class TimelineService(
|
||||
.ToList()
|
||||
}).ToList(),
|
||||
OrderedScenes = orderedScenes,
|
||||
MetricGraphs = settings.ShowMetricShape ? BuildMetricGraphs(timeline, orderedScenes) : [],
|
||||
PlotLanes = settings.ShowPlotLines ? BuildPlotLanes(timeline, orderedScenes) : [],
|
||||
AssetLanes = settings.ShowStoryAssets ? BuildAssetLanes(assetTimeline.Assets, assetTimeline.Events, orderedScenes) : [],
|
||||
CharacterLanes = settings.ShowCharacterAppearances ? BuildCharacterLanes(characterTimeline.Characters, characterTimeline.Appearances, orderedScenes) : [],
|
||||
SceneOverviews = settings.ShowSceneCards ? BuildSceneOverviews(orderedScenes, characterTimeline.Appearances, assetTimeline.Assets, assetTimeline.Events) : new Dictionary<int, TimelineSceneOverviewViewModel>(),
|
||||
MetricGraphs = metricGraphs,
|
||||
PlotLanes = plotLanes,
|
||||
AssetLanes = assetLanes,
|
||||
CharacterLanes = characterLanes,
|
||||
SceneOverviews = sceneOverviews,
|
||||
WarningsByScene = warningsByScene,
|
||||
MatchingSceneIDs = matchingSceneIds,
|
||||
FocusSceneIDs = focusSceneIds,
|
||||
@ -2792,60 +2814,59 @@ public sealed class TimelineService(
|
||||
|
||||
private static IReadOnlyDictionary<int, TimelineSceneOverviewViewModel> BuildSceneOverviews(
|
||||
IReadOnlyList<Scene> orderedScenes,
|
||||
IReadOnlyList<SceneCharacter> appearances,
|
||||
IReadOnlyList<StoryAsset> assets,
|
||||
IReadOnlyList<AssetEvent> assetEvents)
|
||||
IReadOnlyList<TimelineSceneOverviewRow> overviewRows)
|
||||
{
|
||||
var assetImportanceById = assets.ToDictionary(x => x.StoryAssetID, x => x.Importance);
|
||||
var charactersByScene = appearances
|
||||
var charactersByScene = overviewRows
|
||||
.Where(x => x.CharacterID.HasValue && !string.IsNullOrWhiteSpace(x.CharacterName))
|
||||
.GroupBy(x => x.SceneID)
|
||||
.ToDictionary(
|
||||
sceneGroup => sceneGroup.Key,
|
||||
sceneGroup => sceneGroup
|
||||
.GroupBy(x => x.CharacterID)
|
||||
.GroupBy(x => x.CharacterID!.Value)
|
||||
.Select(characterGroup =>
|
||||
{
|
||||
var first = characterGroup
|
||||
.OrderByDescending(x => string.Equals(x.RoleInSceneTypeName, "POV Character", StringComparison.OrdinalIgnoreCase))
|
||||
.ThenBy(x => x.SceneCharacterID)
|
||||
.OrderByDescending(x => x.IsPov)
|
||||
.ThenBy(x => x.CharacterName)
|
||||
.First();
|
||||
return new TimelineSceneCharacterOverviewViewModel
|
||||
{
|
||||
CharacterID = first.CharacterID,
|
||||
CharacterName = first.CharacterName,
|
||||
IsPov = string.Equals(first.RoleInSceneTypeName, "POV Character", StringComparison.OrdinalIgnoreCase)
|
||||
CharacterID = first.CharacterID!.Value,
|
||||
CharacterName = first.CharacterName ?? string.Empty,
|
||||
IsPov = first.IsPov
|
||||
};
|
||||
})
|
||||
.OrderByDescending(x => x.IsPov)
|
||||
.ThenBy(x => x.CharacterName)
|
||||
.ToList());
|
||||
var assetsByScene = assetEvents
|
||||
var assetsByScene = overviewRows
|
||||
.Where(x => x.StoryAssetID.HasValue && !string.IsNullOrWhiteSpace(x.AssetName))
|
||||
.GroupBy(x => x.SceneID)
|
||||
.ToDictionary(
|
||||
sceneGroup => sceneGroup.Key,
|
||||
sceneGroup => sceneGroup
|
||||
.GroupBy(x => x.StoryAssetID)
|
||||
.GroupBy(x => x.StoryAssetID!.Value)
|
||||
.Select(assetGroup =>
|
||||
{
|
||||
var first = assetGroup.OrderByDescending(x => x.UpdatedDate).First();
|
||||
var first = assetGroup.First();
|
||||
return new TimelineSceneAssetOverviewViewModel
|
||||
{
|
||||
StoryAssetID = first.StoryAssetID,
|
||||
AssetName = first.AssetName,
|
||||
Importance = assetImportanceById.GetValueOrDefault(first.StoryAssetID),
|
||||
StoryAssetID = first.StoryAssetID!.Value,
|
||||
AssetName = first.AssetName ?? string.Empty,
|
||||
Importance = first.AssetImportance.GetValueOrDefault(),
|
||||
EventTypeName = first.AssetEventTypeName
|
||||
};
|
||||
})
|
||||
.OrderByDescending(x => x.Importance)
|
||||
.ThenBy(x => x.AssetName)
|
||||
.ToList());
|
||||
var characterLocationsByScene = appearances
|
||||
.Where(x => !string.IsNullOrWhiteSpace(x.LocationPath) || !string.IsNullOrWhiteSpace(x.LocationName))
|
||||
var locationsByScene = overviewRows
|
||||
.Where(x => !string.IsNullOrWhiteSpace(x.LocationName))
|
||||
.GroupBy(x => x.SceneID)
|
||||
.ToDictionary(
|
||||
sceneGroup => sceneGroup.Key,
|
||||
sceneGroup => sceneGroup
|
||||
.Select(x => x.LocationPath ?? x.LocationName ?? string.Empty)
|
||||
.Select(x => x.LocationName ?? string.Empty)
|
||||
.Where(x => !string.IsNullOrWhiteSpace(x))
|
||||
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||
.OrderBy(x => x)
|
||||
@ -2862,7 +2883,7 @@ public sealed class TimelineService(
|
||||
{
|
||||
locations.Add(scene.PrimaryLocationName);
|
||||
}
|
||||
if (characterLocationsByScene.TryGetValue(scene.SceneID, out var characterLocations))
|
||||
if (locationsByScene.TryGetValue(scene.SceneID, out var characterLocations))
|
||||
{
|
||||
locations.AddRange(characterLocations);
|
||||
}
|
||||
|
||||
444
PlotLine/Sql/089_TimelineLoadPerformance.sql
Normal file
444
PlotLine/Sql/089_TimelineLoadPerformance.sql
Normal file
@ -0,0 +1,444 @@
|
||||
SET ANSI_NULLS ON;
|
||||
GO
|
||||
SET QUOTED_IDENTIFIER ON;
|
||||
GO
|
||||
|
||||
/*
|
||||
Timeline-focused performance pass.
|
||||
|
||||
Scope:
|
||||
- Keep the initial Timeline page from loading full character/asset lane data
|
||||
just to render scene-card summaries.
|
||||
- Keep project/book filters as early as possible inside heavy Timeline queries.
|
||||
- Add narrow supporting indexes for the Timeline warning, character, asset,
|
||||
and scene-overview paths.
|
||||
*/
|
||||
|
||||
IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE name = N'IX_ContinuityWarnings_TimelineActiveList' AND object_id = OBJECT_ID(N'dbo.ContinuityWarnings'))
|
||||
BEGIN
|
||||
CREATE INDEX IX_ContinuityWarnings_TimelineActiveList
|
||||
ON dbo.ContinuityWarnings(ProjectID, BookID, WarningSeverityID, WarningTypeID, SceneID, LastDetectedDate DESC, ContinuityWarningID DESC)
|
||||
INCLUDE (ValidationRunID, ChapterID, EntityType, EntityID, Message, Details, CreatedDate)
|
||||
WHERE IsDismissed = 0 AND IsIntentional = 0;
|
||||
END;
|
||||
GO
|
||||
|
||||
IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE name = N'IX_ContinuityWarnings_TimelineList' AND object_id = OBJECT_ID(N'dbo.ContinuityWarnings'))
|
||||
BEGIN
|
||||
CREATE INDEX IX_ContinuityWarnings_TimelineList
|
||||
ON dbo.ContinuityWarnings(ProjectID, BookID, SceneID, EntityType, EntityID, WarningTypeID, WarningSeverityID)
|
||||
INCLUDE (IsDismissed, IsIntentional, LastDetectedDate, ValidationRunID, ChapterID, Message, Details, CreatedDate);
|
||||
END;
|
||||
GO
|
||||
|
||||
IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE name = N'IX_SceneCharacters_SceneID_TimelineOverview' AND object_id = OBJECT_ID(N'dbo.SceneCharacters'))
|
||||
BEGIN
|
||||
CREATE INDEX IX_SceneCharacters_SceneID_TimelineOverview
|
||||
ON dbo.SceneCharacters(SceneID, CharacterID)
|
||||
INCLUDE (SceneCharacterID, RoleInSceneTypeID, PresenceTypeID, LocationID, EntryLocationID, ExitLocationID, AppearanceNotes, OutfitDescription, PhysicalCondition, EmotionalState, KnowledgeNotes, CreatedDate, UpdatedDate);
|
||||
END;
|
||||
GO
|
||||
|
||||
IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE name = N'IX_Books_Project_Active_Timeline' AND object_id = OBJECT_ID(N'dbo.Books'))
|
||||
BEGIN
|
||||
CREATE INDEX IX_Books_Project_Active_Timeline
|
||||
ON dbo.Books(ProjectID, IsArchived, BookID)
|
||||
INCLUDE (BookTitle, Subtitle, SortOrder, BookNumber);
|
||||
END;
|
||||
GO
|
||||
|
||||
IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE name = N'IX_Chapters_Book_Active_Timeline' AND object_id = OBJECT_ID(N'dbo.Chapters'))
|
||||
BEGIN
|
||||
CREATE INDEX IX_Chapters_Book_Active_Timeline
|
||||
ON dbo.Chapters(BookID, IsArchived, ChapterID)
|
||||
INCLUDE (ChapterNumber, ChapterTitle, SortOrder);
|
||||
END;
|
||||
GO
|
||||
|
||||
IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE name = N'IX_Scenes_Chapter_Active_Timeline' AND object_id = OBJECT_ID(N'dbo.Scenes'))
|
||||
BEGIN
|
||||
CREATE INDEX IX_Scenes_Chapter_Active_Timeline
|
||||
ON dbo.Scenes(ChapterID, IsArchived, SceneID)
|
||||
INCLUDE (SceneNumber, SceneTitle, SortOrder, POVCharacterID, PrimaryLocationID, RevisionStatusID);
|
||||
END;
|
||||
GO
|
||||
|
||||
IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE name = N'IX_StoryAssets_Project_TimelineList' AND object_id = OBJECT_ID(N'dbo.StoryAssets'))
|
||||
BEGIN
|
||||
CREATE INDEX IX_StoryAssets_Project_TimelineList
|
||||
ON dbo.StoryAssets(ProjectID, IsArchived, Importance DESC, AssetName)
|
||||
INCLUDE (AssetKindID, CurrentStateID, CurrentLocationID, IsResolved, ShowInQuickAddBar, ImagePath, ThumbnailPath, CreatedDate, UpdatedDate);
|
||||
END;
|
||||
GO
|
||||
|
||||
IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE name = N'IX_AssetEvents_SceneID_TimelineOverview' AND object_id = OBJECT_ID(N'dbo.AssetEvents'))
|
||||
BEGIN
|
||||
CREATE INDEX IX_AssetEvents_SceneID_TimelineOverview
|
||||
ON dbo.AssetEvents(SceneID, StoryAssetID, UpdatedDate DESC)
|
||||
INCLUDE (AssetEventTypeID, EventTitle);
|
||||
END;
|
||||
GO
|
||||
|
||||
IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE name = N'IX_AssetStates_Active_Project_Sort' AND object_id = OBJECT_ID(N'dbo.AssetStates'))
|
||||
BEGIN
|
||||
CREATE INDEX IX_AssetStates_Active_Project_Sort
|
||||
ON dbo.AssetStates(IsActive, ProjectID, SortOrder)
|
||||
INCLUDE (AssetKindID, StateName, Description, IsResolvedState);
|
||||
END;
|
||||
GO
|
||||
|
||||
IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE name = N'IX_CharacterAttributeTypes_Active_Project_Sort' AND object_id = OBJECT_ID(N'dbo.CharacterAttributeTypes'))
|
||||
BEGIN
|
||||
CREATE INDEX IX_CharacterAttributeTypes_Active_Project_Sort
|
||||
ON dbo.CharacterAttributeTypes(IsActive, ProjectID, SortOrder)
|
||||
INCLUDE (AttributeName, DataType, IsNormallyStable);
|
||||
END;
|
||||
GO
|
||||
|
||||
CREATE OR ALTER PROCEDURE dbo.ContinuityWarning_List
|
||||
@ProjectID int,
|
||||
@BookID int = NULL,
|
||||
@SceneID int = NULL,
|
||||
@EntityType nvarchar(80) = NULL,
|
||||
@EntityID int = NULL,
|
||||
@WarningTypeID int = NULL,
|
||||
@WarningSeverityID int = NULL,
|
||||
@IncludeDismissed bit = 0,
|
||||
@IncludeIntentional bit = 0
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
|
||||
IF @IncludeDismissed = 0 AND @IncludeIntentional = 0
|
||||
BEGIN
|
||||
SELECT cw.ContinuityWarningID, cw.ValidationRunID, cw.ProjectID, cw.BookID, b.BookTitle, b.Subtitle AS BookSubtitle,
|
||||
cw.ChapterID, c.ChapterNumber, c.ChapterTitle, cw.SceneID, s.SceneNumber, s.SceneTitle, cw.EntityType,
|
||||
cw.EntityID, cw.WarningTypeID, wt.TypeName AS WarningTypeName, cw.WarningSeverityID, ws.SeverityName,
|
||||
cw.Message, cw.Details, cw.IsDismissed, cw.IsIntentional, cw.CreatedDate, cw.LastDetectedDate
|
||||
FROM dbo.ContinuityWarnings cw
|
||||
INNER JOIN dbo.WarningTypes wt ON wt.WarningTypeID = cw.WarningTypeID
|
||||
INNER JOIN dbo.WarningSeverities ws ON ws.WarningSeverityID = cw.WarningSeverityID
|
||||
LEFT JOIN dbo.Books b ON b.BookID = cw.BookID
|
||||
LEFT JOIN dbo.Chapters c ON c.ChapterID = cw.ChapterID
|
||||
LEFT JOIN dbo.Scenes s ON s.SceneID = cw.SceneID
|
||||
WHERE cw.ProjectID = @ProjectID
|
||||
AND cw.IsDismissed = 0
|
||||
AND cw.IsIntentional = 0
|
||||
AND (@BookID IS NULL OR cw.BookID = @BookID)
|
||||
AND (@SceneID IS NULL OR cw.SceneID = @SceneID)
|
||||
AND (@EntityType IS NULL OR cw.EntityType = @EntityType)
|
||||
AND (@EntityID IS NULL OR cw.EntityID = @EntityID)
|
||||
AND (@WarningTypeID IS NULL OR cw.WarningTypeID = @WarningTypeID)
|
||||
AND (@WarningSeverityID IS NULL OR cw.WarningSeverityID = @WarningSeverityID)
|
||||
ORDER BY ws.SortOrder DESC, cw.LastDetectedDate DESC, cw.ContinuityWarningID DESC
|
||||
OPTION (RECOMPILE);
|
||||
|
||||
RETURN;
|
||||
END;
|
||||
|
||||
SELECT cw.ContinuityWarningID, cw.ValidationRunID, cw.ProjectID, cw.BookID, b.BookTitle, b.Subtitle AS BookSubtitle,
|
||||
cw.ChapterID, c.ChapterNumber, c.ChapterTitle, cw.SceneID, s.SceneNumber, s.SceneTitle, cw.EntityType,
|
||||
cw.EntityID, cw.WarningTypeID, wt.TypeName AS WarningTypeName, cw.WarningSeverityID, ws.SeverityName,
|
||||
cw.Message, cw.Details, cw.IsDismissed, cw.IsIntentional, cw.CreatedDate, cw.LastDetectedDate
|
||||
FROM dbo.ContinuityWarnings cw
|
||||
INNER JOIN dbo.WarningTypes wt ON wt.WarningTypeID = cw.WarningTypeID
|
||||
INNER JOIN dbo.WarningSeverities ws ON ws.WarningSeverityID = cw.WarningSeverityID
|
||||
LEFT JOIN dbo.Books b ON b.BookID = cw.BookID
|
||||
LEFT JOIN dbo.Chapters c ON c.ChapterID = cw.ChapterID
|
||||
LEFT JOIN dbo.Scenes s ON s.SceneID = cw.SceneID
|
||||
WHERE cw.ProjectID = @ProjectID
|
||||
AND (@BookID IS NULL OR cw.BookID = @BookID)
|
||||
AND (@SceneID IS NULL OR cw.SceneID = @SceneID)
|
||||
AND (@EntityType IS NULL OR cw.EntityType = @EntityType)
|
||||
AND (@EntityID IS NULL OR cw.EntityID = @EntityID)
|
||||
AND (@WarningTypeID IS NULL OR cw.WarningTypeID = @WarningTypeID)
|
||||
AND (@WarningSeverityID IS NULL OR cw.WarningSeverityID = @WarningSeverityID)
|
||||
AND (@IncludeDismissed = 1 OR cw.IsDismissed = 0)
|
||||
AND (@IncludeIntentional = 1 OR cw.IsIntentional = 0)
|
||||
ORDER BY ws.SortOrder DESC, cw.LastDetectedDate DESC, cw.ContinuityWarningID DESC
|
||||
OPTION (RECOMPILE);
|
||||
END;
|
||||
GO
|
||||
|
||||
CREATE OR ALTER PROCEDURE dbo.StoryAsset_ListByProject
|
||||
@ProjectID int
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
|
||||
CREATE TABLE #ProjectLocationPaths
|
||||
(
|
||||
LocationID int NOT NULL PRIMARY KEY,
|
||||
LocationName nvarchar(200) NOT NULL,
|
||||
LocationPath nvarchar(max) NOT NULL
|
||||
);
|
||||
|
||||
;WITH ProjectLocationTree AS
|
||||
(
|
||||
SELECT LocationID, LocationName, CAST(LocationName AS nvarchar(max)) AS LocationPath
|
||||
FROM dbo.Locations
|
||||
WHERE ProjectID = @ProjectID AND ParentLocationID IS NULL AND IsArchived = 0
|
||||
|
||||
UNION ALL
|
||||
|
||||
SELECT child.LocationID, child.LocationName, CAST(parent.LocationPath + N' > ' + child.LocationName AS nvarchar(max)) AS LocationPath
|
||||
FROM dbo.Locations child
|
||||
INNER JOIN ProjectLocationTree parent ON parent.LocationID = child.ParentLocationID
|
||||
WHERE child.ProjectID = @ProjectID AND child.IsArchived = 0
|
||||
)
|
||||
INSERT #ProjectLocationPaths (LocationID, LocationName, LocationPath)
|
||||
SELECT LocationID, LocationName, LocationPath
|
||||
FROM ProjectLocationTree;
|
||||
|
||||
SELECT sa.StoryAssetID, sa.ProjectID, sa.AssetName, sa.AssetKindID, ak.KindName, sa.Description, sa.Importance,
|
||||
sa.CurrentStateID, ast.StateName AS CurrentStateName, sa.CurrentLocationID,
|
||||
location.LocationName AS CurrentLocationName, location.LocationPath AS CurrentLocationPath,
|
||||
sa.IsResolved, sa.ShowInQuickAddBar, sa.ImagePath, sa.ThumbnailPath, sa.CreatedDate, sa.UpdatedDate, sa.IsArchived
|
||||
FROM dbo.StoryAssets sa
|
||||
INNER JOIN dbo.AssetKinds ak ON ak.AssetKindID = sa.AssetKindID
|
||||
LEFT JOIN dbo.AssetStates ast ON ast.AssetStateID = sa.CurrentStateID
|
||||
LEFT JOIN #ProjectLocationPaths location ON location.LocationID = sa.CurrentLocationID
|
||||
WHERE sa.ProjectID = @ProjectID
|
||||
AND sa.IsArchived = 0
|
||||
ORDER BY sa.Importance DESC, sa.AssetName;
|
||||
END;
|
||||
GO
|
||||
|
||||
CREATE OR ALTER PROCEDURE dbo.AssetLookup_All
|
||||
@ProjectID int = NULL
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
|
||||
SELECT AssetKindID, KindName, SortOrder, IsActive FROM dbo.AssetKinds WHERE IsActive = 1 ORDER BY SortOrder;
|
||||
SELECT AssetStateID, ProjectID, AssetKindID, StateName, SortOrder, Description, IsResolvedState, IsActive
|
||||
FROM dbo.AssetStates
|
||||
WHERE IsActive = 1
|
||||
AND (@ProjectID IS NULL OR ProjectID IS NULL OR ProjectID = @ProjectID)
|
||||
ORDER BY SortOrder;
|
||||
SELECT AssetEventTypeID, TypeName, MarkerText, SortOrder, IsActive FROM dbo.AssetEventTypes WHERE IsActive = 1 ORDER BY SortOrder;
|
||||
SELECT AssetDependencyTypeID, TypeName, SortOrder, IsValidatingDependency, IsActive FROM dbo.AssetDependencyTypes WHERE IsActive = 1 ORDER BY SortOrder;
|
||||
SELECT AssetCustodyEventTypeID, TypeName, SortOrder, IsActive FROM dbo.AssetCustodyEventTypes WHERE IsActive = 1 ORDER BY SortOrder;
|
||||
SELECT CustodyRoleID, RoleName, SortOrder, IsActive FROM dbo.CustodyRoles WHERE IsActive = 1 ORDER BY SortOrder;
|
||||
END;
|
||||
GO
|
||||
|
||||
CREATE OR ALTER PROCEDURE dbo.CharacterLookup_All
|
||||
@ProjectID int = NULL
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
|
||||
SELECT CharacterRoleInSceneTypeID, TypeName, SortOrder, IsActive FROM dbo.CharacterRoleInSceneTypes WHERE IsActive = 1 ORDER BY SortOrder;
|
||||
SELECT PresenceTypeID, TypeName, SortOrder, IsActive FROM dbo.PresenceTypes WHERE IsActive = 1 ORDER BY SortOrder;
|
||||
SELECT CharacterAttributeTypeID, ProjectID, AttributeName, DataType, IsNormallyStable, SortOrder, IsActive
|
||||
FROM dbo.CharacterAttributeTypes
|
||||
WHERE IsActive = 1
|
||||
AND (@ProjectID IS NULL OR ProjectID IS NULL OR ProjectID = @ProjectID)
|
||||
ORDER BY SortOrder;
|
||||
SELECT KnowledgeStateID, StateName, SortOrder, IsActive FROM dbo.KnowledgeStates WHERE IsActive = 1 ORDER BY SortOrder;
|
||||
SELECT rt.RelationshipTypeID, rt.TypeName, rt.IsPermanentDefault, rt.SortOrder, rt.IsActive,
|
||||
rt.RelationshipCategoryID, rc.CategoryName AS RelationshipCategoryName, rc.SortOrder AS CategorySortOrder
|
||||
FROM dbo.RelationshipTypes rt
|
||||
LEFT JOIN dbo.RelationshipCategories rc ON rc.RelationshipCategoryID = rt.RelationshipCategoryID
|
||||
WHERE rt.IsActive = 1
|
||||
ORDER BY COALESCE(rc.SortOrder, 900), rt.SortOrder, rt.TypeName;
|
||||
SELECT RelationshipStateID, StateName, SortOrder, IsActive FROM dbo.RelationshipStates WHERE IsActive = 1 ORDER BY SortOrder;
|
||||
SELECT RelationshipCategoryID, CategoryName, SortOrder, IsActive FROM dbo.RelationshipCategories WHERE IsActive = 1 ORDER BY SortOrder, CategoryName;
|
||||
END;
|
||||
GO
|
||||
|
||||
CREATE OR ALTER PROCEDURE dbo.CharacterTimeline_GetByProject
|
||||
@ProjectID int,
|
||||
@BookID int = NULL
|
||||
WITH RECOMPILE
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
|
||||
CREATE TABLE #TimelineScenes
|
||||
(
|
||||
SceneID int NOT NULL PRIMARY KEY,
|
||||
SceneNumber decimal(9,2) NOT NULL,
|
||||
SceneTitle nvarchar(200) NOT NULL,
|
||||
ChapterNumber decimal(9,2) NOT NULL,
|
||||
BookTitle nvarchar(200) NOT NULL,
|
||||
BookSubtitle nvarchar(200) NULL,
|
||||
BookSortOrder int NOT NULL,
|
||||
ChapterSortOrder int NOT NULL,
|
||||
SceneSortOrder int NOT NULL
|
||||
);
|
||||
|
||||
INSERT #TimelineScenes (SceneID, SceneNumber, SceneTitle, ChapterNumber, BookTitle, BookSubtitle, BookSortOrder, ChapterSortOrder, SceneSortOrder)
|
||||
SELECT s.SceneID, s.SceneNumber, s.SceneTitle, ch.ChapterNumber, b.BookTitle, b.Subtitle, b.SortOrder, ch.SortOrder, s.SortOrder
|
||||
FROM dbo.Books b
|
||||
INNER JOIN dbo.Chapters ch ON ch.BookID = b.BookID
|
||||
INNER JOIN dbo.Scenes s ON s.ChapterID = ch.ChapterID
|
||||
WHERE b.ProjectID = @ProjectID
|
||||
AND b.IsArchived = 0
|
||||
AND ch.IsArchived = 0
|
||||
AND s.IsArchived = 0
|
||||
AND (@BookID IS NULL OR b.BookID = @BookID);
|
||||
|
||||
CREATE TABLE #TimelineCharacters
|
||||
(
|
||||
CharacterID int NOT NULL PRIMARY KEY,
|
||||
ProjectID int NOT NULL,
|
||||
CharacterName nvarchar(200) NOT NULL,
|
||||
ShortName nvarchar(100) NULL,
|
||||
Sex nvarchar(50) NULL,
|
||||
BirthDate date NULL,
|
||||
AgeAtSeriesStart int NULL,
|
||||
AgeReferenceSceneID int NULL,
|
||||
Height nvarchar(50) NULL,
|
||||
EyeColour nvarchar(50) NULL,
|
||||
CharacterImportance int NULL,
|
||||
ShowInQuickAddBar bit NOT NULL,
|
||||
DefaultDescription nvarchar(max) NULL,
|
||||
CreatedDate datetime2 NOT NULL,
|
||||
UpdatedDate datetime2 NOT NULL,
|
||||
IsArchived bit NOT NULL
|
||||
);
|
||||
|
||||
INSERT #TimelineCharacters (CharacterID, ProjectID, CharacterName, ShortName, Sex, BirthDate, AgeAtSeriesStart, AgeReferenceSceneID, Height, EyeColour, CharacterImportance, ShowInQuickAddBar, DefaultDescription, CreatedDate, UpdatedDate, IsArchived)
|
||||
SELECT CharacterID, ProjectID, CharacterName, ShortName, Sex, BirthDate, AgeAtSeriesStart, AgeReferenceSceneID,
|
||||
Height, EyeColour, CharacterImportance, ShowInQuickAddBar, DefaultDescription, CreatedDate, UpdatedDate, IsArchived
|
||||
FROM dbo.Characters
|
||||
WHERE ProjectID = @ProjectID AND IsArchived = 0;
|
||||
|
||||
CREATE TABLE #ProjectLocationPaths
|
||||
(
|
||||
LocationID int NOT NULL PRIMARY KEY,
|
||||
LocationName nvarchar(200) NOT NULL,
|
||||
LocationPath nvarchar(max) NOT NULL
|
||||
);
|
||||
|
||||
;WITH ProjectLocationTree AS
|
||||
(
|
||||
SELECT LocationID, LocationName, CAST(LocationName AS nvarchar(max)) AS LocationPath
|
||||
FROM dbo.Locations
|
||||
WHERE ProjectID = @ProjectID AND ParentLocationID IS NULL AND IsArchived = 0
|
||||
|
||||
UNION ALL
|
||||
|
||||
SELECT child.LocationID, child.LocationName, CAST(parent.LocationPath + N' > ' + child.LocationName AS nvarchar(max)) AS LocationPath
|
||||
FROM dbo.Locations child
|
||||
INNER JOIN ProjectLocationTree parent ON parent.LocationID = child.ParentLocationID
|
||||
WHERE child.ProjectID = @ProjectID AND child.IsArchived = 0
|
||||
)
|
||||
INSERT #ProjectLocationPaths (LocationID, LocationName, LocationPath)
|
||||
SELECT LocationID, LocationName, LocationPath
|
||||
FROM ProjectLocationTree;
|
||||
|
||||
SELECT CharacterID, ProjectID, CharacterName, ShortName, Sex, BirthDate, AgeAtSeriesStart, AgeReferenceSceneID,
|
||||
Height, EyeColour, CharacterImportance, ShowInQuickAddBar, DefaultDescription, CreatedDate, UpdatedDate, IsArchived
|
||||
FROM #TimelineCharacters
|
||||
ORDER BY ISNULL(CharacterImportance, 0) DESC, CharacterName;
|
||||
|
||||
SELECT sc.SceneCharacterID, sc.SceneID, sc.CharacterID, c.ProjectID, c.CharacterName, c.ShortName, c.BirthDate, c.AgeAtSeriesStart,
|
||||
sc.RoleInSceneTypeID, role.TypeName AS RoleInSceneTypeName, sc.PresenceTypeID, presence.TypeName AS PresenceTypeName,
|
||||
sc.LocationID, location.LocationName, location.LocationPath, sc.EntryLocationID, entry.LocationName AS EntryLocationName,
|
||||
sc.ExitLocationID, exitLocation.LocationName AS ExitLocationName, sc.AppearanceNotes, sc.OutfitDescription,
|
||||
sc.PhysicalCondition, sc.EmotionalState, sc.KnowledgeNotes, s.SceneNumber, s.SceneTitle, s.ChapterNumber, s.BookTitle, s.BookSubtitle,
|
||||
sc.CreatedDate, sc.UpdatedDate
|
||||
FROM #TimelineScenes s
|
||||
INNER JOIN dbo.SceneCharacters sc ON sc.SceneID = s.SceneID
|
||||
INNER JOIN #TimelineCharacters c ON c.CharacterID = sc.CharacterID
|
||||
LEFT JOIN dbo.CharacterRoleInSceneTypes role ON role.CharacterRoleInSceneTypeID = sc.RoleInSceneTypeID
|
||||
LEFT JOIN dbo.PresenceTypes presence ON presence.PresenceTypeID = sc.PresenceTypeID
|
||||
LEFT JOIN #ProjectLocationPaths location ON location.LocationID = sc.LocationID
|
||||
LEFT JOIN #ProjectLocationPaths entry ON entry.LocationID = sc.EntryLocationID
|
||||
LEFT JOIN #ProjectLocationPaths exitLocation ON exitLocation.LocationID = sc.ExitLocationID
|
||||
ORDER BY s.BookSortOrder, s.ChapterSortOrder, s.SceneSortOrder, c.CharacterName;
|
||||
END;
|
||||
GO
|
||||
|
||||
CREATE OR ALTER PROCEDURE dbo.Timeline_SceneOverviews
|
||||
@ProjectID int,
|
||||
@BookID int = NULL
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
|
||||
CREATE TABLE #TimelineScenes
|
||||
(
|
||||
SceneID int NOT NULL PRIMARY KEY,
|
||||
BookSortOrder int NOT NULL,
|
||||
ChapterSortOrder int NOT NULL,
|
||||
SceneSortOrder int NOT NULL
|
||||
);
|
||||
|
||||
INSERT #TimelineScenes (SceneID, BookSortOrder, ChapterSortOrder, SceneSortOrder)
|
||||
SELECT s.SceneID, b.SortOrder, c.SortOrder, s.SortOrder
|
||||
FROM dbo.Books b
|
||||
INNER JOIN dbo.Chapters c ON c.BookID = b.BookID
|
||||
INNER JOIN dbo.Scenes s ON s.ChapterID = c.ChapterID
|
||||
WHERE b.ProjectID = @ProjectID
|
||||
AND b.IsArchived = 0
|
||||
AND c.IsArchived = 0
|
||||
AND s.IsArchived = 0
|
||||
AND (@BookID IS NULL OR b.BookID = @BookID);
|
||||
|
||||
CREATE TABLE #ProjectLocationPaths
|
||||
(
|
||||
LocationID int NOT NULL PRIMARY KEY,
|
||||
LocationName nvarchar(200) NOT NULL,
|
||||
LocationPath nvarchar(max) NOT NULL
|
||||
);
|
||||
|
||||
;WITH ProjectLocationTree AS
|
||||
(
|
||||
SELECT LocationID, LocationName, CAST(LocationName AS nvarchar(max)) AS LocationPath
|
||||
FROM dbo.Locations
|
||||
WHERE ProjectID = @ProjectID AND ParentLocationID IS NULL AND IsArchived = 0
|
||||
|
||||
UNION ALL
|
||||
|
||||
SELECT child.LocationID, child.LocationName, CAST(parent.LocationPath + N' > ' + child.LocationName AS nvarchar(max)) AS LocationPath
|
||||
FROM dbo.Locations child
|
||||
INNER JOIN ProjectLocationTree parent ON parent.LocationID = child.ParentLocationID
|
||||
WHERE child.ProjectID = @ProjectID AND child.IsArchived = 0
|
||||
)
|
||||
INSERT #ProjectLocationPaths (LocationID, LocationName, LocationPath)
|
||||
SELECT LocationID, LocationName, LocationPath
|
||||
FROM ProjectLocationTree;
|
||||
|
||||
SELECT ts.SceneID,
|
||||
sc.CharacterID,
|
||||
ch.CharacterName,
|
||||
CONVERT(bit, CASE WHEN role.TypeName LIKE N'%POV%' THEN 1 ELSE 0 END) AS IsPov,
|
||||
CONVERT(int, NULL) AS StoryAssetID,
|
||||
CONVERT(nvarchar(200), NULL) AS AssetName,
|
||||
CONVERT(int, NULL) AS AssetImportance,
|
||||
CONVERT(nvarchar(100), NULL) AS AssetEventTypeName,
|
||||
location.LocationPath AS LocationName
|
||||
FROM #TimelineScenes ts
|
||||
INNER JOIN dbo.SceneCharacters sc ON sc.SceneID = ts.SceneID
|
||||
INNER JOIN dbo.Characters ch ON ch.CharacterID = sc.CharacterID AND ch.ProjectID = @ProjectID AND ch.IsArchived = 0
|
||||
LEFT JOIN dbo.CharacterRoleInSceneTypes role ON role.CharacterRoleInSceneTypeID = sc.RoleInSceneTypeID
|
||||
LEFT JOIN #ProjectLocationPaths location ON location.LocationID = sc.LocationID
|
||||
|
||||
UNION ALL
|
||||
|
||||
SELECT SceneID, CharacterID, CharacterName, IsPov, StoryAssetID, AssetName, AssetImportance, AssetEventTypeName, LocationName
|
||||
FROM
|
||||
(
|
||||
SELECT ts.SceneID,
|
||||
CONVERT(int, NULL) AS CharacterID,
|
||||
CONVERT(nvarchar(200), NULL) AS CharacterName,
|
||||
CONVERT(bit, 0) AS IsPov,
|
||||
sa.StoryAssetID,
|
||||
sa.AssetName,
|
||||
sa.Importance AS AssetImportance,
|
||||
aet.TypeName AS AssetEventTypeName,
|
||||
CONVERT(nvarchar(max), NULL) AS LocationName,
|
||||
ROW_NUMBER() OVER (PARTITION BY ts.SceneID, sa.StoryAssetID ORDER BY ae.UpdatedDate DESC, ae.AssetEventID DESC) AS rn
|
||||
FROM #TimelineScenes ts
|
||||
INNER JOIN dbo.AssetEvents ae ON ae.SceneID = ts.SceneID
|
||||
INNER JOIN dbo.StoryAssets sa ON sa.StoryAssetID = ae.StoryAssetID AND sa.ProjectID = @ProjectID AND sa.IsArchived = 0
|
||||
INNER JOIN dbo.AssetEventTypes aet ON aet.AssetEventTypeID = ae.AssetEventTypeID
|
||||
) assetRows
|
||||
WHERE rn = 1
|
||||
ORDER BY SceneID;
|
||||
END;
|
||||
GO
|
||||
Loading…
x
Reference in New Issue
Block a user