• User Newbie

    Programma PHP che esegue backup e ripristino di database SQL SERVER

    Ciao a tutti, spiego subito il problema che mi ritrovo: devo eseguire dei backup su un SQL SERVER 2000, copiarli su un nuovo server che supporta SQL 2008 ed eseguire quindi i ripristini uno alla volta.
    Tenete presente che questo programma dovrebbe spostare circa un migliaio di database anche molto pesanti, relativi ad anni passati e, soprattutto, 146 database del corrente anno una volta al giorno.

    Ho creato un programma che esegue i vari passaggi:

    • Esegue i vari backup (uno alla volta) dei database che si trovano su un SQL SERVER 2000 con relativa query BACKUP DATABASE che si crea di volta in volta un file di backup in base al nome del database, alla data e all'ora in cui viene eseguito.
    • Verifica il corretto backup con la query RESTORE VERIFYONLY.
    • Copia il file backup appena creato sul nuovo server e cancella quello presente sul vecchio server.
    • Fa il restore sul nuovo server (che come detto, è un SQL SQRVER 2008) con la query di RESTORE DATABASE.

    Tutto questo in teoria... perchè il problema principale è che il comando che lancia la query (sqlsrv_query , preciso che il mio programma è lanciato da un terzo server che supporta le librerie PHP di SQL SERVER 2008) è come se venisse interrotto e la query va sempre in errore nonostante i backup o le verifiche o gli stessi ripristini siano andati a buon fine (e questo vuol dire che le query sono corrette anche perchè sono state verificate con il Management Studio).

    Io penso che come accade sul Management Studio, dove all'esecuzione della query mi vengono restituiti dei messaggi di completamento delle varie operazioni (ad esempio quando viene eseguito il backup, Man Studio mi ritorna messaggio che mi dicono l'avanzamento della fase di backup, ad esempio "41 percent backed up.") e che questi messaggi vengano interpretati da sqlsrv_query come messaggi di errore e che quindi vada in errore.
    Sul manuale ho trovato alcuni stati di errore SQLSTATE che potrebbero anche essere evitati ed infatti ho dovuto creare una funzione di rilevazione di questo tipo di stati e che quindi mi evita la rilevazione dell'errore, di seguito il codice di esempio.

    [php]

    sqlsrv_query($connect, $sql) or trigger_error("La query ha ritornato falso : ".erroriQuerySql2008($backup, $bkPathFile, "Errore durante il Backup del database", $bkDBName, $Log));

    //funzione per segnalare più esplicitamente gli errori derivanti dalle query SQL 2008
    function erroriQuerySql2008($result, $path, $errore, $bkDBName, $Log){
    $errText="";
    if($result===false){
    if(($errors=sqlsrv_errors())!=null){
    foreach($errors as $error){
    $errText.="SQLSTATE: ".$error[ 'SQLSTATE']."\n";
    $errText.="code: ".$error[ 'code']."\n";
    $errText.="message: ".$error[ 'message']."\n";
    if($error['SQLSTATE'] == '01000' || $error[ 'SQLSTATE'] == '01001' || $error[ 'SQLSTATE'] == '01003' || $error[ 'SQLSTATE'] == '01S02' ){
    $errText = "ma non ci sono errori!!! \n";
    return $errText;
    }else{
    echo $errore;
    $array = explode("", $path);
    $Log->BackupLog("Server: ".$array[2]." DB: ".$bkDBName, $errore, basename($_SERVER["PHP_SELF"]));
    $GLOBALS['erroriBackup']++;
    return $errText;
    }
    }
    }
    }
    }
    [/php]Il problema persiste nel momento in cui mi ritrovo di cmq che i restore non vengono effettuati in nessun caso.
    In più nel momento in cui provo a fare anche solamente eseguire il tutto per i db del 2012 senza fare i restore molte volte durante la verifica del backup mi ritrovo errori di questo tipo:

    
    SQLSTATE: 42000
    code: 3201
    message: [Microsoft][SQL SERVER Native Client 10.0][SQL server]Cannot open backup device <percorso del file di backup>. Device error or device off-line. See the SQL Server error log for more details.  in <percorso del programma> on line <nr linea>
    ```L'sql server non riesce ad aprire il backup il  device, nonostante questo sia online e funzionante: il risultato di  tutto ciò è che in genere di 146 db del 2012, circa 35/38 a seconda, e  non sempre gli stessi, non vengono copiati nel nuovo server...
    
    Qualche idea? Non so se sono stato abbastanza chiaro su quello che sto cercando di risolvere...
    
    Tenete anche presente che ho provato ad effettuare anche con il Man  Studio le copie dei database con il comando di Wizard, ma questo non  parte nemmeno perchè mi dà errore durante la "configurazione del  pacchetto di Integration Services" e quindi andando in errore subito  senza nemmeno eseguire una sola azione.

  • User Newbie

    Davvero nessuno ha qualche idea??

    Al momento siamo riusciti ad aggirare parzialmente il problema, creando poi un programmino che durante il fine settimana ha fatto il detach di alcuni database degli anni passati, li ha copiati sul nuovo server e ne ha fatto il reattach. Certo è che andando aventi così ci vorranno parecchi fine settimana.

    In più il problema persiste per i database del corrente anno dei quali si vorrebbe avere un backup giornaliero da spostare.

    HELP!! 😞


  • User Newbie

    Forse ci siamo, come suggeritomi in altri forum, l'errore era proprio nella query!!!

    Eliminando le opzioni REWIND, NOUNLOAD e soprattutto STATS = 10 perlomeno i backup vengono eseguiti tutti correttamente. La nuova funzione è quindi la seguente con l'opzione RETAINDAYS = 3 per mantenere i db del corrente anno 3 giorni sul nuovo server:

    [php]
    function backupDatabase($bkDBName, $bkPathFile, $bkSet, $Log){
    sqlsrv_configure("WarningsReturnAsErrors", 0);
    $dbSenzaParentesi = filtraParentesiDb($bkDBName);
    $serverName = <nome_server>;
    $connectionInfo = array("UID" => <user_name>, "PWD" => <password>, "Database" => $dbSenzaParentesi);
    $connect = sqlsrv_connect($serverName, $connectionInfo) or trigger_error('Impossibile collegarsi al database '.$dbSenzaParentesi);

    //$sql = "ALTER DATABASE ".$bkDBName." SET RECOVERY FULL";
    echo "\n Backup del database ".$bkDBName." in corso \n";
    $sql = "BACKUP DATABASE ".$bkDBName." 
            TO  DISK = N'".$bkPathFile."' 
            WITH  RETAINDAYS = 3, NOFORMAT, NOINIT,  NAME = N'".$bkSet."', SKIP";
    echo " Query sql per il backup del database ".$bkDBName." in corso \n";
    $backup = sqlsrv_query($connect, $sql) or trigger_error("La query ha ritornato falso : ".erroriQuerySql2008($backup, $bkPathFile, "Errore durante il Backup del database: ", $bkDBName, $Log));
    //sqlsrv_close($connect);
    

    }
    [/php]Resta il problema sul restore... :arrabbiato:

    La funzione di restore non ha mai funzionato e a questo punto mi viene il dubbio che possa essere ancora qualche opzione del comando RESTORE che mi ha sempre dato noie in quanto non ha mai funzionato e quando dico mai intendo che nemmeno per i db piccoli funziona.
    Cioè la chiamata della query non dà errori però se provo a visualizzare i db con Man Studio li vedo in costatnte ripristino e mi vengono creati dei file di checkRestore.
    Effettivamente poi questo ripristino non va mai a buon fine nel senso che non viene mai completato.
    Questa la funzione di restore:

    [php]
    //Funzione per il restore del database
    function restoreDatabese($bkDBName, $bkNuovoPathFile, $Log){
    sqlsrv_configure("WarningsReturnAsErrors", 0);
    $dbSenzaParentesi = filtraParentesiDb('<nome_database_esistente>');
    $serverName = "<nome_server>";
    $connectionInfo = array("UID" => "<nome_user>", "PWD" => "<password>", "Database" => $dbSenzaParentesi);
    $connect = sqlsrv_connect($serverName, $connectionInfo) or trigger_error('Impossibile collegarsi al database '.$dbSenzaParentesi);

    echo "\n Restore del Backup del database ".$bkDBName." in corso sul nuovo server \n";
    $sql = "RESTORE DATABASE ".$bkDBName."
            FROM DISK = '".$bkNuovoPathFile."'
            WITH REPLACE, 
            MOVE '".$bkDBName."_Data' TO '<percorso>".$bkDBName."_Data.MDF',
            MOVE '".$bkDBName."_Log' TO '<percorso>".$bkDBName."_Log.LDF'";
    echo "\n Query sql per il restore del database ".$bkDBName." in corso \n";
    $restore = sqlsrv_query($connect, $sql) or trigger_error("La query ha ritornato falso : ".erroriQuerySql2008($restore, $bkNuovoPathFile, "Errore durante il Restore del database", $bkDBName, $Log));
    //sqlsrv_close($connect);
    

    }
    [/php]Che errore può essere dato che se la stessa query eseguita sul Man Studio va a buon fine :mmm: ?


  • User Newbie

    CI SIAMO RAGA!!!

    La soluzione è sostanzialmente la funzione sqlsrv_next_result() da utilizzare in questo ciclo:

    [php]
    if ( ($restore = sqlsrv_query($connect, $sql)) ){
    do {
    print_r(sqlsrv_errors());
    echo " * ---End of result --- * \n";
    } while ( sqlsrv_next_result($restore) ) ;
    sqlsrv_free_stmt($restore);
    }
    [/php]
    😄
    In pratica con questo ciclo si prendono tutti i risultati che la query ritorna ed interpreta come errori e glieli si fa leggere tutti di modo che il restore alla fine venga portato effettivamente a termine.
    E' in pratica come avere una query select alla quale mi faccio restituire con il fetch array tutti i risultati, in questo caso mi faccio restituire tutti quelli che PHP interpreta come errori per far sì che la query SQL venga effettivamente portata a termine e che quindi venga effettuato il restore.

    Per il mio caso, l'interesse sarà che siano visualizzati i messaggi che SQL SERVER mi ritorna quindi mi farò leggere solo gli errori di sqlsrv_errors() che si trovano in 'message' anche solo per verificare che una volta portata a termine la query, come sul Man Studio, l'ultimo messaggio visualizzato alla fine di ogni ciclo, sarà quello di operazione avvenuta con successo.
    La nuova funzione di restore sarà quindi così composta:

    [php]
    function restoreDatabese($bkDBName, $bkNuovoPathFile, $restorePath, $file, $Log){
    sqlsrv_configure("WarningsReturnAsErrors", 0);
    $dbSenzaParentesi = filtraParentesiDb('<database_esistente>');
    $serverName = "<nome_server>";
    $connectionInfo = array("UID" => "<user_name>", "PWD" => "<password>", "Database" => $dbSenzaParentesi);
    $connect = sqlsrv_connect($serverName, $connectionInfo) or trigger_error('Impossibile collegarsi al database '.$dbSenzaParentesi);

    if(strpos($file, "_Data")){
        $moveBkDBFile = $bkDBName."_Data";
        $moveBkDBFileLog = $bkDBName."_Log";
    }else if(strpos($file, "_data")){
        $moveBkDBFile = $bkDBName."_data";
        $moveBkDBFileLog = $bkDBName."_log";
    }else{
        $moveBkDBFile = $bkDBName;
        $moveBkDBFileLog = $bkDBName."_log";
    }
    echo "\n Restore del Backup del database ".$bkDBName." in corso sul nuovo server \n";
    $sql = "RESTORE DATABASE ".$bkDBName."
            FROM DISK = N'".$bkNuovoPathFile."'
            WITH REPLACE, RECOVERY, 
            MOVE N'".$moveBkDBFile."' TO N'".$restorePath."\\".$moveBkDBFile.".MDF',
            MOVE N'".$moveBkDBFileLog."' TO N'".$restorePath."\\".$moveBkDBFileLog.".LDF'";
    echo " Query sql per il restore del database ".$bkDBName." in corso \n";
    //$restore = sqlsrv_query($connect, $sql) or trigger_error("La query ha ritornato falso : ".erroriQuerySql2008($restore, $bkNuovoPathFile, "Errore durante il Restore del database: ", $bkDBName, $Log));
    if ( ($restore = sqlsrv_query($connect, $sql)) ){
        do{
            $array = sqlsrv_errors();
            echo " Risposta del server: ".$array[0]['message']."\n \n";
        } while ( sqlsrv_next_result($restore) ) ;
        sqlsrv_free_stmt($restore);
    }
    sqlsrv_close($connect);
    

    }
    [/php]Alla fine si riesce anche a chiudere la connessione senza problemi.

    Dovrà essere testata a fondo in settimana ma penso che questa sia la soluzione definitiva.
    Con lo stesso trucchetto potranno essere modificate anche le funzioni di Backup e di verifica del backup.
    :campione:


  • User Newbie

    Per dovere di completezza e per essere corretti fino in fondo nell'uso della programmazione, al posto dei cicli if per trovare il nome del file logico, la cosa migliore da fare è sostituirli con la chiamata SQL RESTORE FILELISTONLY.

    Quindi al posto di

    [PHP] if(strpos($file, "_Data")){
    $moveBkDBFile = $bkDBName."_Data";
    $moveBkDBFileLog = $bkDBName."_Log";
    }else if(strpos($file, "_data")){
    $moveBkDBFile = $bkDBName."_data";
    $moveBkDBFileLog = $bkDBName."_log";
    }else{
    $moveBkDBFile = $bkDBName;
    $moveBkDBFileLog = $bkDBName."_log";
    }
    [/PHP]

    è più corretto e soprattutto più sicuro (possono capitare errori di riferimenti tra i vari file) usare:

    [PHP] $sql = "RESTORE FILELISTONLY
    FROM DISK = '".$bkNuovoPathFile."'";
    $restoreFileList = sqlsrv_query($connect, $sql) or trigger_error("La query RESTORE
    FILELISTONLY ha ritornato falso : ".erroriQuerySql2008($restoreFileList, $bkNuovoPathFile,
    "Errore durante la RESTORE FILELISTONLY: ", $bkDBName, $Log));
    while($logicalName = sqlsrv_fetch_array($restoreFileList)){
    if($logicalName['Type'] == 'D'){
    $moveBkDBFile = $logicalName['LogicalName'];
    }else if ($logicalName['Type'] == 'L'){
    $moveBkDBFileLog = $logicalName['LogicalName'];
    }
    }
    @sqlsrv_free_stmt($restoreFileList);
    [/PHP]

    e lasciare tutto il resto inalterato.