Data reload from PROD to Non-PROD in sequence of table referenced by Foreign Key [closed]

  Kiến thức lập trình

I want to reload the data from the production to the non-production environment, I’ve many tables in dbo schema, these tables are referred by foreign keys to each other, the tables also have auto-generated primary keys, I can create Linked Servers.

I need to generate a reusable data retrofit script, which should fulfill the requirements, the script should take care of the following:

  1. Identifying the database tables, constraints, and keys without taking it as input from the user,
  2. Truncate multiple tables in a sequence of their reference, so the child tables should be truncated first then the parent,
  3. Load data from another server using Linked Server,
  4. Load the identity (primary key) data as well

3

Recently I’ve generated the following script which helped me reload (retrofit) data from production to non-production environment:

DECLARE @LinkedServerName NVARCHAR(255) = 'ProductionLinkedServer'
DECLARE @DatabaseName NVARCHAR(255) = 'MyDatabase'
DECLARE @Schema NVARCHAR(255) = 'dbo'

DECLARE @DropForeignKeyTemplate NVARCHAR(MAX) = 'ALTER TABLE %s DROP CONSTRAINT %s;';
DECLARE @CreateForeignKeyTemplate NVARCHAR(MAX) = 'ALTER TABLE %s WITH CHECK ADD CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s (%s);';
DECLARE @TruncateTemplate NVARCHAR(MAX) = 'TRUNCATE TABLE %s;';
DECLARE @InsertDataTemplate NVARCHAR(MAX) = 'SET IDENTITY_INSERT %s ON; INSERT INTO %s SELECT * FROM %s.%s.%s.%s; SET IDENTITY_INSERT %s OFF;';

DECLARE @Tables TABLE (
    TableName NVARCHAR(MAX),
    ForeignKeyTableName NVARCHAR(MAX),
    ForeignKeyConstraintName NVARCHAR(MAX),
    DropForeignKeyScript NVARCHAR(MAX),
    CreateForeignKeyScript NVARCHAR(MAX),
    TruncateScript NVARCHAR(MAX),
    HasForeignKey BIT,
    InsertDataScript NVARCHAR(MAX)
);

INSERT INTO @Tables (TableName, ForeignKeyTableName, ForeignKeyConstraintName, DropForeignKeyScript, CreateForeignKeyScript, TruncateScript, HasForeignKey, InsertDataScript)
SELECT t.name AS TableName,
       OBJECT_NAME(fk.referenced_object_id) AS ForeignKeyTableName,
       fk.name AS ForeignKeyConstraintName,
       IIF(fk.name is not null, FORMATMESSAGE(@DropForeignKeyTemplate, QUOTENAME(t.name),QUOTENAME(fk.name)),'') AS DropForeignKeyScript,
       IIF(fk.name IS NOT NULL, 
          FORMATMESSAGE(@CreateForeignKeyTemplate,QUOTENAME(t.name),QUOTENAME(fk.name)
          ,STRING_AGG(QUOTENAME(c.COLUMN_NAME), ', ') WITHIN GROUP (ORDER BY ic.ORDINAL_POSITION)
          ,QUOTENAME(OBJECT_NAME(fk.referenced_object_id)),STRING_AGG(QUOTENAME(c.COLUMN_NAME), ', ') 
          WITHIN GROUP (ORDER BY ic.ORDINAL_POSITION)), 
           ''
          ) AS CreateForeignKeyScript,
       FORMATMESSAGE(@TruncateTemplate, QUOTENAME(t.name)) AS TruncateScript,
       IIF(fk.name IS NOT NULL, 1, 0) AS HasForeignKey,
       FORMATMESSAGE(@InsertDataTemplate, 
                  QUOTENAME(t.name), 
                  QUOTENAME(t.name), 
                  QUOTENAME(@LinkedServerName), 
                  QUOTENAME(@DatabaseName), 
                  QUOTENAME(@Schema), 
                  QUOTENAME(t.name),
                  QUOTENAME(t.name)) AS InsertDataScript
FROM sys.tables t
LEFT JOIN sys.foreign_keys fk ON fk.parent_object_id = t.object_id
LEFT JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS ic ON fk.name = ic.CONSTRAINT_NAME
LEFT JOIN INFORMATION_SCHEMA.COLUMNS AS c ON ic.TABLE_NAME = c.TABLE_NAME AND ic.COLUMN_NAME = c.COLUMN_NAME
WHERE 1=1
GROUP BY t.name, fk.name, fk.referenced_object_id
ORDER BY fk.name DESC, t.name;

--CREATE LINKED SERVER IF NOT ALREADY EXISTS
    -- Check if the linked server already exists
    DECLARE @existingLinkedServer NVARCHAR(255)

    SELECT @existingLinkedServer = name
    FROM sys.servers
    WHERE name = @LinkedServerName

    IF @existingLinkedServer IS NULL
    BEGIN
        EXEC sp_addlinkedserver   
           @server = @LinkedServerName, -- Name of the linked server
           @srvproduct = '',  
           @provider = 'SQLNCLI', -- Provider for SQL Server
           @datasrc = 'serveraddress,port', -- IP address and port of the server
           @provstr = 'UID=userId;PWD=password'; -- SQL Server authentication credentials

        PRINT 'Linked server created successfully.'
    END
    ELSE
    BEGIN
        PRINT 'Linked server already exists.'
    END

--EXECUTE THE SCRIPTS
    DECLARE @SqlCommand NVARCHAR(MAX);
    DECLARE @Commands TABLE (Command NVARCHAR(MAX));

    -- Populate the @Commands table with all commands
    INSERT INTO @Commands (Command)
    SELECT DropForeignKeyScript FROM @Tables where DropForeignKeyScript <> '' GROUP BY DropForeignKeyScript 
    UNION ALL
    SELECT TruncateScript FROM @Tables where TruncateScript <> '' GROUP BY TruncateScript 
    UNION ALL
    SELECT InsertDataScript FROM @Tables where InsertDataScript <> '' GROUP BY InsertDataScript
    UNION ALL
    SELECT CreateForeignKeyScript FROM @Tables where CreateForeignKeyScript <> '' GROUP BY CreateForeignKeyScript 

    -- Initialize loop variables
    DECLARE @RowCount INT = (SELECT COUNT(*) FROM @Commands)
    DECLARE @Counter INT = 1
    
    --SELECT * FROM @COMMANDS
        
    -- Loop through each command and execute it
    WHILE @Counter <= @RowCount
    BEGIN
        SELECT @SqlCommand = Command
        FROM @Commands
        WHERE (SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL))) = @Counter
        -- Execute the command
        EXEC sp_executesql @SqlCommand
        SET @Counter = @Counter + 1
    END

LEAVE A COMMENT