152 lines
6.8 KiB
Transact-SQL
152 lines
6.8 KiB
Transact-SQL
SET ANSI_NULLS ON;
|
|
GO
|
|
SET QUOTED_IDENTIFIER ON;
|
|
GO
|
|
|
|
/*
|
|
Focused optimisation for dbo.CharacterTimeline_GetByProject.
|
|
|
|
Diagnosis:
|
|
- The timeline timing showed this procedure as the clear bottleneck.
|
|
- The original appearance query joined SceneCharacters to Characters, Scenes,
|
|
Chapters, Books, and the recursive LocationPaths view three times.
|
|
- Filtering the project/book scene set once gives SQL Server a small driving
|
|
set before joining appearances.
|
|
- Materialising project location paths once avoids repeated recursive view
|
|
expansion for location, entry location, and exit location labels.
|
|
*/
|
|
|
|
IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE name = N'IX_Characters_ProjectID_Importance_Name' AND object_id = OBJECT_ID(N'dbo.Characters'))
|
|
BEGIN
|
|
-- Supports CharacterTimeline_GetByProject character list ordering.
|
|
CREATE INDEX IX_Characters_ProjectID_Importance_Name
|
|
ON dbo.Characters(ProjectID, IsArchived, CharacterImportance DESC, CharacterName);
|
|
END;
|
|
GO
|
|
|
|
IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE name = N'IX_SceneCharacters_CharacterID_SceneID' AND object_id = OBJECT_ID(N'dbo.SceneCharacters'))
|
|
BEGIN
|
|
-- Supports appearance lookups when the optimizer starts from project characters.
|
|
CREATE INDEX IX_SceneCharacters_CharacterID_SceneID
|
|
ON dbo.SceneCharacters(CharacterID, SceneID, RoleInSceneTypeID);
|
|
END;
|
|
GO
|
|
|
|
IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE name = N'IX_Locations_Project_Parent_Name' AND object_id = OBJECT_ID(N'dbo.Locations'))
|
|
BEGIN
|
|
-- Supports project-scoped LocationPaths materialisation.
|
|
CREATE INDEX IX_Locations_Project_Parent_Name
|
|
ON dbo.Locations(ProjectID, IsArchived, ParentLocationID, LocationName);
|
|
END;
|
|
GO
|
|
|
|
IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE name = N'IX_Locations_ParentLocationID' AND object_id = OBJECT_ID(N'dbo.Locations'))
|
|
BEGIN
|
|
-- Supports recursive LocationPaths expansion from parent to child locations.
|
|
CREATE INDEX IX_Locations_ParentLocationID
|
|
ON dbo.Locations(ParentLocationID, IsArchived, LocationName);
|
|
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,
|
|
BookSortOrder int NOT NULL,
|
|
ChapterSortOrder int NOT NULL,
|
|
SceneSortOrder int NOT NULL
|
|
);
|
|
|
|
INSERT #TimelineScenes (SceneID, SceneNumber, SceneTitle, ChapterNumber, BookTitle, BookSortOrder, ChapterSortOrder, SceneSortOrder)
|
|
SELECT s.SceneID, s.SceneNumber, s.SceneTitle, ch.ChapterNumber, b.BookTitle, 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,
|
|
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, DefaultDescription, CreatedDate, UpdatedDate, IsArchived)
|
|
SELECT CharacterID, ProjectID, CharacterName, ShortName, Sex, BirthDate, AgeAtSeriesStart, AgeReferenceSceneID,
|
|
Height, EyeColour, CharacterImportance, 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, 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,
|
|
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
|