Comparing two SQL Server database schema in C#

前端 未结 4 1101
情深已故
情深已故 2021-02-09 02:25

I am releasing a newer version of my Windows application. There are DB schema changes in the new version. Also I do not want to lose the data.

So the approach that I ha

相关标签:
4条回答
  • 2021-02-09 03:13

    We use SQL Compare from RedGate, but it's not particularly cheap.

    SQL Compare

    This lets us compare the structure of two databases, and creates a SQL script to update one of the databases to match the other.

    0 讨论(0)
  • 2021-02-09 03:14

    Create your Database Migration scripts and run them with a tool such as Db Up to keep track of schema changes. SQL Scripts migrate your database from version 1 to 2, 2 to 3, etc. Schema Compare is another option mentioned in a previous question.

    • Modify Customer Tables.sql
    • Update Settings.sql
    0 讨论(0)
  • 2021-02-09 03:15

    Here's a free way to compare databases.

    Below is a SQL Server script I knocked up, which outputs the contents of your database's Stored Procedures, Views and Tables into the Output window.

    You run it by calling:

    exec [dbo].[ScriptStoredProcedures]
    

    On many of my projects, I'll run this script, copy the text into a file in my Visual Studio project, so I can check-in a copy of how our database looked at a particular time.

    (Yes, you can also have Database Projects within Visual Studio, but this is an alternative method.)

    If you run this script on both of your database, you can compare the two outputs to find differences.

    CREATE PROCEDURE [dbo].[ScriptStoredProcedures]
    AS
    BEGIN
        --
        --  Attempt to create a long SQL script, to Drop, then "CREATE PROCEDURE" on all SPs and "CREATE FUNCTION" on all Functions in this database. 
        --
        --  You can then run this script on a "target" database, and it'll have the latest Stored Procedures & functions
        --  created/updated on it.
        --
        --      exec [dbo].[ScriptStoredProcedures]
        --  
        SET NOCOUNT ON
    
        PRINT '--'
        PRINT '--  SQL Script, generated by the [ScriptStoredProcedures] Stored Procedure.'
        PRINT '--  Created on ' + convert(nvarchar, GetDate(), 106) + ' ' + convert(nvarchar, GetDate(), 108)
        PRINT '--'
        PRINT '--  This will create/update the Stored Procedures on this database, to bring them up-to-date with the SPs '
        PRINT '--  from the database ''' + DB_NAME() + ''' on the server ''' + @@SERVERNAME + ''''
        PRINT '--'
        PRINT '--'
    
    
    
        --  Create a temporary table, where each record contains one line of Stored Procedure/Function script
        --  (i.e. If you have a Stored Procedure with 30 lines of script in it, we'll create 30 temporary records
        --  to store it)
        CREATE TABLE #tmp 
        (
            [inx] INT IDENTITY(1, 1),
            [text] nvarchar(4000)
        )
    
        DECLARE @StoredProcedureName NVARCHAR(200)
        DECLARE @StoredProcedureType NVARCHAR(10)
        DECLARE @ExecCommand NVARCHAR(200)
        DECLARE @OneLineOfScript NVARCHAR(4000)
    
        --  First, get a list of all Stored Procedures & Functions in this database
        DECLARE cursorEachStoredProcedure CURSOR LOCAL FORWARD_ONLY STATIC READ_ONLY FOR 
        SELECT [name],              --  Name of the Stored Procedure or Function
               [type]               --  This will contain "FN" if it's a Function, or "P" if it's a Stored Procedure
        FROM sysobjects 
        WHERE (OBJECTPROPERTY(id, N'IsProcedure') = 1
          OR OBJECTPROPERTY(id, N'IsTableFunction') = 1
          OR OBJECTPROPERTY(id, N'IsScalarFunction') = 1
          OR OBJECTPROPERTY(id, N'IsView') = 1)
        AND [name] NOT LIKE 'sp_%'
        AND [name] NOT LIKE 'fn_%'
        ORDER BY [type] DESC,       --  Sort by Stored Procedures first, then functions
                 [name]             --  then show the list of SPs/Functions in name order
    
    
        OPEN cursorEachStoredProcedure 
        FETCH NEXT FROM cursorEachStoredProcedure INTO @StoredProcedureName, @StoredProcedureType
    
        --  For each Stored Procedure we've found in our database, create some script to delete the Stored Procedure
        --  from the target database if it exists, then re-create it.
        WHILE (@@FETCH_STATUS = 0) 
        BEGIN 
    
            PRINT ''
            IF (@StoredProcedureType = 'P')
            BEGIN
                PRINT 'PRINT ''Creating stored procedure: ''''' + @StoredProcedureName + ''''''''
                PRINT ''
                PRINT 'IF EXISTS(select Name from sysobjects where OBJECTPROPERTY(id, N''IsProcedure'') = 1 AND Name = ''' + @StoredProcedureName + ''')'
                PRINT 'BEGIN'
                PRINT '   DROP PROCEDURE [' + @StoredProcedureName + '] '
                PRINT 'END'
            END
            ELSE
            IF (@StoredProcedureType = 'V')
            BEGIN
                PRINT 'PRINT ''Creating view: ''''' + @StoredProcedureName + ''''''''
                PRINT ''
                PRINT 'IF EXISTS(select Name from sysobjects where OBJECTPROPERTY(id, N''IsView'') = 1 AND Name = ''' + @StoredProcedureName + ''')'
                PRINT 'BEGIN'
                PRINT '   DROP VIEW [' + @StoredProcedureName + '] '
                PRINT 'END'
            END
            ELSE
            BEGIN
                PRINT 'PRINT ''Creating function: ''''' + @StoredProcedureName + ''''''''
                PRINT ''
                PRINT 'IF EXISTS(select Name from sysobjects where (OBJECTPROPERTY(id, N''IsTableFunction'') = 1 OR OBJECTPROPERTY(id, N''IsScalarFunction'') = 1) AND Name = ''' + @StoredProcedureName + ''')'
                PRINT 'BEGIN'
                PRINT '   DROP FUNCTION [' + @StoredProcedureName + '] '
                PRINT 'END'
            END         
            PRINT 'GO '
    
            --  Run the "sp_helptext" command, to get the text of this Stored Procedure (one row per *line* of script)
            --  and store this set of results in a temporary table, so we can step through, line-by-line, and send
            --  the output to the Messages window.
            SET @ExecCommand = 'sp_helptext @objname = ''' + @StoredProcedureName + ''''
    
            DELETE FROM #tmp
    
            INSERT INTO #tmp
            EXEC(@ExecCommand)
    
            --  Step through each line of this Stored Procedure
            DECLARE cursorEachLineOfScript CURSOR LOCAL FORWARD_ONLY STATIC READ_ONLY FOR 
            SELECT [text] 
            FROM #tmp
            ORDER BY [inx]
    
            OPEN cursorEachLineOfScript 
            FETCH NEXT FROM cursorEachLineOfScript INTO @OneLineOfScript
    
            WHILE (@@FETCH_STATUS = 0) 
            BEGIN 
                --  For each line of Stored Procedure script, send the text to the Messages window
                PRINT @OneLineOfScript
    
                FETCH NEXT FROM cursorEachLineOfScript INTO @OneLineOfScript
            END 
            CLOSE cursorEachLineOfScript 
            DEALLOCATE cursorEachLineOfScript   
            PRINT 'GO '
    
            FETCH NEXT FROM cursorEachStoredProcedure INTO @StoredProcedureName, @StoredProcedureType
        END
    
        CLOSE cursorEachStoredProcedure 
        DEALLOCATE cursorEachStoredProcedure    
    
        DROP TABLE #tmp 
    
        PRINT 'EXEC [dbo].[spGrantExectoAllStoredProcs]'
        PRINT 'GO'
    
        PRINT '--'
        PRINT '--'
        PRINT '--  List of tables (and their fields) in this database'
        PRINT '--'
        PRINT '--'
        PRINT '--'
    
    
        --  First, let's iterate through our list of tables, and find out which fields they contain.
        DECLARE 
            @tableName nvarchar(200),
            @fieldName nvarchar(500),
            @fieldType nvarchar(500),
            @fieldNullable nvarchar(200)
    
        DECLARE cursorTables CURSOR LOCAL FORWARD_ONLY STATIC READ_ONLY FOR 
        SELECT st.NAME as 'Table_name'
        FROM sys.tables st
        ORDER BY 1
    
        OPEN cursorTables 
        FETCH NEXT FROM cursorTables INTO @tableName
    
        WHILE (@@FETCH_STATUS = 0) 
        BEGIN
            PRINT '--  Table: ' + @tableName
    
            DECLARE cursorFields CURSOR LOCAL FORWARD_ONLY STATIC READ_ONLY FOR 
            SELECT sc.NAME as 'Field_name',
                case when t.Name in ('char', 'varchar', 'nvarchar') 
               then t.Name + '(' + cast(sc.max_length/2 as nvarchar) + ')' 
               else 
                    case when t.Name in ('numeric') 
                        then t.Name + '(' + cast(sc.precision as nvarchar)  + ',' + cast(sc.scale as nvarchar) + ')'  
                        else t.Name 
                    end
            end as 'Data_type',
            case when sc.is_nullable=1 then 'null' else 'not null' end as 'Nullable'
            FROM sys.tables st
            INNER JOIN sys.columns sc ON st.object_id = sc.object_id
            INNER JOIN sys.types t ON sc.system_type_id = t.system_type_id
            WHERE t.Name != 'sysname'
            AND st.name = @tableName
            ORDER BY 1, 2
    
            OPEN cursorFields 
            FETCH NEXT FROM cursorFields INTO @fieldName, @fieldType, @fieldNullable
    
            WHILE (@@FETCH_STATUS = 0) 
            BEGIN
                PRINT '--    ' + @fieldName + '  (' + @fieldType + ', ' + @fieldNullable + ')'
                FETCH NEXT FROM cursorFields INTO @fieldName, @fieldType, @fieldNullable
            END
            CLOSE cursorFields 
            DEALLOCATE cursorFields 
    
            PRINT '--'
    
            FETCH NEXT FROM cursorTables INTO @tableName
        END
        CLOSE cursorTables 
        DEALLOCATE cursorTables 
    END
    
    0 讨论(0)
  • 2021-02-09 03:26

    Two suggestions.

    1. use the SQL Comparison SDK from Redgate (who I work for). This allows you programmatic access to the SQL Compare technology.
    2. There must be a characteristic of a particular schema version you can check to determine which one it is? If so, you could run the appropriate script against that version to bring it to the next. Your installer just needs to include a chain of such migration scripts that get run in succession to take it to the next incremental version. Ideally you'd have version information embedded in the schema either via an extended property or in a version table, which gets updated upon successful application of a migration script.
    0 讨论(0)
提交回复
热议问题