MSSQL-Change Identity seed, table records using cursors and verify records.

Kasun Nanayakkara - Feb 27 - - Dev Community

Find the tables that belong to the column name. Show the primary tables and foreign key tables as a list here.

SELECT      c.name  AS 'ColumnName'
            ,(SCHEMA_NAME(t.schema_id) + '.' + t.name) AS 'TableName'
FROM        sys.columns c
JOIN        sys.tables  t   ON c.object_id = t.object_id
WHERE       c.name LIKE '%MyColumnName%'
ORDER BY    TableName
            ,ColumnName;
Enter fullscreen mode Exit fullscreen mode
ColumnName         TableName
MyColumnName       dbo.FkTable1
MyColumnName       dbo.FkTable2
MyColumnName       dbo.MyTable
Enter fullscreen mode Exit fullscreen mode

The task here is to update data in MyTable. In this table, I need to change the records code with primary keys 1, 2, and 3, and update other records primary keys as well. Additionally, I need to verify the current primary keys of the records and correct them if necessary.I will also need to check the current structure of MyTable and update the structure accordingly. After making the updates, the identity seed should be reset.

Current Mytable 
id     code     name
1      abc      abc-disc 
2      efg      efg-disc 
3      ijh      ijh-disc 
20     kmn      kmn-disc 
30     qrs      qrs-disc 

Udated Mytable
id        code     name
1         cba      abc-disc 
2         gfe      efg-disc 
3         hji      ijh-disc 
100000000 kmn      kmn-disc 
100000001 qrs      qrs-disc 
Enter fullscreen mode Exit fullscreen mode

Stop the current identity check

SET IDENTITY_INSERT Mytable ON
GO
Enter fullscreen mode Exit fullscreen mode

Declare a table parameter to capture the current data before making any changes to the MyTable, so that you can keep the original data intact and then modify it accordingly. This is a common approach to ensure data integrity during updates. You can create a table variable or temporary table to store the original data before making any changes.

DECLARE @TempMytable TABLE (ID INT, Code VARCHAR(MAX), Name VARCHAR(MAX), NewRecordID INT);
Enter fullscreen mode Exit fullscreen mode

Insert Mytable record to tmp table call TempMytable.

INSERT INTO @TempMytable(ID, Code, Name)
SELECT ID, Code, Name
FROM Mytable;

DECLARE @ID INT, @Code VARCHAR(MAX), @Name VARCHAR(MAX)
Enter fullscreen mode Exit fullscreen mode

Set the default identity seed value and other related parameter values.

DECLARE @Seed INT= 10000000, @NewRecordID INT = 1, @NewCode VARCHAR(MAX) = '';
Enter fullscreen mode Exit fullscreen mode

Disable the check constraint.

ALTER TABLE MPP NOCHECK CONSTRAINT [FK_MPP_Mytable];
ALTER TABLE Mytable NOCHECK CONSTRAINT ALL;
Enter fullscreen mode Exit fullscreen mode

Remove the unique constraint.

ALTER TABLE Mytable
DROP CONSTRAINT U1_Mytable, UK_Mytable_Name;
Enter fullscreen mode Exit fullscreen mode

Disable the trigers

ALTER TABLE MyTable DISABLE TRIGGER Mytable_InsteadOfDTrig
Enter fullscreen mode Exit fullscreen mode

Declare the cursor and refer to the records available in MyTable because I created a temporary table. When the cursor runs, it gets the current table information. If the table is updated, the record will also be fetched and processed. So, I want to avoid that behavior and run based on the previous (old) state of the table and data.

DECLARE db_cursor CURSOR FOR
SELECT ID, Code, Name
FROM @TempMytable

BEGIN
    OPEN db_cursor  
    FETCH NEXT FROM db_cursor INTO @ID, @Code, @Name
    WHILE @@FETCH_STATUS = 0  
    BEGIN

        IF @Name = 'abc-disc' OR @Name = 'efg-disc' OR  @Name = 'ijh-disc'
        BEGIN
            /* Insert / Update system code */

            IF @Name = 'abc-disc' 
                SELECT @NewRecordID = 1, @NewCode = 'cba';
            ELSE IF @Name = 'efg-disc' 
                SELECT @NewRecordID = 2, @NewCode = 'gfe';
            ELSE IF @Name = 'ijh-disc'
                SELECT @NewRecordID = 3, @NewCode = 'hji';

            IF @ID = @NewRecordID
            BEGIN
                UPDATE Mytable SET Code = @NewCode WHERE ID = @NewRecordID
            END
            ELSE IF EXISTS(SELECT * FROM Mytable WHERE ID = @NewRecordID)
            BEGIN 
                UPDATE Mytable SET Code = @NewCode,
                Name = @Name
                WHERE ID = @NewRecordID
            END 
            ELSE
            BEGIN
                INSERT INTO Mytable(ID, Code, Name)
                VALUES(@NewRecordID, @NewCode, @Name)
            END

        END
Enter fullscreen mode Exit fullscreen mode

In here, I want to check whether records already exist in your table before inserting new ones, so you can avoid inserting duplicate records that would violate a unique constraint.

 ELSE IF(@ID < 10000000)
/* Check table id morethan the 10,000,000 */  
        BEGIN
            /* Insert record as a new record Mytable */

            INSERT INTO Mytable(ID, Code, Name)
            VALUES(@Seed, @Code, @Name)

            SELECT @NewRecordID = @Seed;

            SELECT @Seed = @Seed + 1;

        END
Enter fullscreen mode Exit fullscreen mode

