Spaces:
Paused
Paused
feat(bidder): simplify SQL queries by removing unnecessary index hints for improved readability
dd08f57 | -- ============================================================================ | |
| -- DATABASE CHANGES - Consolidated Script | |
| -- Date: 30 November 2025 | |
| -- Purpose: Performance optimization for AquaBarrier project management API | |
| -- ============================================================================ | |
| -- ============================================================================ | |
| -- PART 1: ADD TIMESTAMP COLUMNS TO PROJECTS TABLE | |
| -- ============================================================================ | |
| -- Purpose: Support dashboard metrics and track record lifecycle | |
| -- Impact: Enables "New Projects" filtering in dashboard | |
| -- ============================================================================ | |
| -- Add CreatedDate column (if not exists) | |
| IF NOT EXISTS (SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS | |
| WHERE TABLE_NAME = 'Projects' AND COLUMN_NAME = 'CreatedDate') | |
| BEGIN | |
| ALTER TABLE [dbo].[Projects] ADD [CreatedDate] [datetime2](7) NULL DEFAULT (getutcdate()); | |
| PRINT 'Added CreatedDate column to Projects table'; | |
| END | |
| ELSE | |
| PRINT 'CreatedDate already exists in Projects table'; | |
| GO | |
| -- Add ModifiedDate column (if not exists) | |
| IF NOT EXISTS (SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS | |
| WHERE TABLE_NAME = 'Projects' AND COLUMN_NAME = 'ModifiedDate') | |
| BEGIN | |
| ALTER TABLE [dbo].[Projects] ADD [ModifiedDate] [datetime2](7) NULL DEFAULT (getutcdate()); | |
| PRINT 'Added ModifiedDate column to Projects table'; | |
| END | |
| ELSE | |
| PRINT 'ModifiedDate already exists in Projects table'; | |
| GO | |
| -- Backfill CreatedDate with current date for existing records (if NULL) | |
| UPDATE [dbo].[Projects] | |
| SET [CreatedDate] = GETUTCDATE() | |
| WHERE [CreatedDate] IS NULL; | |
| PRINT 'Backfilled CreatedDate for ' + CAST(@@ROWCOUNT AS varchar) + ' existing Projects'; | |
| GO | |
| -- ============================================================================ | |
| -- PART 2: CREATE PERFORMANCE INDEXES | |
| -- ============================================================================ | |
| -- Purpose: Optimize query performance for project and customer endpoints | |
| -- Impact: Reduces query execution time from 5+ seconds to sub-second | |
| -- ============================================================================ | |
| -- Index for dashboard metrics filtering by CreatedDate | |
| IF NOT EXISTS (SELECT 1 FROM sys.indexes | |
| WHERE name = 'IX_Projects_CreatedDate' AND object_id = OBJECT_ID('dbo.Projects')) | |
| BEGIN | |
| CREATE NONCLUSTERED INDEX [IX_Projects_CreatedDate] ON [dbo].[Projects] | |
| ( | |
| [CreatedDate] ASC | |
| )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, | |
| DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, | |
| ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]; | |
| PRINT 'Created index IX_Projects_CreatedDate'; | |
| END | |
| ELSE | |
| PRINT 'Index IX_Projects_CreatedDate already exists'; | |
| GO | |
| -- Index for Bidders ProjectNo lookups (if not exists) | |
| IF NOT EXISTS (SELECT 1 FROM sys.indexes | |
| WHERE name = 'IX_Bidders_ProjectNo' AND object_id = OBJECT_ID('dbo.Bidders')) | |
| BEGIN | |
| CREATE NONCLUSTERED INDEX [IX_Bidders_ProjectNo] ON [dbo].[Bidders] | |
| ( | |
| [ProjNo] ASC | |
| )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, | |
| DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, | |
| ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]; | |
| PRINT 'Created index IX_Bidders_ProjectNo'; | |
| END | |
| ELSE | |
| PRINT 'Index IX_Bidders_ProjectNo already exists'; | |
| GO | |
| -- Index for Bidders Id (keyset pagination) | |
| IF NOT EXISTS (SELECT 1 FROM sys.indexes | |
| WHERE name = 'IX_Bidders_Id' AND object_id = OBJECT_ID('dbo.Bidders')) | |
| BEGIN | |
| CREATE NONCLUSTERED INDEX [IX_Bidders_Id] ON [dbo].[Bidders] | |
| ( | |
| [Id] ASC | |
| )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, | |
| DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, | |
| ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]; | |
| PRINT 'Created index IX_Bidders_Id'; | |
| END | |
| ELSE | |
| PRINT 'Index IX_Bidders_Id already exists'; | |
| GO | |
| -- Index for Bidders CustId lookups | |
| IF NOT EXISTS (SELECT 1 FROM sys.indexes | |
| WHERE name = 'IX_Bidders_CustId' AND object_id = OBJECT_ID('dbo.Bidders')) | |
| BEGIN | |
| CREATE NONCLUSTERED INDEX [IX_Bidders_CustId] ON [dbo].[Bidders] | |
| ( | |
| [CustId] ASC | |
| )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, | |
| DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, | |
| ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]; | |
| PRINT 'Created index IX_Bidders_CustId'; | |
| END | |
| ELSE | |
| PRINT 'Index IX_Bidders_CustId already exists'; | |
| GO | |
| -- Index for Customers CustomerID lookups | |
| IF EXISTS (SELECT 1 FROM sys.indexes | |
| WHERE name = 'IX_Customers_CustomerID' AND object_id = OBJECT_ID('dbo.Customers')) | |
| BEGIN | |
| DROP INDEX [IX_Customers_CustomerID] ON [dbo].[Customers]; | |
| PRINT 'Dropped existing IX_Customers_CustomerID to recreate with INCLUDE(CompanyName)'; | |
| END | |
| GO | |
| CREATE NONCLUSTERED INDEX [IX_Customers_CustomerID] ON [dbo].[Customers] | |
| ( | |
| [CustomerID] ASC | |
| ) | |
| INCLUDE ([CompanyName]) | |
| WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, | |
| DROP_EXISTING = OFF, ONLINE = ON, ALLOW_ROW_LOCKS = ON, | |
| ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]; | |
| PRINT 'Created index IX_Customers_CustomerID with INCLUDE(CompanyName)'; | |
| GO | |
| -- Composite index to optimize keyset pagination and project bidders fetch | |
| IF NOT EXISTS (SELECT 1 FROM sys.indexes | |
| WHERE name = 'IX_Bidders_ProjNo_Id' AND object_id = OBJECT_ID('dbo.Bidders')) | |
| BEGIN | |
| CREATE NONCLUSTERED INDEX [IX_Bidders_ProjNo_Id] ON [dbo].[Bidders] | |
| ( | |
| [ProjNo] ASC, | |
| [Id] ASC | |
| ) | |
| INCLUDE ([CustId]) | |
| WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, | |
| DROP_EXISTING = OFF, ONLINE = ON, ALLOW_ROW_LOCKS = ON, | |
| ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]; | |
| PRINT 'Created index IX_Bidders_ProjNo_Id'; | |
| END | |
| ELSE | |
| PRINT 'Index IX_Bidders_ProjNo_Id already exists'; | |
| GO | |
| -- Composite index to support ORDER BY [Primary] DESC, Id for offset pagination | |
| IF NOT EXISTS (SELECT 1 FROM sys.indexes | |
| WHERE name = 'IX_Bidders_ProjNo_Primary_Id' AND object_id = OBJECT_ID('dbo.Bidders')) | |
| BEGIN | |
| CREATE NONCLUSTERED INDEX [IX_Bidders_ProjNo_Primary_Id] ON [dbo].[Bidders] | |
| ( | |
| [ProjNo] ASC, | |
| [Primary] DESC, | |
| [Id] ASC | |
| ) | |
| INCLUDE ( | |
| [CustId], [Quote], [DateLastContact], [DateFollowup], | |
| [CustType], [EmailAddress], [ReplacementCost], [Enabled] | |
| ) | |
| WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, | |
| DROP_EXISTING = OFF, ONLINE = ON, ALLOW_ROW_LOCKS = ON, | |
| ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]; | |
| PRINT 'Created index IX_Bidders_ProjNo_Primary_Id'; | |
| END | |
| ELSE | |
| PRINT 'Index IX_Bidders_ProjNo_Primary_Id already exists'; | |
| GO | |
| -- ============================================================================ | |
| -- PART 3: OPTIONAL - MIGRATE Bidders.CustId FROM nvarchar(15) TO int | |
| -- ============================================================================ | |
| -- Purpose: Eliminate CAST() in JOIN clause for better index usage | |
| -- Impact: Removes type conversion overhead, allows index seek on Customers | |
| -- WARNING: This is a breaking schema change - test thoroughly before applying | |
| -- Status: OPTIONAL - Only apply if type mismatch is causing performance issues | |
| -- ============================================================================ | |
| /* | |
| -- Uncomment this section to perform the migration | |
| -- Step 1: Add new int column | |
| IF NOT EXISTS (SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS | |
| WHERE TABLE_NAME = 'Bidders' AND COLUMN_NAME = 'CustId_New') | |
| BEGIN | |
| ALTER TABLE [dbo].[Bidders] ADD [CustId_New] [int] NULL; | |
| PRINT 'Added CustId_New column'; | |
| END | |
| GO | |
| -- Step 2: Migrate data | |
| UPDATE [dbo].[Bidders] | |
| SET [CustId_New] = CAST([CustId] AS int) | |
| WHERE ISNUMERIC([CustId]) = 1 | |
| AND [CustId_New] IS NULL; | |
| PRINT 'Migrated ' + CAST(@@ROWCOUNT AS varchar) + ' rows to CustId_New'; | |
| GO | |
| -- Step 3: Drop index on old column | |
| IF EXISTS (SELECT 1 FROM sys.indexes | |
| WHERE name = 'IX_Bidders_CustId' AND object_id = OBJECT_ID('dbo.Bidders')) | |
| BEGIN | |
| DROP INDEX [IX_Bidders_CustId] ON [dbo].[Bidders]; | |
| PRINT 'Dropped index IX_Bidders_CustId'; | |
| END | |
| GO | |
| -- Step 4: Drop old nvarchar column | |
| IF EXISTS (SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS | |
| WHERE TABLE_NAME = 'Bidders' AND COLUMN_NAME = 'CustId' AND DATA_TYPE = 'nvarchar') | |
| BEGIN | |
| ALTER TABLE [dbo].[Bidders] DROP COLUMN [CustId]; | |
| PRINT 'Dropped old nvarchar CustId column'; | |
| END | |
| GO | |
| -- Step 5: Rename new column | |
| IF EXISTS (SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS | |
| WHERE TABLE_NAME = 'Bidders' AND COLUMN_NAME = 'CustId_New') | |
| AND NOT EXISTS (SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS | |
| WHERE TABLE_NAME = 'Bidders' AND COLUMN_NAME = 'CustId') | |
| BEGIN | |
| EXEC sp_rename 'dbo.Bidders.CustId_New', 'CustId', 'COLUMN'; | |
| PRINT 'Renamed CustId_New to CustId'; | |
| END | |
| GO | |
| -- Step 6: Set NOT NULL constraint | |
| IF EXISTS (SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS | |
| WHERE TABLE_NAME = 'Bidders' AND COLUMN_NAME = 'CustId' | |
| AND DATA_TYPE = 'int' AND IS_NULLABLE = 'YES') | |
| BEGIN | |
| IF NOT EXISTS (SELECT 1 FROM [dbo].[Bidders] WHERE [CustId] IS NULL) | |
| BEGIN | |
| ALTER TABLE [dbo].[Bidders] ALTER COLUMN [CustId] [int] NOT NULL; | |
| PRINT 'Set CustId to NOT NULL'; | |
| END | |
| ELSE | |
| BEGIN | |
| PRINT 'WARNING: NULL values exist in CustId, cannot set NOT NULL'; | |
| END | |
| END | |
| GO | |
| -- Step 7: Recreate index on new int column | |
| IF NOT EXISTS (SELECT 1 FROM sys.indexes | |
| WHERE name = 'IX_Bidders_CustId' AND object_id = OBJECT_ID('dbo.Bidders')) | |
| BEGIN | |
| CREATE NONCLUSTERED INDEX [IX_Bidders_CustId] ON [dbo].[Bidders] | |
| ( | |
| [CustId] ASC | |
| )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, | |
| DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, | |
| ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]; | |
| PRINT 'Recreated index IX_Bidders_CustId on int column'; | |
| END | |
| GO | |
| */ | |
| -- ============================================================================ | |
| -- VERIFICATION QUERIES | |
| -- ============================================================================ | |
| PRINT ''; | |
| PRINT '============================================================================'; | |
| PRINT 'VERIFICATION RESULTS'; | |
| PRINT '============================================================================'; | |
| -- Verify Projects timestamp columns | |
| SELECT 'Projects Timestamp Columns' AS CheckType; | |
| SELECT | |
| COLUMN_NAME, | |
| DATA_TYPE, | |
| IS_NULLABLE, | |
| COLUMN_DEFAULT | |
| FROM INFORMATION_SCHEMA.COLUMNS | |
| WHERE TABLE_NAME = 'Projects' | |
| AND COLUMN_NAME IN ('CreatedDate', 'ModifiedDate') | |
| ORDER BY COLUMN_NAME; | |
| -- Verify all indexes exist | |
| SELECT 'Performance Indexes' AS CheckType; | |
| SELECT | |
| t.name AS TableName, | |
| i.name AS IndexName, | |
| c.name AS ColumnName, | |
| ty.name AS DataType | |
| FROM sys.indexes i | |
| JOIN sys.index_columns ic ON i.object_id = ic.object_id AND i.index_id = ic.index_id | |
| JOIN sys.columns c ON ic.object_id = c.object_id AND ic.column_id = c.column_id | |
| JOIN sys.types ty ON c.user_type_id = ty.user_type_id | |
| JOIN sys.tables t ON i.object_id = t.object_id | |
| WHERE i.name IN ( | |
| 'IX_Projects_CreatedDate', | |
| 'IX_Bidders_ProjectNo', | |
| 'IX_Bidders_Id', | |
| 'IX_Bidders_CustId', | |
| 'IX_Customers_CustomerID' | |
| ) | |
| ORDER BY t.name, i.name; | |
| -- Verify Bidders.CustId data type | |
| SELECT 'Bidders.CustId Data Type' AS CheckType; | |
| SELECT | |
| COLUMN_NAME, | |
| DATA_TYPE, | |
| CHARACTER_MAXIMUM_LENGTH, | |
| IS_NULLABLE | |
| FROM INFORMATION_SCHEMA.COLUMNS | |
| WHERE TABLE_NAME = 'Bidders' AND COLUMN_NAME = 'CustId'; | |
| PRINT ''; | |
| PRINT '============================================================================'; | |
| PRINT 'DATABASE CHANGES COMPLETE'; | |
| PRINT '============================================================================'; | |
| GO | |