niedziela, 27 marca 2011

Przeniesienie bazy z SQL 2008 na SQL 2005 (downgrade).

Ostatnio uczestniczyłem w przenoszeniu bazy z SQL 2008 na SQL 2005. Proste backup i restore w tym przypadku nie zadziała. Do przeniesienia struktury wykorzystałem narzędzie GENERATE SCRIPTS, a do transferu danych EXPORT DATA.


Cały proces przeszedł bez problemu... (tak mi się tylko wydawało).

Napotkałem w sumie na dwa problemy. Pierwszy polegał na tym, że niektóre obiekty, głównie klucze i defaulty. W tych miejscach gdzie te obiekty nie miały nadanej konkretnej nazwy, a została ona wygenerowana przez SQL serwer (dostaje ona wtedy dziwny numerek na końcu nazwy), na nowym serwerze nazwa ta też została wygenerowana. Czyli na kolumnie była zdefiniowana wartość domyślna ale constraint miał inny numerek w nazwie.

Drugi problem polegał na przeniesieniu danych. Okazało się, że nie przeniosły się one jeden w jeden. W moim przypadki problem był w kolumnach, które miały ustawioną wartość domyślną, ale dopuszczały NULL. Jeżeli na bazie źródłowej w kolumnie takiej był NULL to w bazie docelowej w tym samym polu pojawiła się wartość domyślna.

Napisałem skrypciorka, który pokazał mi w jakich tabelach zmieniły się dane.
USE ADVENTUREWORKSLT2008;

-- DEKLARACJA ZMIENNYCH
DECLARE @TABLE_ID INT -- ID TABELI
    , @TABLE_NAME VARCHAR(50) -- NAZWA TABELI
    , @SCHEMA_NAME VARCHAR(50) -- NAZWA SCHEMATU
    , @COLUMN_NAME VARCHAR(MAX) -- NAZWY KOLUMN
    , @S_SERVER_NAME VARCHAR(100) -- SERWER ŹRÓDŁOWY
    , @D_SERVER_NAME VARCHAR(100) -- SERWER DOCELOWY
    , @S_DB_NAME VARCHAR(100) -- BAZA ŹRÓDŁOWA
    , @D_DB_NAME VARCHAR(100) -- BAZA DOCELOWA
    , @MYSQL VARCHAR(MAX)
    , @MYSQL_EXT VARCHAR(MAX); 

-- PRZYPISANIE WARTOŚCI ZMIENNYM
SET @S_SERVER_NAME = '[SQL2008]';
SET @D_SERVER_NAME = '[SQL2005]';
SET @S_DB_NAME = 'ADVENTUREWORKSLT2008';
SET @D_DB_NAME = 'ADVENTUREWORKSLT2005';
    
DECLARE TABLE_CURSOR CURSOR FOR -- DEKLARACJA KURSORA DO POBIERANIA INFORMACJI O TABELACH
SELECT [OBJECT_ID]
    , SCHEMA_NAME(SCHEMA_ID) AS SCH_NAME
    , NAME
FROM SYS.TABLES
ORDER BY NAME ASC;

OPEN TABLE_CURSOR;

FETCH NEXT FROM TABLE_CURSOR 
INTO @TABLE_ID
    , @SCHEMA_NAME
    , @TABLE_NAME;

WHILE @@FETCH_STATUS = 0
BEGIN
    
    SET @COLUMN_NAME = '';
    SET @MYSQL = '';
    SET @MYSQL_EXT = '';
    
    -- ZŁOŻENIE KOLUMN TABELI
    SELECT @COLUMN_NAME = @COLUMN_NAME + ',[' + C.NAME + ']'
    FROM SYS.COLUMNS AS C
        INNER JOIN SYS.TYPES AS T ON C.SYSTEM_TYPE_ID = T.SYSTEM_TYPE_ID
    WHERE C.[OBJECT_ID] = @TABLE_ID
        AND T.NAME NOT IN ('IMAGE', 'TEXT', 'NTEXT', 'XML') -- NIE WSZYSTKIE DANE ZA POMOCĄ EXCEPT DA SIĘ PORÓWNAĆ
    ORDER BY C.COLUMN_ID ASC;

    SET @COLUMN_NAME = STUFF(@COLUMN_NAME, 1, 1, '') -- USUNIĘCIE PRZECINKA Z POCZĄTKU STRINGA

    -- ZŁOŻENIE ZAPYTANIE
    SET @MYSQL_EXT = @SCHEMA_NAME + '.' + @TABLE_NAME;
    
    SET @MYSQL =    'SELECT ' + @COLUMN_NAME 
                    + ' FROM ' + @S_SERVER_NAME + '.' + @S_DB_NAME + '.' + @SCHEMA_NAME + '.' + @TABLE_NAME
                    + ' EXCEPT '
                    + 'SELECT ' + @COLUMN_NAME 
                    + ' FROM ' + @D_SERVER_NAME + '.' + @D_DB_NAME + '.' + @SCHEMA_NAME + '.' + @TABLE_NAME;
    
    EXEC ('SELECT ''' + @MYSQL_EXT + '''') -- TABELA ANALIZOWANA
    EXEC (@MYSQL); -- ANALIZA DANYCH
        
    FETCH NEXT FROM TABLE_CURSOR 
    INTO @TABLE_ID
        , @SCHEMA_NAME
        , @TABLE_NAME;
    
END

CLOSE TABLE_CURSOR;
DEALLOCATE TABLE_CURSOR;
Aby nie mieć problemów z wartościami NULL w kolumnach z wartością domyślną nie należy generować ich za pomocą GENERATE SCRIPTS przed wstawieniem danych.

Co do pierwszego problemu to obejścia dalej szukam. ;))