SET ANSI_NULLS ON; GO SET QUOTED_IDENTIFIER ON; GO CREATE OR ALTER VIEW dbo.NarrativeSceneOrder AS SELECT p.ProjectID, b.BookID, c.ChapterID, s.SceneID, b.SortOrder AS BookSortOrder, c.SortOrder AS ChapterSortOrder, s.SortOrder AS SceneSortOrder, ROW_NUMBER() OVER (PARTITION BY p.ProjectID ORDER BY b.SortOrder, b.BookNumber, c.SortOrder, c.ChapterNumber, s.SortOrder, s.SceneNumber, s.SceneID) AS GlobalSceneIndex, ROW_NUMBER() OVER (PARTITION BY b.BookID ORDER BY c.SortOrder, c.ChapterNumber, s.SortOrder, s.SceneNumber, s.SceneID) AS BookSceneIndex, b.BookTitle, b.Subtitle AS BookSubtitle, b.BookNumber, c.ChapterNumber, c.ChapterTitle, s.SceneNumber, s.SceneTitle, s.PrimaryLocationID, s.StartDateTime, s.RelativeTimeText, s.ScenePurposeNotes, s.SceneOutcomeNotes FROM dbo.Projects p INNER JOIN dbo.Books b ON b.ProjectID = p.ProjectID AND b.IsArchived = 0 INNER JOIN dbo.Chapters c ON c.BookID = b.BookID AND c.IsArchived = 0 INNER JOIN dbo.Scenes s ON s.ChapterID = c.ChapterID AND s.IsArchived = 0 WHERE p.IsArchived = 0; GO CREATE OR ALTER PROCEDURE dbo.SceneOption_ListByProject @ProjectID int AS BEGIN SET NOCOUNT ON; SELECT s.SceneID, b.BookTitle, b.Subtitle AS BookSubtitle, c.ChapterNumber, c.ChapterTitle, s.SceneNumber, s.SceneTitle FROM dbo.Scenes s INNER JOIN dbo.Chapters c ON c.ChapterID = s.ChapterID INNER JOIN dbo.Books b ON b.BookID = c.BookID WHERE b.ProjectID = @ProjectID AND b.IsArchived = 0 AND c.IsArchived = 0 AND s.IsArchived = 0 ORDER BY b.SortOrder, b.BookNumber, c.SortOrder, c.ChapterNumber, s.SortOrder, s.SceneNumber; END; GO CREATE OR ALTER PROCEDURE dbo.ChapterOption_ListByProject @ProjectID int AS BEGIN SET NOCOUNT ON; SELECT c.ChapterID, b.BookID, b.BookTitle, b.Subtitle AS BookSubtitle, b.BookNumber, c.ChapterNumber, c.ChapterTitle, c.SortOrder FROM dbo.Chapters c INNER JOIN dbo.Books b ON b.BookID = c.BookID WHERE b.ProjectID = @ProjectID AND b.IsArchived = 0 AND c.IsArchived = 0 ORDER BY b.SortOrder, b.BookNumber, c.SortOrder, c.ChapterNumber; END; GO CREATE OR ALTER PROCEDURE dbo.SceneDependency_ListByScene @SceneID int AS BEGIN SET NOCOUNT ON; SELECT sd.SceneDependencyID, sd.ProjectID, sd.SourceSceneID, sd.TargetSceneID, sd.SceneDependencyTypeID, sdt.TypeName AS SceneDependencyTypeName, sd.Description, sd.IsHardDependency, sd.CreatedDate, sd.UpdatedDate, sd.IsArchived, sourceOrder.BookID AS SourceBookID, sourceOrder.BookTitle AS SourceBookTitle, sourceOrder.BookSubtitle AS SourceBookSubtitle, sourceOrder.ChapterID AS SourceChapterID, sourceOrder.ChapterNumber AS SourceChapterNumber, sourceOrder.SceneNumber AS SourceSceneNumber, sourceOrder.SceneTitle AS SourceSceneTitle, targetOrder.BookID AS TargetBookID, targetOrder.BookTitle AS TargetBookTitle, targetOrder.BookSubtitle AS TargetBookSubtitle, targetOrder.ChapterID AS TargetChapterID, targetOrder.ChapterNumber AS TargetChapterNumber, targetOrder.SceneNumber AS TargetSceneNumber, targetOrder.SceneTitle AS TargetSceneTitle, CASE WHEN sd.TargetSceneID = @SceneID THEN N'DependsOn' ELSE N'Dependent' END AS DependencyDirection FROM dbo.SceneDependencies sd INNER JOIN dbo.SceneDependencyTypes sdt ON sdt.SceneDependencyTypeID = sd.SceneDependencyTypeID INNER JOIN dbo.NarrativeSceneOrder sourceOrder ON sourceOrder.SceneID = sd.SourceSceneID INNER JOIN dbo.NarrativeSceneOrder targetOrder ON targetOrder.SceneID = sd.TargetSceneID WHERE sd.IsArchived = 0 AND @SceneID IN (sd.SourceSceneID, sd.TargetSceneID) ORDER BY DependencyDirection, sourceOrder.GlobalSceneIndex, targetOrder.GlobalSceneIndex; END; GO CREATE OR ALTER PROCEDURE dbo.SceneDependency_ListByProject @ProjectID int, @BookID int = NULL AS BEGIN SET NOCOUNT ON; SELECT sd.SceneDependencyID, sd.ProjectID, sd.SourceSceneID, sd.TargetSceneID, sd.SceneDependencyTypeID, sdt.TypeName AS SceneDependencyTypeName, sd.Description, sd.IsHardDependency, sd.CreatedDate, sd.UpdatedDate, sd.IsArchived, sourceOrder.BookID AS SourceBookID, sourceOrder.BookTitle AS SourceBookTitle, sourceOrder.BookSubtitle AS SourceBookSubtitle, sourceOrder.ChapterID AS SourceChapterID, sourceOrder.ChapterNumber AS SourceChapterNumber, sourceOrder.SceneNumber AS SourceSceneNumber, sourceOrder.SceneTitle AS SourceSceneTitle, targetOrder.BookID AS TargetBookID, targetOrder.BookTitle AS TargetBookTitle, targetOrder.BookSubtitle AS TargetBookSubtitle, targetOrder.ChapterID AS TargetChapterID, targetOrder.ChapterNumber AS TargetChapterNumber, targetOrder.SceneNumber AS TargetSceneNumber, targetOrder.SceneTitle AS TargetSceneTitle FROM dbo.SceneDependencies sd INNER JOIN dbo.SceneDependencyTypes sdt ON sdt.SceneDependencyTypeID = sd.SceneDependencyTypeID INNER JOIN dbo.NarrativeSceneOrder sourceOrder ON sourceOrder.SceneID = sd.SourceSceneID INNER JOIN dbo.NarrativeSceneOrder targetOrder ON targetOrder.SceneID = sd.TargetSceneID WHERE sd.IsArchived = 0 AND sd.ProjectID = @ProjectID AND (@BookID IS NULL OR targetOrder.BookID = @BookID OR sourceOrder.BookID = @BookID) ORDER BY sourceOrder.GlobalSceneIndex, targetOrder.GlobalSceneIndex; END; GO CREATE OR ALTER PROCEDURE dbo.SceneMovePreview_Get @SceneID int, @TargetChapterID int, @Position nvarchar(20) = N'End' AS BEGIN SET NOCOUNT ON; SELECT s.SceneID, s.SceneNumber, s.SceneTitle, s.ChapterID AS SourceChapterID, sourceChapter.ChapterNumber AS SourceChapterNumber, sourceChapter.ChapterTitle AS SourceChapterTitle, sourceBook.BookID AS SourceBookID, sourceBook.BookTitle AS SourceBookTitle, sourceBook.Subtitle AS SourceBookSubtitle, @TargetChapterID AS TargetChapterID, targetChapter.ChapterNumber AS TargetChapterNumber, targetChapter.ChapterTitle AS TargetChapterTitle, targetBook.BookID AS TargetBookID, targetBook.BookTitle AS TargetBookTitle, targetBook.Subtitle AS TargetBookSubtitle, @Position AS Position, (SELECT COUNT(*) FROM dbo.SceneDependencies WHERE IsArchived = 0 AND @SceneID IN (SourceSceneID, TargetSceneID)) AS DependencyCount, (SELECT COUNT(*) FROM dbo.ContinuityWarnings WHERE IsDismissed = 0 AND IsIntentional = 0 AND SceneID = @SceneID) AS ActiveWarningCount FROM dbo.Scenes s INNER JOIN dbo.Chapters sourceChapter ON sourceChapter.ChapterID = s.ChapterID INNER JOIN dbo.Books sourceBook ON sourceBook.BookID = sourceChapter.BookID INNER JOIN dbo.Chapters targetChapter ON targetChapter.ChapterID = @TargetChapterID INNER JOIN dbo.Books targetBook ON targetBook.BookID = targetChapter.BookID WHERE s.SceneID = @SceneID; 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; 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; END; GO CREATE OR ALTER PROCEDURE dbo.CharacterTimeline_GetByProject @ProjectID int, @BookID int = NULL 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.CharacterAttributeEvent_ListByCharacter @CharacterID int AS BEGIN SET NOCOUNT ON; SELECT cae.CharacterAttributeEventID, cae.CharacterID, ch.CharacterName, cae.CharacterAttributeTypeID, cat.AttributeName, cae.SceneID, s.SceneNumber, s.SceneTitle, c.ChapterNumber, b.BookTitle, b.Subtitle AS BookSubtitle, cae.AttributeValue, cae.Description, cae.CreatedDate, cae.UpdatedDate FROM dbo.CharacterAttributeEvents cae INNER JOIN dbo.Characters ch ON ch.CharacterID = cae.CharacterID INNER JOIN dbo.CharacterAttributeTypes cat ON cat.CharacterAttributeTypeID = cae.CharacterAttributeTypeID INNER JOIN dbo.Scenes s ON s.SceneID = cae.SceneID INNER JOIN dbo.Chapters c ON c.ChapterID = s.ChapterID INNER JOIN dbo.Books b ON b.BookID = c.BookID WHERE cae.CharacterID = @CharacterID ORDER BY b.SortOrder, c.SortOrder, s.SortOrder, cat.SortOrder; END; GO CREATE OR ALTER PROCEDURE dbo.CharacterKnowledge_ListByCharacter @CharacterID int AS BEGIN SET NOCOUNT ON; SELECT ck.CharacterKnowledgeID, ck.CharacterID, ch.CharacterName, ck.StoryAssetID, sa.AssetName, ck.PlotThreadID, pt.ThreadTitle, ck.SceneID, s.SceneNumber, s.SceneTitle, c.ChapterNumber, b.BookTitle, b.Subtitle AS BookSubtitle, ck.KnowledgeStateID, ks.StateName AS KnowledgeStateName, ck.SourceEventID, ck.Description, ck.CreatedDate, ck.UpdatedDate FROM dbo.CharacterKnowledge ck INNER JOIN dbo.Characters ch ON ch.CharacterID = ck.CharacterID INNER JOIN dbo.KnowledgeStates ks ON ks.KnowledgeStateID = ck.KnowledgeStateID INNER JOIN dbo.Scenes s ON s.SceneID = ck.SceneID INNER JOIN dbo.Chapters c ON c.ChapterID = s.ChapterID INNER JOIN dbo.Books b ON b.BookID = c.BookID LEFT JOIN dbo.StoryAssets sa ON sa.StoryAssetID = ck.StoryAssetID LEFT JOIN dbo.PlotThreads pt ON pt.PlotThreadID = ck.PlotThreadID WHERE ck.CharacterID = @CharacterID ORDER BY b.SortOrder, c.SortOrder, s.SortOrder, ck.CharacterKnowledgeID; END; GO CREATE OR ALTER PROCEDURE dbo.Relationship_ListInitialByCharacter @CharacterID int AS BEGIN SET NOCOUNT ON; SELECT cr.CharacterRelationshipID, cr.ProjectID, cr.CharacterAID, ca.CharacterName AS CharacterAName, cr.CharacterBID, cb.CharacterName AS CharacterBName, cr.RelationshipTypeID, rt.TypeName AS RelationshipTypeName, rt.RelationshipCategoryID, rc.CategoryName AS RelationshipCategoryName, COALESCE(rc.SortOrder, 900) AS CategorySortOrder, rt.SortOrder AS RelationshipTypeSortOrder, cr.IsPermanent, cr.StartSceneID, cr.EndSceneID, cr.IsKnownToReader, cr.Notes, cr.IsInitialRelationship, cr.InitialBookID, b.BookTitle AS InitialBookTitle, b.Subtitle AS InitialBookSubtitle, cr.ReaderInitiallyKnows, cr.InitialRelationshipStateID, rs.StateName AS InitialRelationshipStateName, cr.InitialIntensity, cr.IsReciprocal, cr.CreatedDate, cr.UpdatedDate, cr.IsArchived FROM dbo.CharacterRelationships cr INNER JOIN dbo.Characters ca ON ca.CharacterID = cr.CharacterAID INNER JOIN dbo.Characters cb ON cb.CharacterID = cr.CharacterBID INNER JOIN dbo.RelationshipTypes rt ON rt.RelationshipTypeID = cr.RelationshipTypeID LEFT JOIN dbo.RelationshipCategories rc ON rc.RelationshipCategoryID = rt.RelationshipCategoryID LEFT JOIN dbo.Books b ON b.BookID = cr.InitialBookID LEFT JOIN dbo.RelationshipStates rs ON rs.RelationshipStateID = cr.InitialRelationshipStateID WHERE cr.IsArchived = 0 AND cr.IsInitialRelationship = 1 AND @CharacterID IN (cr.CharacterAID, cr.CharacterBID) ORDER BY COALESCE(rc.SortOrder, 900), COALESCE(b.SortOrder, 0), rt.SortOrder, ca.CharacterName, cb.CharacterName; END; GO CREATE OR ALTER PROCEDURE dbo.Relationship_ListByCharacter @CharacterID int AS BEGIN SET NOCOUNT ON; SELECT cr.CharacterRelationshipID, cr.ProjectID, cr.CharacterAID, ca.CharacterName AS CharacterAName, cr.CharacterBID, cb.CharacterName AS CharacterBName, cr.RelationshipTypeID, rt.TypeName AS RelationshipTypeName, rt.RelationshipCategoryID, rc.CategoryName AS RelationshipCategoryName, COALESCE(rc.SortOrder, 900) AS CategorySortOrder, rt.SortOrder AS RelationshipTypeSortOrder, cr.IsPermanent, cr.StartSceneID, cr.EndSceneID, cr.IsKnownToReader, cr.Notes, cr.IsInitialRelationship, cr.InitialBookID, b.BookTitle AS InitialBookTitle, b.Subtitle AS InitialBookSubtitle, cr.ReaderInitiallyKnows, cr.InitialRelationshipStateID, rs.StateName AS InitialRelationshipStateName, cr.InitialIntensity, cr.IsReciprocal, cr.CreatedDate, cr.UpdatedDate, cr.IsArchived FROM dbo.CharacterRelationships cr INNER JOIN dbo.Characters ca ON ca.CharacterID = cr.CharacterAID INNER JOIN dbo.Characters cb ON cb.CharacterID = cr.CharacterBID INNER JOIN dbo.RelationshipTypes rt ON rt.RelationshipTypeID = cr.RelationshipTypeID LEFT JOIN dbo.RelationshipCategories rc ON rc.RelationshipCategoryID = rt.RelationshipCategoryID LEFT JOIN dbo.Books b ON b.BookID = cr.InitialBookID LEFT JOIN dbo.RelationshipStates rs ON rs.RelationshipStateID = cr.InitialRelationshipStateID WHERE cr.IsArchived = 0 AND @CharacterID IN (cr.CharacterAID, cr.CharacterBID) ORDER BY COALESCE(rc.SortOrder, 900), ca.CharacterName, cb.CharacterName, rt.SortOrder; END; GO CREATE OR ALTER PROCEDURE dbo.Relationship_ListByProject @ProjectID int AS BEGIN SET NOCOUNT ON; SELECT cr.CharacterRelationshipID, cr.ProjectID, cr.CharacterAID, ca.CharacterName AS CharacterAName, cr.CharacterBID, cb.CharacterName AS CharacterBName, cr.RelationshipTypeID, rt.TypeName AS RelationshipTypeName, rt.RelationshipCategoryID, rc.CategoryName AS RelationshipCategoryName, COALESCE(rc.SortOrder, 900) AS CategorySortOrder, rt.SortOrder AS RelationshipTypeSortOrder, cr.IsPermanent, cr.StartSceneID, cr.EndSceneID, cr.IsKnownToReader, cr.Notes, cr.IsInitialRelationship, cr.InitialBookID, b.BookTitle AS InitialBookTitle, b.Subtitle AS InitialBookSubtitle, cr.ReaderInitiallyKnows, cr.InitialRelationshipStateID, rs.StateName AS InitialRelationshipStateName, cr.InitialIntensity, cr.IsReciprocal, cr.CreatedDate, cr.UpdatedDate, cr.IsArchived FROM dbo.CharacterRelationships cr INNER JOIN dbo.Characters ca ON ca.CharacterID = cr.CharacterAID INNER JOIN dbo.Characters cb ON cb.CharacterID = cr.CharacterBID INNER JOIN dbo.RelationshipTypes rt ON rt.RelationshipTypeID = cr.RelationshipTypeID LEFT JOIN dbo.RelationshipCategories rc ON rc.RelationshipCategoryID = rt.RelationshipCategoryID LEFT JOIN dbo.Books b ON b.BookID = cr.InitialBookID LEFT JOIN dbo.RelationshipStates rs ON rs.RelationshipStateID = cr.InitialRelationshipStateID WHERE cr.ProjectID = @ProjectID AND cr.IsArchived = 0 ORDER BY COALESCE(rc.SortOrder, 900), ca.CharacterName, cb.CharacterName, rt.SortOrder; END; GO CREATE OR ALTER PROCEDURE dbo.RelationshipEvent_ListByCharacter @CharacterID int AS BEGIN SET NOCOUNT ON; SELECT re.RelationshipEventID, re.CharacterRelationshipID, cr.ProjectID, cr.CharacterAID, ca.CharacterName AS CharacterAName, cr.CharacterBID, cb.CharacterName AS CharacterBName, rt.TypeName AS RelationshipTypeName, rt.RelationshipCategoryID, rc.CategoryName AS RelationshipCategoryName, COALESCE(rc.SortOrder, 900) AS CategorySortOrder, rt.SortOrder AS RelationshipTypeSortOrder, cr.IsReciprocal, re.SceneID, s.SceneNumber, s.SceneTitle, ch.ChapterNumber, b.BookTitle, b.Subtitle AS BookSubtitle, re.RelationshipStateID, rs.StateName AS RelationshipStateName, re.Intensity, re.IsKnownToOtherCharacter, re.Description, re.CreatedDate, re.UpdatedDate FROM dbo.RelationshipEvents re INNER JOIN dbo.CharacterRelationships cr ON cr.CharacterRelationshipID = re.CharacterRelationshipID INNER JOIN dbo.Characters ca ON ca.CharacterID = cr.CharacterAID INNER JOIN dbo.Characters cb ON cb.CharacterID = cr.CharacterBID INNER JOIN dbo.RelationshipTypes rt ON rt.RelationshipTypeID = cr.RelationshipTypeID LEFT JOIN dbo.RelationshipCategories rc ON rc.RelationshipCategoryID = rt.RelationshipCategoryID INNER JOIN dbo.RelationshipStates rs ON rs.RelationshipStateID = re.RelationshipStateID INNER JOIN dbo.Scenes s ON s.SceneID = re.SceneID INNER JOIN dbo.Chapters ch ON ch.ChapterID = s.ChapterID INNER JOIN dbo.Books b ON b.BookID = ch.BookID WHERE @CharacterID IN (cr.CharacterAID, cr.CharacterBID) ORDER BY b.SortOrder, ch.SortOrder, s.SortOrder, re.CreatedDate, re.RelationshipEventID; END; GO CREATE OR ALTER PROCEDURE dbo.AssetEvent_ListByAsset @StoryAssetID int AS BEGIN SET NOCOUNT ON; SELECT ae.AssetEventID, ae.StoryAssetID, sa.ProjectID, sa.AssetName, sa.AssetKindID, ak.KindName, ae.SceneID, ae.AssetEventTypeID, aet.TypeName AS AssetEventTypeName, aet.MarkerText, ae.FromStateID, fs.StateName AS FromStateName, ae.ToStateID, ts.StateName AS ToStateName, ae.EventTitle, ae.EventDescription, ae.CreatedDate, ae.UpdatedDate, b.BookTitle, b.Subtitle AS BookSubtitle, c.ChapterNumber, s.SceneNumber, s.SceneTitle FROM dbo.AssetEvents ae INNER JOIN dbo.StoryAssets sa ON sa.StoryAssetID = ae.StoryAssetID INNER JOIN dbo.AssetKinds ak ON ak.AssetKindID = sa.AssetKindID INNER JOIN dbo.AssetEventTypes aet ON aet.AssetEventTypeID = ae.AssetEventTypeID INNER JOIN dbo.Scenes s ON s.SceneID = ae.SceneID INNER JOIN dbo.Chapters c ON c.ChapterID = s.ChapterID INNER JOIN dbo.Books b ON b.BookID = c.BookID LEFT JOIN dbo.AssetStates fs ON fs.AssetStateID = ae.FromStateID LEFT JOIN dbo.AssetStates ts ON ts.AssetStateID = ae.ToStateID WHERE ae.StoryAssetID = @StoryAssetID ORDER BY b.SortOrder, c.SortOrder, s.SortOrder, ae.AssetEventID; END; GO CREATE OR ALTER PROCEDURE dbo.AssetCustodyEvent_ListByAsset @StoryAssetID int AS BEGIN SET NOCOUNT ON; SELECT ace.AssetCustodyEventID, ace.StoryAssetID, sa.AssetName, ace.SceneID, ace.AssetCustodyEventTypeID, acet.TypeName AS CustodyEventTypeName, ace.Description, STRING_AGG(CONCAT(aec.CharacterNameText, N' (', cr.RoleName, N')'), N', ') AS CustodianSummary, ace.CreatedDate, ace.UpdatedDate, b.BookTitle, b.Subtitle AS BookSubtitle, c.ChapterNumber, s.SceneNumber, s.SceneTitle FROM dbo.AssetCustodyEvents ace INNER JOIN dbo.StoryAssets sa ON sa.StoryAssetID = ace.StoryAssetID INNER JOIN dbo.AssetCustodyEventTypes acet ON acet.AssetCustodyEventTypeID = ace.AssetCustodyEventTypeID INNER JOIN dbo.Scenes s ON s.SceneID = ace.SceneID INNER JOIN dbo.Chapters c ON c.ChapterID = s.ChapterID INNER JOIN dbo.Books b ON b.BookID = c.BookID LEFT JOIN dbo.AssetCustodyEventCharacters aec ON aec.AssetCustodyEventID = ace.AssetCustodyEventID LEFT JOIN dbo.CustodyRoles cr ON cr.CustodyRoleID = aec.CustodyRoleID WHERE ace.StoryAssetID = @StoryAssetID GROUP BY ace.AssetCustodyEventID, ace.StoryAssetID, sa.AssetName, ace.SceneID, ace.AssetCustodyEventTypeID, acet.TypeName, ace.Description, ace.CreatedDate, ace.UpdatedDate, b.BookTitle, b.Subtitle, c.ChapterNumber, s.SceneNumber, s.SceneTitle, b.SortOrder, c.SortOrder, s.SortOrder ORDER BY b.SortOrder, c.SortOrder, s.SortOrder, ace.AssetCustodyEventID; END; GO CREATE OR ALTER PROCEDURE dbo.WriterDashboard_Scenes @ProjectID int = NULL, @Queue nvarchar(50) AS BEGIN SET NOCOUNT ON; WITH SceneBase AS ( SELECT p.ProjectID, p.ProjectName, b.BookID, b.BookTitle, b.Subtitle AS BookSubtitle, c.ChapterID, c.ChapterNumber, c.ChapterTitle, s.SceneID, s.SceneNumber, s.SceneTitle, s.Summary, s.RevisionStatusID, rs.StatusName AS RevisionStatusName, COALESCE(sw.DraftStatus, rs.StatusName, N'Planned') AS DraftStatus, sw.Priority, COALESCE(sw.IsBlocked, 0) AS IsBlocked, sw.BlockedReason, COALESCE(sw.ReadyForDraft, 0) AS ReadyForDraft, COALESCE(sw.ReadyForRevision, 0) AS ReadyForRevision, COALESCE(sw.ReadyForPolish, 0) AS ReadyForPolish, b.SortOrder AS BookSortOrder, c.SortOrder AS ChapterSortOrder, s.SortOrder AS SceneSortOrder FROM dbo.Projects p INNER JOIN dbo.Books b ON b.ProjectID = p.ProjectID AND b.IsArchived = 0 INNER JOIN dbo.Chapters c ON c.BookID = b.BookID AND c.IsArchived = 0 INNER JOIN dbo.Scenes s ON s.ChapterID = c.ChapterID AND s.IsArchived = 0 INNER JOIN dbo.RevisionStatuses rs ON rs.RevisionStatusID = s.RevisionStatusID LEFT JOIN dbo.SceneWorkflow sw ON sw.SceneID = s.SceneID WHERE p.IsArchived = 0 AND (@ProjectID IS NULL OR p.ProjectID = @ProjectID) ) SELECT TOP (40) sb.ProjectID, sb.ProjectName, sb.BookID, sb.BookTitle, sb.BookSubtitle, sb.ChapterID, sb.ChapterNumber, sb.ChapterTitle, sb.SceneID, sb.SceneNumber, sb.SceneTitle, sb.Summary, sb.RevisionStatusID, sb.RevisionStatusName, sb.DraftStatus, sb.Priority, sb.IsBlocked, sb.BlockedReason, ISNULL(warnings.WarningCount, 0) AS WarningCount, ISNULL(checklist.RemainingChecklistCount, 0) AS RemainingChecklistCount, ISNULL(notes.UnresolvedNoteCount, 0) AS UnresolvedNoteCount, threads.ImportantThreads, pinned.PinnedNotes FROM SceneBase sb OUTER APPLY ( SELECT COUNT(*) AS WarningCount FROM dbo.ContinuityWarnings cw WHERE cw.SceneID = sb.SceneID AND cw.IsDismissed = 0 ) warnings OUTER APPLY ( SELECT COUNT(*) AS RemainingChecklistCount FROM dbo.SceneChecklistItems sci WHERE sci.SceneID = sb.SceneID AND sci.IsCompleted = 0 ) checklist OUTER APPLY ( SELECT COUNT(*) AS UnresolvedNoteCount FROM dbo.SceneNotes sn WHERE sn.SceneID = sb.SceneID AND sn.IsResolved = 0 ) notes OUTER APPLY ( SELECT STRING_AGG(CONVERT(nvarchar(max), pt.ThreadTitle), N', ') AS ImportantThreads FROM dbo.ThreadEvents te INNER JOIN dbo.PlotThreads pt ON pt.PlotThreadID = te.PlotThreadID WHERE te.SceneID = sb.SceneID AND pt.Importance >= 8 AND pt.IsArchived = 0 ) threads OUTER APPLY ( SELECT STRING_AGG(CONVERT(nvarchar(max), COALESCE(sn.NoteTitle, sn.NoteText)), N' | ') AS PinnedNotes FROM dbo.SceneNotes sn WHERE sn.SceneID = sb.SceneID AND sn.IsPinned = 1 AND sn.IsResolved = 0 ) pinned WHERE (@Queue = N'Blocked' AND sb.IsBlocked = 1) OR (@Queue = N'ReadyForDraft' AND sb.ReadyForDraft = 1) OR (@Queue = N'ReadyForRevision' AND sb.ReadyForRevision = 1) OR (@Queue = N'ReadyForPolish' AND sb.ReadyForPolish = 1) OR (@Queue = N'Warnings' AND ISNULL(warnings.WarningCount, 0) > 0) ORDER BY CASE WHEN sb.IsBlocked = 1 THEN 0 ELSE 1 END, ISNULL(sb.Priority, 0) DESC, sb.BookSortOrder, sb.ChapterSortOrder, sb.SceneSortOrder, sb.SceneID; END; GO CREATE OR ALTER PROCEDURE dbo.Dynamics_CharacterArc @CharacterID int AS BEGIN SET NOCOUNT ON; DECLARE @ProjectID int = (SELECT ProjectID FROM dbo.Characters WHERE CharacterID = @CharacterID); SELECT CharacterID, ProjectID, CharacterName, ShortName, Sex, BirthDate, AgeAtSeriesStart, AgeReferenceSceneID, Height, EyeColour, DefaultDescription, CreatedDate, UpdatedDate, IsArchived FROM dbo.Characters WHERE CharacterID = @CharacterID; SELECT sc.SceneID, nso.BookTitle, nso.BookSubtitle, nso.ChapterNumber, nso.SceneNumber, nso.SceneTitle, nso.GlobalSceneIndex, CAST(CASE WHEN COALESCE(s.POVCharacterID, c.POVCharacterID) = @CharacterID THEN 1 ELSE 0 END AS bit) AS IsPov, MAX(CASE WHEN smt.MetricName = N'Emotional Weight' THEN smv.Value END) AS EmotionalWeight, MAX(CASE WHEN smt.MetricName = N'Tension' THEN smv.Value END) AS Tension, MAX(CASE WHEN smt.MetricName = N'Romance' THEN smv.Value END) AS Romance, MAX(CASE WHEN smt.MetricName = N'Action' THEN smv.Value END) AS Action, MAX(CASE WHEN smt.MetricName = N'Darkness' THEN smv.Value END) AS Darkness, MAX(CASE WHEN smt.MetricName = N'Hope / Lightness' THEN smv.Value END) AS HopeLightness, MAX(CASE WHEN smt.MetricName = N'Overall Intensity' THEN smv.Value END) AS OverallIntensity, role.TypeName AS RoleInSceneTypeName, presence.TypeName AS PresenceTypeName, sc.EmotionalState, sc.AppearanceNotes, notes.KeyNotes FROM dbo.SceneCharacters sc INNER JOIN dbo.NarrativeSceneOrder nso ON nso.SceneID = sc.SceneID INNER JOIN dbo.Scenes s ON s.SceneID = sc.SceneID INNER JOIN dbo.Chapters c ON c.ChapterID = s.ChapterID LEFT JOIN dbo.CharacterRoleInSceneTypes role ON role.CharacterRoleInSceneTypeID = sc.RoleInSceneTypeID LEFT JOIN dbo.PresenceTypes presence ON presence.PresenceTypeID = sc.PresenceTypeID LEFT JOIN dbo.SceneMetricValues smv ON smv.SceneID = sc.SceneID LEFT JOIN dbo.SceneMetricTypes smt ON smt.MetricTypeID = smv.MetricTypeID OUTER APPLY ( SELECT STRING_AGG(CONVERT(nvarchar(max), COALESCE(sn.NoteTitle, sn.NoteText)), N' | ') AS KeyNotes FROM dbo.SceneNotes sn INNER JOIN dbo.SceneNoteTypes snt ON snt.SceneNoteTypeID = sn.SceneNoteTypeID WHERE sn.SceneID = sc.SceneID AND sn.IsResolved = 0 AND snt.TypeName IN (N'Emotional Intent', N'Character Reminder', N'Revision Note', N'TODO') ) notes WHERE sc.CharacterID = @CharacterID GROUP BY sc.SceneID, nso.BookTitle, nso.BookSubtitle, nso.ChapterNumber, nso.SceneNumber, nso.SceneTitle, nso.GlobalSceneIndex, s.POVCharacterID, c.POVCharacterID, role.TypeName, presence.TypeName, sc.EmotionalState, sc.AppearanceNotes, notes.KeyNotes ORDER BY nso.GlobalSceneIndex; EXEC dbo.RelationshipEvent_ListByCharacter @CharacterID; EXEC dbo.CharacterKnowledge_ListByCharacter @CharacterID; SELECT N'Character Presence' AS Category, ws.SeverityName, cw.Message AS Title, cw.Details AS Detail, cw.SceneID, cw.EntityID, cw.EntityType FROM dbo.ContinuityWarnings cw INNER JOIN dbo.WarningSeverities ws ON ws.WarningSeverityID = cw.WarningSeverityID INNER JOIN dbo.WarningTypes wt ON wt.WarningTypeID = cw.WarningTypeID WHERE cw.ProjectID = @ProjectID AND cw.IsDismissed = 0 AND ( (cw.EntityType = N'Character' AND cw.EntityID = @CharacterID) OR cw.SceneID IN (SELECT SceneID FROM dbo.SceneCharacters WHERE CharacterID = @CharacterID) ) AND wt.TypeName IN (N'Knowledge Continuity', N'Long Character Absence', N'Character Underused', N'Scene Character Overload'); END; GO CREATE OR ALTER PROCEDURE dbo.Dynamics_RelationshipEvolution @CharacterRelationshipID int AS BEGIN SET NOCOUNT ON; SELECT cr.CharacterRelationshipID, cr.ProjectID, cr.CharacterAID, ca.CharacterName AS CharacterAName, cr.CharacterBID, cb.CharacterName AS CharacterBName, cr.RelationshipTypeID, rt.TypeName AS RelationshipTypeName, cr.IsPermanent, cr.StartSceneID, cr.EndSceneID, cr.IsKnownToReader, cr.Notes, cr.CreatedDate, cr.UpdatedDate, cr.IsArchived FROM dbo.CharacterRelationships cr INNER JOIN dbo.Characters ca ON ca.CharacterID = cr.CharacterAID INNER JOIN dbo.Characters cb ON cb.CharacterID = cr.CharacterBID INNER JOIN dbo.RelationshipTypes rt ON rt.RelationshipTypeID = cr.RelationshipTypeID WHERE cr.CharacterRelationshipID = @CharacterRelationshipID; SELECT re.RelationshipEventID, re.CharacterRelationshipID, re.SceneID, nso.BookTitle, nso.BookSubtitle, nso.ChapterNumber, nso.SceneNumber, nso.SceneTitle, nso.GlobalSceneIndex, rs.StateName AS RelationshipStateName, re.Intensity, re.IsKnownToOtherCharacter, re.Description FROM dbo.RelationshipEvents re INNER JOIN dbo.RelationshipStates rs ON rs.RelationshipStateID = re.RelationshipStateID INNER JOIN dbo.NarrativeSceneOrder nso ON nso.SceneID = re.SceneID WHERE re.CharacterRelationshipID = @CharacterRelationshipID ORDER BY nso.GlobalSceneIndex, re.RelationshipEventID; SELECT N'Relationship Continuity' AS Category, ws.SeverityName, cw.Message AS Title, cw.Details AS Detail, cw.SceneID, cw.EntityID, cw.EntityType FROM dbo.ContinuityWarnings cw INNER JOIN dbo.WarningSeverities ws ON ws.WarningSeverityID = cw.WarningSeverityID INNER JOIN dbo.WarningTypes wt ON wt.WarningTypeID = cw.WarningTypeID WHERE cw.IsDismissed = 0 AND cw.EntityType = N'Relationship' AND cw.EntityID = @CharacterRelationshipID AND wt.TypeName = N'Abrupt Relationship Change'; END; GO CREATE OR ALTER PROCEDURE dbo.StoryBible_TimelineSummary @ProjectID int AS BEGIN SET NOCOUNT ON; SELECT b.BookID, b.BookTitle, b.Subtitle AS BookSubtitle, c.ChapterID, c.ChapterNumber, c.ChapterTitle, s.SceneID, s.SceneNumber, s.SceneTitle, s.Summary, pov.CharacterName AS PovCharacterName, location.LocationPath, rs.StatusName AS RevisionStatusName FROM dbo.Books b INNER JOIN dbo.Chapters c ON c.BookID = b.BookID AND c.IsArchived = 0 INNER JOIN dbo.Scenes s ON s.ChapterID = c.ChapterID AND s.IsArchived = 0 INNER JOIN dbo.RevisionStatuses rs ON rs.RevisionStatusID = s.RevisionStatusID LEFT JOIN dbo.Characters pov ON pov.CharacterID = COALESCE(s.POVCharacterID, c.POVCharacterID) LEFT JOIN dbo.LocationPaths location ON location.LocationID = s.PrimaryLocationID WHERE b.ProjectID = @ProjectID AND b.IsArchived = 0 ORDER BY b.SortOrder, c.SortOrder, s.SortOrder, s.SceneID; END; GO CREATE OR ALTER PROCEDURE dbo.Export_SceneBrief @SceneID int AS BEGIN SET NOCOUNT ON; SELECT p.ProjectID, p.ProjectName, b.BookID, b.BookTitle, b.Subtitle AS BookSubtitle, c.ChapterID, c.ChapterNumber, c.ChapterTitle, s.SceneID, s.SceneNumber, s.SceneTitle, s.Summary, s.ScenePurposeNotes, s.SceneOutcomeNotes, rs.StatusName AS RevisionStatusName, pov.CharacterName AS PovCharacterName, location.LocationPath, COALESCE(s.RelativeTimeText, CONVERT(nvarchar(30), s.StartDateTime, 113), N'Time not set') AS TimeLabel, COALESCE(sw.DraftStatus, N'Planned') AS DraftStatus, COALESCE(sw.IsBlocked, 0) AS IsBlocked, sw.BlockedReason, sw.Priority FROM dbo.Scenes s INNER JOIN dbo.Chapters c ON c.ChapterID = s.ChapterID INNER JOIN dbo.Books b ON b.BookID = c.BookID INNER JOIN dbo.Projects p ON p.ProjectID = b.ProjectID INNER JOIN dbo.RevisionStatuses rs ON rs.RevisionStatusID = s.RevisionStatusID LEFT JOIN dbo.Characters pov ON pov.CharacterID = COALESCE(s.POVCharacterID, c.POVCharacterID) LEFT JOIN dbo.LocationPaths location ON location.LocationID = s.PrimaryLocationID LEFT JOIN dbo.SceneWorkflow sw ON sw.SceneID = s.SceneID WHERE s.SceneID = @SceneID; SELECT N'Purpose' AS ItemType, spt.PurposeName AS Title, CAST(NULL AS nvarchar(max)) AS Detail, CAST(NULL AS nvarchar(100)) AS Status, spt.SortOrder FROM dbo.ScenePurposes sp INNER JOIN dbo.ScenePurposeTypes spt ON spt.ScenePurposeTypeID = sp.ScenePurposeTypeID WHERE sp.SceneID = @SceneID UNION ALL SELECT N'Character', ch.CharacterName, CONCAT(ISNULL(role.TypeName, N'No role'), N' / ', ISNULL(presence.TypeName, N'No presence'), N'. ', ISNULL(sc.AppearanceNotes, N'')), ISNULL(location.LocationPath, N'No location'), 100 FROM dbo.SceneCharacters sc INNER JOIN dbo.Characters ch ON ch.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 dbo.LocationPaths location ON location.LocationID = sc.LocationID WHERE sc.SceneID = @SceneID UNION ALL SELECT N'Plot Thread', pt.ThreadTitle, CONCAT(tet.TypeName, N': ', te.EventTitle, N'. ', ISNULL(te.EventDescription, N'')), CAST(pt.Importance AS nvarchar(20)), 200 FROM dbo.ThreadEvents te INNER JOIN dbo.PlotThreads pt ON pt.PlotThreadID = te.PlotThreadID INNER JOIN dbo.ThreadEventTypes tet ON tet.ThreadEventTypeID = te.EventTypeID WHERE te.SceneID = @SceneID UNION ALL SELECT N'Asset', sa.AssetName, CONCAT(aet.TypeName, N': ', ae.EventTitle, N'. ', ISNULL(ae.EventDescription, N'')), ast.StateName, 300 FROM dbo.AssetEvents ae INNER JOIN dbo.StoryAssets sa ON sa.StoryAssetID = ae.StoryAssetID INNER JOIN dbo.AssetEventTypes aet ON aet.AssetEventTypeID = ae.AssetEventTypeID LEFT JOIN dbo.AssetStates ast ON ast.AssetStateID = ae.ToStateID WHERE ae.SceneID = @SceneID UNION ALL SELECT N'Relationship', CONCAT(ca.CharacterName, N' / ', cb.CharacterName), CONCAT(rs.StateName, N'. ', ISNULL(re.Description, N'')), CAST(re.Intensity AS nvarchar(20)), 400 FROM dbo.RelationshipEvents re INNER JOIN dbo.CharacterRelationships cr ON cr.CharacterRelationshipID = re.CharacterRelationshipID INNER JOIN dbo.Characters ca ON ca.CharacterID = cr.CharacterAID INNER JOIN dbo.Characters cb ON cb.CharacterID = cr.CharacterBID INNER JOIN dbo.RelationshipStates rs ON rs.RelationshipStateID = re.RelationshipStateID WHERE re.SceneID = @SceneID UNION ALL SELECT N'Note', COALESCE(sn.NoteTitle, snt.TypeName), sn.NoteText, CASE WHEN sn.IsResolved = 1 THEN N'Resolved' ELSE N'Open' END, 500 FROM dbo.SceneNotes sn INNER JOIN dbo.SceneNoteTypes snt ON snt.SceneNoteTypeID = sn.SceneNoteTypeID WHERE sn.SceneID = @SceneID UNION ALL SELECT N'Attachment', sa.Title, CONCAT(ISNULL(sa.Notes, N''), N' ', ISNULL(sa.ExternalUrl, N''), N' ', ISNULL(sa.FilePath, N'')), sat.TypeName, 600 FROM dbo.SceneAttachments sa INNER JOIN dbo.SceneAttachmentTypes sat ON sat.SceneAttachmentTypeID = sa.SceneAttachmentTypeID WHERE sa.SceneID = @SceneID UNION ALL SELECT N'Checklist', sci.ItemText, CAST(NULL AS nvarchar(max)), CASE WHEN sci.IsCompleted = 1 THEN N'Done' ELSE N'Open' END, 700 FROM dbo.SceneChecklistItems sci WHERE sci.SceneID = @SceneID UNION ALL SELECT N'Warning', wt.TypeName, cw.Message, ws.SeverityName, 800 FROM dbo.ContinuityWarnings cw INNER JOIN dbo.WarningTypes wt ON wt.WarningTypeID = cw.WarningTypeID INNER JOIN dbo.WarningSeverities ws ON ws.WarningSeverityID = cw.WarningSeverityID WHERE cw.SceneID = @SceneID AND cw.IsDismissed = 0 ORDER BY SortOrder, ItemType, Title; END; GO CREATE OR ALTER PROCEDURE dbo.Export_ChapterBrief @ChapterID int AS BEGIN SET NOCOUNT ON; SELECT p.ProjectID, p.ProjectName, b.BookID, b.BookTitle, b.Subtitle AS BookSubtitle, c.ChapterID, c.ChapterNumber, c.ChapterTitle, c.Summary, rs.StatusName AS RevisionStatusName, cg.GoalSummary, cg.EmotionalGoal, cg.ReaderTakeaway, cg.MustInclude, cg.RisksOrConcerns FROM dbo.Chapters c INNER JOIN dbo.Books b ON b.BookID = c.BookID INNER JOIN dbo.Projects p ON p.ProjectID = b.ProjectID INNER JOIN dbo.RevisionStatuses rs ON rs.RevisionStatusID = c.RevisionStatusID LEFT JOIN dbo.ChapterGoals cg ON cg.ChapterID = c.ChapterID WHERE c.ChapterID = @ChapterID; SELECT b.BookID, b.BookTitle, b.Subtitle AS BookSubtitle, c.ChapterID, c.ChapterNumber, c.ChapterTitle, s.SceneID, s.SceneNumber, s.SceneTitle, s.Summary, pov.CharacterName AS PovCharacterName, location.LocationPath, rs.StatusName AS RevisionStatusName FROM dbo.Scenes s INNER JOIN dbo.Chapters c ON c.ChapterID = s.ChapterID INNER JOIN dbo.Books b ON b.BookID = c.BookID INNER JOIN dbo.RevisionStatuses rs ON rs.RevisionStatusID = s.RevisionStatusID LEFT JOIN dbo.Characters pov ON pov.CharacterID = COALESCE(s.POVCharacterID, c.POVCharacterID) LEFT JOIN dbo.LocationPaths location ON location.LocationID = s.PrimaryLocationID WHERE s.ChapterID = @ChapterID AND s.IsArchived = 0 ORDER BY s.SortOrder, s.SceneID; SELECT N'Warning' AS ItemType, wt.TypeName AS Title, cw.Message AS Detail, ws.SeverityName AS Status, 10 AS SortOrder FROM dbo.ContinuityWarnings cw INNER JOIN dbo.WarningTypes wt ON wt.WarningTypeID = cw.WarningTypeID INNER JOIN dbo.WarningSeverities ws ON ws.WarningSeverityID = cw.WarningSeverityID WHERE cw.ChapterID = @ChapterID AND cw.IsDismissed = 0 UNION ALL SELECT N'Open Thread', pt.ThreadTitle, pt.Summary, ts.StatusName, 20 FROM dbo.ThreadEvents te INNER JOIN dbo.Scenes s ON s.SceneID = te.SceneID INNER JOIN dbo.PlotThreads pt ON pt.PlotThreadID = te.PlotThreadID INNER JOIN dbo.ThreadStatuses ts ON ts.ThreadStatusID = pt.ThreadStatusID WHERE s.ChapterID = @ChapterID AND pt.IsArchived = 0 AND ts.IsOpenStatus = 1 UNION ALL SELECT N'Open Asset', sa.AssetName, sa.Description, ast.StateName, 30 FROM dbo.AssetEvents ae INNER JOIN dbo.Scenes s ON s.SceneID = ae.SceneID INNER JOIN dbo.StoryAssets sa ON sa.StoryAssetID = ae.StoryAssetID LEFT JOIN dbo.AssetStates ast ON ast.AssetStateID = sa.CurrentStateID WHERE s.ChapterID = @ChapterID AND sa.IsArchived = 0 AND sa.IsResolved = 0 UNION ALL SELECT N'Open Note', COALESCE(sn.NoteTitle, snt.TypeName), sn.NoteText, snt.TypeName, 40 FROM dbo.SceneNotes sn INNER JOIN dbo.SceneNoteTypes snt ON snt.SceneNoteTypeID = sn.SceneNoteTypeID INNER JOIN dbo.Scenes s ON s.SceneID = sn.SceneID WHERE s.ChapterID = @ChapterID AND sn.IsResolved = 0 ORDER BY SortOrder, ItemType, Title; END; GO CREATE OR ALTER PROCEDURE dbo.Export_CharacterReference @CharacterID int AS BEGIN SET NOCOUNT ON; SELECT CharacterID, ProjectID, CharacterName, ShortName, Sex, BirthDate, AgeAtSeriesStart, AgeReferenceSceneID, Height, EyeColour, DefaultDescription, CreatedDate, UpdatedDate, IsArchived FROM dbo.Characters WHERE CharacterID = @CharacterID; EXEC dbo.Relationship_ListByCharacter @CharacterID; SELECT sc.SceneCharacterID, sc.SceneID, sc.CharacterID, ch.ProjectID, ch.CharacterName, ch.ShortName, ch.BirthDate, ch.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, c.ChapterNumber, b.BookTitle, b.Subtitle AS BookSubtitle, sc.CreatedDate, sc.UpdatedDate FROM dbo.SceneCharacters sc INNER JOIN dbo.Characters ch ON ch.CharacterID = sc.CharacterID INNER JOIN dbo.Scenes s ON s.SceneID = sc.SceneID INNER JOIN dbo.Chapters c ON c.ChapterID = s.ChapterID INNER JOIN dbo.Books b ON b.BookID = c.BookID LEFT JOIN dbo.CharacterRoleInSceneTypes role ON role.CharacterRoleInSceneTypeID = sc.RoleInSceneTypeID LEFT JOIN dbo.PresenceTypes presence ON presence.PresenceTypeID = sc.PresenceTypeID LEFT JOIN dbo.LocationPaths location ON location.LocationID = sc.LocationID LEFT JOIN dbo.LocationPaths entry ON entry.LocationID = sc.EntryLocationID LEFT JOIN dbo.LocationPaths exitLocation ON exitLocation.LocationID = sc.ExitLocationID WHERE sc.CharacterID = @CharacterID AND s.IsArchived = 0 AND c.IsArchived = 0 AND b.IsArchived = 0 ORDER BY b.SortOrder, c.SortOrder, s.SortOrder; EXEC dbo.CharacterKnowledge_ListByCharacter @CharacterID; SELECT ace.AssetCustodyEventID, ace.StoryAssetID, sa.AssetName, ace.SceneID, ace.AssetCustodyEventTypeID, acet.TypeName AS CustodyEventTypeName, ace.Description, STRING_AGG(CONCAT(COALESCE(custodian.CharacterName, aec.CharacterNameText), N' (', cr.RoleName, N')'), N', ') AS CustodianSummary, ace.CreatedDate, ace.UpdatedDate, b.BookTitle, b.Subtitle AS BookSubtitle, ch.ChapterNumber, s.SceneNumber, s.SceneTitle FROM dbo.AssetCustodyEvents ace INNER JOIN dbo.StoryAssets sa ON sa.StoryAssetID = ace.StoryAssetID INNER JOIN dbo.AssetCustodyEventTypes acet ON acet.AssetCustodyEventTypeID = ace.AssetCustodyEventTypeID INNER JOIN dbo.Scenes s ON s.SceneID = ace.SceneID INNER JOIN dbo.Chapters ch ON ch.ChapterID = s.ChapterID INNER JOIN dbo.Books b ON b.BookID = ch.BookID INNER JOIN dbo.AssetCustodyEventCharacters aec ON aec.AssetCustodyEventID = ace.AssetCustodyEventID LEFT JOIN dbo.Characters custodian ON custodian.CharacterID = aec.CharacterID LEFT JOIN dbo.CustodyRoles cr ON cr.CustodyRoleID = aec.CustodyRoleID WHERE aec.CharacterID = @CharacterID GROUP BY ace.AssetCustodyEventID, ace.StoryAssetID, sa.AssetName, ace.SceneID, ace.AssetCustodyEventTypeID, acet.TypeName, ace.Description, ace.CreatedDate, ace.UpdatedDate, b.BookTitle, b.Subtitle, ch.ChapterNumber, s.SceneNumber, s.SceneTitle, b.SortOrder, ch.SortOrder, s.SortOrder ORDER BY b.SortOrder, ch.SortOrder, s.SortOrder; SELECT N'Note' AS ItemType, COALESCE(sn.NoteTitle, snt.TypeName) AS Title, sn.NoteText AS Detail, CASE WHEN sn.IsResolved = 1 THEN N'Resolved' ELSE N'Open' END AS Status, 10 AS SortOrder FROM dbo.SceneNotes sn INNER JOIN dbo.SceneNoteTypes snt ON snt.SceneNoteTypeID = sn.SceneNoteTypeID INNER JOIN dbo.SceneCharacters sc ON sc.SceneID = sn.SceneID WHERE sc.CharacterID = @CharacterID AND sn.IsResolved = 0 ORDER BY SortOrder, Title; END; GO CREATE OR ALTER PROCEDURE dbo.Export_ResearchPack @ProjectID int AS BEGIN SET NOCOUNT ON; SELECT b.BookTitle, b.Subtitle AS BookSubtitle, c.ChapterNumber, c.ChapterTitle, s.SceneID, s.SceneNumber, s.SceneTitle, snt.TypeName AS Topic, COALESCE(sn.NoteTitle, snt.TypeName) AS Title, sn.NoteText AS Detail, CAST(NULL AS nvarchar(1000)) AS Reference FROM dbo.SceneNotes sn INNER JOIN dbo.SceneNoteTypes snt ON snt.SceneNoteTypeID = sn.SceneNoteTypeID INNER JOIN dbo.Scenes s ON s.SceneID = sn.SceneID INNER JOIN dbo.Chapters c ON c.ChapterID = s.ChapterID INNER JOIN dbo.Books b ON b.BookID = c.BookID WHERE b.ProjectID = @ProjectID AND snt.TypeName IN (N'Research Note', N'Continuity Note', N'Timeline Note') AND s.IsArchived = 0 AND c.IsArchived = 0 AND b.IsArchived = 0 UNION ALL SELECT b.BookTitle, b.Subtitle AS BookSubtitle, c.ChapterNumber, c.ChapterTitle, s.SceneID, s.SceneNumber, s.SceneTitle, sat.TypeName, sa.Title, sa.Notes, COALESCE(sa.ExternalUrl, sa.FilePath) FROM dbo.SceneAttachments sa INNER JOIN dbo.SceneAttachmentTypes sat ON sat.SceneAttachmentTypeID = sa.SceneAttachmentTypeID INNER JOIN dbo.Scenes s ON s.SceneID = sa.SceneID INNER JOIN dbo.Chapters c ON c.ChapterID = s.ChapterID INNER JOIN dbo.Books b ON b.BookID = c.BookID WHERE b.ProjectID = @ProjectID AND s.IsArchived = 0 AND c.IsArchived = 0 AND b.IsArchived = 0 ORDER BY BookTitle, ChapterNumber, SceneNumber, Topic, Title; END; GO CREATE OR ALTER PROCEDURE dbo.Analytics_StoryHealth @ProjectID int, @BookID int = NULL, @StartChapterNumber decimal(10,2) = NULL, @EndChapterNumber decimal(10,2) = NULL, @StartSceneNumber decimal(10,2) = NULL, @EndSceneNumber decimal(10,2) = NULL AS BEGIN SET NOCOUNT ON; DECLARE @HasMetricSettings bit = CASE WHEN EXISTS (SELECT 1 FROM dbo.ProjectTimelineMetricSettings WHERE ProjectID = @ProjectID) THEN 1 ELSE 0 END; ;WITH ScopeScenes AS ( SELECT nso.* FROM dbo.NarrativeSceneOrder nso WHERE nso.ProjectID = @ProjectID AND (@BookID IS NULL OR nso.BookID = @BookID) AND (@StartChapterNumber IS NULL OR nso.ChapterNumber >= @StartChapterNumber) AND (@EndChapterNumber IS NULL OR nso.ChapterNumber <= @EndChapterNumber) AND (@StartSceneNumber IS NULL OR nso.SceneNumber >= @StartSceneNumber) AND (@EndSceneNumber IS NULL OR nso.SceneNumber <= @EndSceneNumber) ) SELECT ss.SceneID, ss.BookID, ss.BookTitle, ss.BookSubtitle, ss.ChapterID, ss.ChapterNumber, ss.ChapterTitle, ss.SceneNumber, ss.SceneTitle, ss.GlobalSceneIndex, ss.BookSceneIndex, smt.MetricTypeID, smt.MetricName, smv.Value, smt.MinValue, smt.MaxValue, CASE WHEN smt.MaxValue = smt.MinValue THEN 0 ELSE CAST(((smv.Value - smt.MinValue) * 100.0) / (smt.MaxValue - smt.MinValue) AS int) END AS [Percent] FROM ScopeScenes ss INNER JOIN dbo.SceneMetricValues smv ON smv.SceneID = ss.SceneID INNER JOIN dbo.SceneMetricTypes smt ON smt.MetricTypeID = smv.MetricTypeID WHERE smt.IsActive = 1 AND ( (@HasMetricSettings = 1 AND EXISTS ( SELECT 1 FROM dbo.ProjectTimelineMetricSettings ptms WHERE ptms.ProjectID = @ProjectID AND ptms.MetricTypeID = smt.MetricTypeID )) OR (@HasMetricSettings = 0 AND smt.MetricName IN ( N'Overall Intensity', N'Tension', N'Emotional Weight', N'Action', N'Romance', N'Violence', N'Sexual Charge', N'Mystery', N'Hope / Lightness', N'Comedy', N'Darkness' )) ) ORDER BY smt.SortOrder, ss.GlobalSceneIndex; ;WITH ScopeScenes AS ( SELECT nso.SceneID, nso.ChapterID FROM dbo.NarrativeSceneOrder nso WHERE nso.ProjectID = @ProjectID AND (@BookID IS NULL OR nso.BookID = @BookID) AND (@StartChapterNumber IS NULL OR nso.ChapterNumber >= @StartChapterNumber) AND (@EndChapterNumber IS NULL OR nso.ChapterNumber <= @EndChapterNumber) AND (@StartSceneNumber IS NULL OR nso.SceneNumber >= @StartSceneNumber) AND (@EndSceneNumber IS NULL OR nso.SceneNumber <= @EndSceneNumber) ) SELECT rs.RevisionStatusID, rs.StatusName, COUNT(s.SceneID) AS SceneCount, SUM(CASE WHEN c.ChapterID IS NOT NULL THEN 1 ELSE 0 END) AS ChapterCount FROM dbo.RevisionStatuses rs LEFT JOIN dbo.Scenes s ON s.RevisionStatusID = rs.RevisionStatusID AND s.SceneID IN (SELECT SceneID FROM ScopeScenes) LEFT JOIN dbo.Chapters c ON c.RevisionStatusID = rs.RevisionStatusID AND c.ChapterID IN (SELECT DISTINCT ChapterID FROM ScopeScenes) GROUP BY rs.RevisionStatusID, rs.StatusName, rs.SortOrder ORDER BY rs.SortOrder; ;WITH ScopeScenes AS ( SELECT nso.SceneID, nso.BookID, nso.BookTitle, nso.BookSubtitle, nso.ChapterID, nso.ChapterNumber, nso.SceneNumber, nso.SceneTitle FROM dbo.NarrativeSceneOrder nso WHERE nso.ProjectID = @ProjectID AND (@BookID IS NULL OR nso.BookID = @BookID) AND (@StartChapterNumber IS NULL OR nso.ChapterNumber >= @StartChapterNumber) AND (@EndChapterNumber IS NULL OR nso.ChapterNumber <= @EndChapterNumber) AND (@StartSceneNumber IS NULL OR nso.SceneNumber >= @StartSceneNumber) AND (@EndSceneNumber IS NULL OR nso.SceneNumber <= @EndSceneNumber) ) SELECT ss.SceneID, ss.BookID, ss.BookTitle, ss.BookSubtitle, ss.ChapterID, ss.ChapterNumber, ss.SceneNumber, ss.SceneTitle, rs.StatusName AS RevisionStatusName FROM ScopeScenes ss INNER JOIN dbo.Scenes s ON s.SceneID = ss.SceneID INNER JOIN dbo.RevisionStatuses rs ON rs.RevisionStatusID = s.RevisionStatusID WHERE rs.StatusName IN (N'Needs Work', N'Cut Candidate', N'Drafted') ORDER BY rs.SortOrder, ss.BookID, ss.ChapterNumber, ss.SceneNumber; ;WITH ScopeScenes AS ( SELECT nso.SceneID FROM dbo.NarrativeSceneOrder nso WHERE nso.ProjectID = @ProjectID AND (@BookID IS NULL OR nso.BookID = @BookID) AND (@StartChapterNumber IS NULL OR nso.ChapterNumber >= @StartChapterNumber) AND (@EndChapterNumber IS NULL OR nso.ChapterNumber <= @EndChapterNumber) AND (@StartSceneNumber IS NULL OR nso.SceneNumber >= @StartSceneNumber) AND (@EndSceneNumber IS NULL OR nso.SceneNumber <= @EndSceneNumber) ), Total AS (SELECT COUNT(*) AS TotalScenes FROM ScopeScenes) SELECT spt.ScenePurposeTypeID, spt.PurposeName, COUNT(sp.SceneID) AS SceneCount, CASE WHEN Total.TotalScenes = 0 THEN 0 ELSE CAST(COUNT(sp.SceneID) * 100.0 / Total.TotalScenes AS decimal(5,1)) END AS PercentOfScenes FROM dbo.ScenePurposeTypes spt CROSS JOIN Total LEFT JOIN dbo.ScenePurposes sp ON sp.ScenePurposeTypeID = spt.ScenePurposeTypeID AND sp.SceneID IN (SELECT SceneID FROM ScopeScenes) WHERE spt.IsArchived = 0 GROUP BY spt.ScenePurposeTypeID, spt.PurposeName, spt.SortOrder, Total.TotalScenes ORDER BY spt.SortOrder; ;WITH ScopeScenes AS ( SELECT nso.SceneID, nso.BookID, nso.BookTitle, nso.BookSubtitle, nso.ChapterID, nso.ChapterNumber, nso.SceneNumber, nso.SceneTitle FROM dbo.NarrativeSceneOrder nso INNER JOIN dbo.Scenes s ON s.SceneID = nso.SceneID WHERE nso.ProjectID = @ProjectID AND (@BookID IS NULL OR nso.BookID = @BookID) AND (@StartChapterNumber IS NULL OR nso.ChapterNumber >= @StartChapterNumber) AND (@EndChapterNumber IS NULL OR nso.ChapterNumber <= @EndChapterNumber) AND (@StartSceneNumber IS NULL OR nso.SceneNumber >= @StartSceneNumber) AND (@EndSceneNumber IS NULL OR nso.SceneNumber <= @EndSceneNumber) AND ( NOT EXISTS (SELECT 1 FROM dbo.ScenePurposes sp WHERE sp.SceneID = nso.SceneID) OR ISNULL(NULLIF(LTRIM(RTRIM(s.SceneOutcomeNotes)), N''), N'') = N'' ) ) SELECT ss.SceneID, ss.BookID, ss.BookTitle, ss.BookSubtitle, ss.ChapterID, ss.ChapterNumber, ss.SceneNumber, ss.SceneTitle, CASE WHEN NOT EXISTS (SELECT 1 FROM dbo.ScenePurposes sp WHERE sp.SceneID = ss.SceneID) THEN 1 ELSE 0 END AS MissingPurpose, CASE WHEN ISNULL(NULLIF(LTRIM(RTRIM(s.SceneOutcomeNotes)), N''), N'') = N'' THEN 1 ELSE 0 END AS MissingOutcome FROM ScopeScenes ss INNER JOIN dbo.Scenes s ON s.SceneID = ss.SceneID ORDER BY ss.BookID, ss.ChapterNumber, ss.SceneNumber; EXEC dbo.Analytics_StoryHealth_Remainder @ProjectID, @BookID, @StartChapterNumber, @EndChapterNumber, @StartSceneNumber, @EndSceneNumber; END; GO CREATE OR ALTER PROCEDURE dbo.RelationshipMap_GetByProject @ProjectID int AS BEGIN SET NOCOUNT ON; SELECT CharacterID, ProjectID, CharacterName, ShortName, Sex, BirthDate, AgeAtSeriesStart, Height, EyeColour, DefaultDescription, CharacterImportance, ShowInQuickAddBar, CreatedDate, UpdatedDate, IsArchived FROM dbo.Characters WHERE ProjectID = @ProjectID AND IsArchived = 0 ORDER BY CharacterName; SELECT b.BookID, b.ProjectID, b.BookTitle, b.Subtitle, b.BookNumber, b.Description, b.SortOrder, b.CreatedDate, b.UpdatedDate, b.IsArchived FROM dbo.Books b WHERE b.ProjectID = @ProjectID AND b.IsArchived = 0 ORDER BY b.SortOrder, b.BookNumber, b.BookTitle; SELECT c.ChapterID, c.BookID, c.ChapterNumber, c.ChapterTitle, c.POVCharacterID, c.Summary, c.SortOrder, c.RevisionStatusID, c.CreatedDate, c.UpdatedDate, c.IsArchived FROM dbo.Chapters c INNER JOIN dbo.Books b ON b.BookID = c.BookID WHERE b.ProjectID = @ProjectID AND b.IsArchived = 0 AND c.IsArchived = 0 ORDER BY b.SortOrder, c.SortOrder, c.ChapterNumber, c.ChapterTitle; SELECT s.SceneID, s.ChapterID, s.SceneNumber, s.SceneTitle, s.Summary, s.POVCharacterID, s.PrimaryLocationID, s.SortOrder, s.TimeModeID, s.StartDateTime, s.EndDateTime, s.DurationAmount, s.DurationUnitID, s.RelativeTimeText, s.TimeConfidenceID, s.ScenePurposeNotes, s.SceneOutcomeNotes, s.RevisionStatusID, s.CreatedDate, s.UpdatedDate, s.IsArchived FROM dbo.Scenes s INNER JOIN dbo.Chapters c ON c.ChapterID = s.ChapterID INNER JOIN dbo.Books b ON b.BookID = c.BookID WHERE b.ProjectID = @ProjectID AND b.IsArchived = 0 AND c.IsArchived = 0 AND s.IsArchived = 0 ORDER BY b.SortOrder, c.SortOrder, s.SortOrder, s.SceneNumber, s.SceneTitle; SELECT cr.CharacterRelationshipID, cr.ProjectID, cr.CharacterAID, ca.CharacterName AS CharacterAName, cr.CharacterBID, cb.CharacterName AS CharacterBName, cr.RelationshipTypeID, rt.TypeName AS RelationshipTypeName, rt.RelationshipCategoryID, rc.CategoryName AS RelationshipCategoryName, COALESCE(rc.SortOrder, 900) AS CategorySortOrder, rt.SortOrder AS RelationshipTypeSortOrder, cr.IsPermanent, cr.StartSceneID, cr.EndSceneID, cr.IsKnownToReader, cr.Notes, cr.IsInitialRelationship, cr.InitialBookID, b.BookTitle AS InitialBookTitle, b.Subtitle AS InitialBookSubtitle, cr.ReaderInitiallyKnows, cr.InitialRelationshipStateID, rs.StateName AS InitialRelationshipStateName, cr.InitialIntensity, cr.IsReciprocal, cr.CreatedDate, cr.UpdatedDate, cr.IsArchived FROM dbo.CharacterRelationships cr INNER JOIN dbo.Characters ca ON ca.CharacterID = cr.CharacterAID INNER JOIN dbo.Characters cb ON cb.CharacterID = cr.CharacterBID INNER JOIN dbo.RelationshipTypes rt ON rt.RelationshipTypeID = cr.RelationshipTypeID LEFT JOIN dbo.RelationshipCategories rc ON rc.RelationshipCategoryID = rt.RelationshipCategoryID LEFT JOIN dbo.Books b ON b.BookID = cr.InitialBookID LEFT JOIN dbo.RelationshipStates rs ON rs.RelationshipStateID = cr.InitialRelationshipStateID WHERE cr.ProjectID = @ProjectID AND cr.IsArchived = 0 ORDER BY COALESCE(rc.SortOrder, 900), ca.CharacterName, cb.CharacterName, rt.SortOrder; SELECT re.RelationshipEventID, re.CharacterRelationshipID, cr.ProjectID, cr.CharacterAID, ca.CharacterName AS CharacterAName, cr.CharacterBID, cb.CharacterName AS CharacterBName, rt.TypeName AS RelationshipTypeName, rt.RelationshipCategoryID, rc.CategoryName AS RelationshipCategoryName, COALESCE(rc.SortOrder, 900) AS CategorySortOrder, rt.SortOrder AS RelationshipTypeSortOrder, cr.IsReciprocal, re.SceneID, s.SceneNumber, s.SceneTitle, ch.ChapterNumber, b.BookTitle, b.Subtitle AS BookSubtitle, re.RelationshipStateID, rs.StateName AS RelationshipStateName, re.Intensity, re.IsKnownToOtherCharacter, re.Description, re.CreatedDate, re.UpdatedDate FROM dbo.RelationshipEvents re INNER JOIN dbo.CharacterRelationships cr ON cr.CharacterRelationshipID = re.CharacterRelationshipID INNER JOIN dbo.Characters ca ON ca.CharacterID = cr.CharacterAID INNER JOIN dbo.Characters cb ON cb.CharacterID = cr.CharacterBID INNER JOIN dbo.RelationshipTypes rt ON rt.RelationshipTypeID = cr.RelationshipTypeID LEFT JOIN dbo.RelationshipCategories rc ON rc.RelationshipCategoryID = rt.RelationshipCategoryID INNER JOIN dbo.RelationshipStates rs ON rs.RelationshipStateID = re.RelationshipStateID INNER JOIN dbo.Scenes s ON s.SceneID = re.SceneID INNER JOIN dbo.Chapters ch ON ch.ChapterID = s.ChapterID INNER JOIN dbo.Books b ON b.BookID = ch.BookID WHERE cr.ProjectID = @ProjectID AND cr.IsArchived = 0 AND b.IsArchived = 0 AND ch.IsArchived = 0 AND s.IsArchived = 0 ORDER BY b.SortOrder, ch.SortOrder, s.SortOrder, re.CreatedDate, re.RelationshipEventID; SELECT RelationshipCategoryID, CategoryName, SortOrder, IsActive FROM dbo.RelationshipCategories WHERE IsActive = 1 ORDER BY SortOrder, CategoryName; END; GO