In this case, I need to get the new record ID into a temporary table called @TempMyTable to check if the records have new IDs

 /* Update @TempMytable tables with new ID */ 

        UPDATE @TempMytable 
            SET NewRecordID = CASE WHEN @ID > @NewRecordID THEN @ID
                ELSE @NewRecordID
            END
            WHERE ID = @ID;

        PRINT  CAST(@ID AS VARCHAR) +' - ' + @Code +' - ' + @Name +' - ' + CAST(@NewRecordID AS VARCHAR) + ' - ' + @NewCode

        FETCH NEXT FROM db_cursor
        INTO @ID, @Code, @Name

    END 
    CLOSE db_cursor
    DEALLOCATE db_cursor
END

Enter fullscreen mode Exit fullscreen mode

Reset @id , @NewRecordsID values

SELECT @ID = 0, @NewRecordID = 0;
Enter fullscreen mode Exit fullscreen mode

/* Verify date at @TempMytable table and foreign key tables */

SELECT * FROM @TempMytable
id     code     name        newRecordid
1      abc      abc-disc    1
2      efg      efg-disc    2
3      ijh      ijh-disc    3
20     kmn      kmn-disc    100000000
30     qrs      qrs-disc    100000002
Enter fullscreen mode Exit fullscreen mode

Check if foreign key tables have records.

SELECT m.ID,Count(m.pkid) FkTable1_Count FROM FkTable1 m GROUP BY m.ID
SELECT m.ID,Count(m.pkid) FkTable2_Count FROM FkTable2 m GROUP BY m.ID
FkTable1
id          FkTable1_Count 
1           1170
2           5522

FkTable2
id          FkTable2_Count 
1           15877
2           155895
3           187958
Enter fullscreen mode Exit fullscreen mode

Declare another cursor and refer to the records available in the temporary table called @TempMyTable to update the new record ID.

DECLARE db_newCursor CURSOR FOR
SELECT ID, NewRecordID
FROM @TempMytable
Enter fullscreen mode Exit fullscreen mode
BEGIN
    OPEN db_newCursor  
    FETCH NEXT FROM db_newCursor INTO @ID, @NewRecordID
    WHILE @@FETCH_STATUS = 0  
    BEGIN
Enter fullscreen mode Exit fullscreen mode

Update the foreign key value as per the newly generated record ID.

 /* Update foreign key tables*/
        IF @ID = @NewRecordID
             If record id s is same no need to change. 
             PRINT 'IDs are same. '+ CAST(@ID AS VARCHAR) + ' -> ' + CAST(@NewRecordID AS VARCHAR);
        ELSE 
        BEGIN
            PRINT 'FkTable1 # ' + CAST(@ID AS VARCHAR) + ' -> ' + CAST(@NewRecordID AS VARCHAR);
            UPDATE FkTable1 SET ID = @NewRecordID
            WHERE ID = @ID;

            PRINT 'FkTable2 # ' + CAST(@ID AS VARCHAR) + ' -> ' + CAST(@NewRecordID AS VARCHAR);
            UPDATE FkTable2 SET ID = @NewRecordID
            WHERE ID = @ID;

        END

        FETCH NEXT FROM db_newCursor INTO @ID, @NewRecordID

    END 
    CLOSE db_newCursor
    DEALLOCATE db_newCursor
END

Enter fullscreen mode Exit fullscreen mode

start the current identity check

SET IDENTITY_INSERT Mytable OFF
GO
Enter fullscreen mode Exit fullscreen mode

Why does the previous state not delete the record? Because it has a foreign key value, and it will be affected when I delete the record. I know the old records have a range from 4 to 99999999, and those records should be deleted in the delete query.

/* Delete old Mytable */
DELETE FROM Mytable WHERE ID BETWEEN 4 AND 9999999
Enter fullscreen mode Exit fullscreen mode

Set up the identity seed value to match the maximum record value.
Generally, users reset the identity seed using this snippet:
DBCC CHECKIDENT ('MyTable', RESEED, @IdentSeed).
However, when I use it, I get the following error:
Checking identity information: current identity value '52228', current column value '52228'. DBCC execution completed. If DBCC printed error messages, contact your system administrator.
I used the keyword WITH NO_INFOMSGS; to overcome the issue.
This problem is described in the following link: identity-column-value-falling-behind-randomly

/* Set new identity seed */
DECLARE @IdentSeed INT = 10000000;
SELECT @IdentSeed = Max(ID) FROM Mytable;

PRINT 'New identity seed # ' + CAST(@IdentSeed AS VARCHAR);
DBCC CHECKIDENT ('Mytable', RESEED, @IdentSeed)  WITH NO_INFOMSGS;
GO

/* Add uniqe constraint */
SET ANSI_PADDING ON
GO
Enter fullscreen mode Exit fullscreen mode

Add a unique key constraints for table records.

/****** Object:  Index [Mytable] */
ALTER TABLE [dbo].[Mytable] ADD  CONSTRAINT [U1_Mytable] UNIQUE NONCLUSTERED 
(
    [Code] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 90, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
GO

ALTER INDEX [U1_Mytable] ON [dbo].[Mytable] DISABLE
GO
PRINT 'Added constrant - U1_Mytable';

/****** Object:  Index [UK_Mytable_Name] */
ALTER TABLE [dbo].[Mytable] ADD  CONSTRAINT [UK_Mytable_Name] UNIQUE NONCLUSTERED 
(
    [Name] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
GO
PRINT 'Added constrant - UK_Mytable_Name';

ALTER TABLE MPP CHECK CONSTRAINT [FK_MPP_Mytable]
GO
ALTER INDEX ALL ON Mytable REBUILD

GO
Enter fullscreen mode Exit fullscreen mode

Enable the trigers

ALTER TABLE MyTable ENABLE TRIGGER MyTable_InsteadOfDTrig
GO
Enter fullscreen mode Exit fullscreen mode
. . .