ab-ms-core / DATABASE_CHANGES_CONSOLIDATED.sql
MukeshKapoor25's picture
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