Sincronizzazione struttura tabelle database

di il
7 risposte

Sincronizzazione struttura tabelle database

Ho un progetto da completare per la sincronizzazione di database. ho un problema nella lettura del formato dei campi da un tipo di database e la creazione dei campi nella tabella del database di destinazione che è di un formato diverso. i formati database principalmente coinvolti sono MySql, Firebird, Ms Access, Ms SqlServer 

ho provato la strada di una tabella con i formati dei campi dei diversi database ma credo non sia la strada giusta.

Public Function LeggiStrutturaTabella(intMasterDbId As Integer,
                                       strMasterDNS As String,
                                       strMasterPercorso As String,
                                       strMasterPorta As String,
                                       strMasterDLL As String,
                                       strMasterSetCaratteri As String,
                                       strMasterNome As String,
                                       strMasterNomeUtente As String,
                                       strMasterPassword As String,
                                       strMasterTabellaNome As String,
                                       intMasterTipologiaDatabaseId As Integer,
                                       intDestDbId As Integer,
                                       strDestDNS As String,
                                       strDestPercorso As String,
                                       strDestPorta As String,
                                       strDestDLL As String,
                                       strDestSetCaratteri As String,
                                       strDestNome As String,
                                       strDestNomeUtente As String,
                                       strDestPassword As String,
                                       strDestTabellaNome As String,
                                       intDestTipologiaDatabaseId As Integer) As DataTable

       Dim strNomeRoutine As String = System.Reflection.MethodBase.GetCurrentMethod().Name
       Dim ConnDbMaster As IDbConnection = DataBaseCaricato.ConnettiDbMaster(intMasterTipologiaDatabaseId,
                                                               strMasterDNS,
                                                               strMasterPercorso,
                                                               strMasterPorta,
                                                               strMasterDLL,
                                                               strMasterNome,
                                                               strMasterNomeUtente,
                                                               strMasterPassword,
                                                               strMasterSetCaratteri)

       Dim ConnDbDestinazione As IDbConnection = DataBaseCaricato.ConnettiDbDestinazione(intDestTipologiaDatabaseId,
                                                                            strDestDNS,
                                                                            strDestPercorso,
                                                                            strDestPorta,
                                                                            strDestDLL,
                                                                            strDestNome,
                                                                            strDestNomeUtente,
                                                                            strDestPassword,
                                                                            strDestSetCaratteri)

       ' Apriamo la connessione ConnDbMaster
       If ConnDbMaster.State = ConnectionState.Closed Then
           ConnDbMaster.Open()
       End If

       ' Apriamo la connessione ConnDbDestinazione
       If ConnDbDestinazione.State = ConnectionState.Closed Then
           ConnDbDestinazione.Open()
       End If

       Dim dtStrutturaMaster As New DataTable()
       Dim dtStrutturaDest As New DataTable()

       Dim tabellaEsistente As Boolean = False
       Dim queryCheckTable As String = ""


       Select Case intDestTipologiaDatabaseId
           Case 1 ' MS Access
               queryCheckTable = $"SELECT COUNT(*) FROM MSysObjects WHERE Type=1 AND Name='{strDestTabellaNome}'"
           Case 2 ' MySQL
               queryCheckTable = $"SELECT 1 FROM information_schema.tables WHERE table_schema = '{strDestNome}' AND table_name = '{strDestTabellaNome}'"
           Case 4 ' Firebird
               queryCheckTable = $"SELECT COUNT(*) FROM RDB$RELATIONS WHERE RDB$RELATION_NAME = '{strDestTabellaNome}'"
           Case Else
               Throw New ArgumentException("Tipologia di database di destinazione non supportata per la verifica della tabella esistente")
       End Select


       Dim commandCheckTable As IDbCommand = ConnDbDestinazione.CreateCommand()
       commandCheckTable.CommandText = queryCheckTable

       Dim result As Integer = CInt(commandCheckTable.ExecuteScalar())
       If result > 0 Then
           tabellaEsistente = True
       End If

       If Not tabellaEsistente Then
           ' La tabella di destinazione non esiste, quindi crea la tabella
           Dim queryCreateTable As String = ""

           ' Carica la struttura della tabella del database master
           If ConnDbMaster IsNot Nothing AndAlso ConnDbMaster.State = ConnectionState.Open Then
               Dim queryMaster As String
               If intDbMasterTipologiaId = 1 Then 'Ms Access
                   queryMaster = $"SELECT * FROM [{strMasterTabellaNome}]"
               Else
                   queryMaster = $"SHOW COLUMNS FROM {strMasterTabellaNome}" ' Correggi il nome della variabile tabellaNome
               End If
               Dim CommandMaster As IDbCommand = ConnDbMaster.CreateCommand()
               CommandMaster.CommandText = queryMaster

               Dim DataAdapterMaster As IDbDataAdapter
               If TypeOf ConnDbMaster Is MySqlConnection Then
                   DataAdapterMaster = New MySqlDataAdapter(DirectCast(CommandMaster, MySqlCommand))
               ElseIf TypeOf ConnDbMaster Is FbConnection Then
                   DataAdapterMaster = New FbDataAdapter(DirectCast(CommandMaster, FbCommand))
               ElseIf TypeOf ConnDbMaster Is OleDbConnection Then
                   DataAdapterMaster = New OleDbDataAdapter(DirectCast(CommandMaster, OleDbCommand))
               Else
                   Throw New ArgumentException("Tipologia di database non supportata")
               End If

               Dim readerMaster As IDataReader = CommandMaster.ExecuteReader()
               ' Carica i dati direttamente nel DataTable
               dtStrutturaMaster.Load(readerMaster)
           Else
               DataBaseCaricato.ScriviLogErrore(DateTime.Now, strNomeRoutine, "0", "Problemi di connessione: ConnDbMaster")
           End If

           ' Aggiunta delle colonne in base ai dati estratti dalla tabella del database master
           For Each column As DataColumn In dtStrutturaMaster.Columns
               Dim nomeColonna As String = column.ColumnName    ' Sostituisci 'NomeColonna' con il nome effettivo della colonna
               Dim tipoDato As String = dtStrutturaMaster.Rows(0)(column.ColumnName).GetType().Name ' Ottieni il tipo di dati della colonna nella prima riga del DataTable
               DataBaseCaricato.ScriviLogErrore(DateTime.Now, strNomeRoutine, "0", nomeColonna & " " & tipoDato)

               ' Trova l'ID del formato campo utilizzando la sigla campo e la tipologia del database
               Dim queryFormatoCampoId As String = $"SELECT FormatoCampoId FROM vFormatoCampiDataBase WHERE SiglaCampo = '{tipoDato}' AND TipologiaDataBaseId = {intMasterTipologiaDatabaseId};"

               ' Esegui la query per ottenere l'ID del formato campo utilizzando la connessione al database principale
               Dim formatoCampoId As Integer = 0
               Using command As New MySqlCommand(queryFormatoCampoId, DataBaseCaricato.ConnettiDbPrincipale()) ' Sostituisci MySqlCommand con il tipo di comando e ConnettiDbPrincipale() con la funzione di connessione al database principale
                   Dim resultFormatoCampoId As Object = command.ExecuteScalar()
                   If resultFormatoCampoId IsNot Nothing AndAlso Not DBNull.Value.Equals(resultFormatoCampoId) Then
                       formatoCampoId = Convert.ToInt32(resultFormatoCampoId)
                   End If
               End Using

               ' Utilizza l'ID del formato campo per recuperare la sigla del campo dalla vista vFormatoCampiDataBase
               Dim querySiglaCampo As String = $"SELECT SiglaCampo FROM vFormatoCampiDataBase WHERE FormatoCampoId = {formatoCampoId} AND TipologiaDataBaseId = {intDestTipologiaDatabaseId};"
               'DataBaseCaricato.ScriviLogErrore(DateTime.Now, strNomeRoutine, "0", querySiglaCampo)
               ' Esegui la query per ottenere la sigla del campo utilizzando la connessione al database principalex
               Dim strSiglaCampo As String = String.Empty
               Using command As New MySqlCommand(querySiglaCampo, DataBaseCaricato.ConnettiDbPrincipale()) ' Sostituisci MySqlCommand con il tipo di comando e ConnettiDbPrincipale() con la funzione di connessione al database principale
                   strSiglaCampo = command.ExecuteScalar()?.ToString()
               End Using
               'DataBaseCaricato.ScriviLogErrore(DateTime.Now, strNomeRoutine, "0", nomeColonna & " " & strSiglaCampo)
               ' Aggiungi la colonna alla query di creazione della tabella con il formato nome recuperato
               queryCreateTable &= $"{nomeColonna} {strSiglaCampo}"
               If column IsNot dtStrutturaMaster.Columns(dtStrutturaMaster.Columns.Count - 1) Then
                   queryCreateTable &= ", "
               End If

           Next
           Exit Function
           ' Aggiungi la parentesi chiusa alla fine della query di creazione della tabella
           queryCreateTable = $"CREATE TABLE {strDestTabellaNome} ({queryCreateTable})"


           Console.WriteLine(queryCreateTable) ' Stampare la query per il debug
           frmPrincipale.txtErrore.Text = queryCreateTable
           DataBaseCaricato.ScriviLogErrore(DateTime.Now, strNomeRoutine, "0", queryCreateTable)
           Dim commandCreateTable As IDbCommand = ConnDbDestinazione.CreateCommand()
           commandCreateTable.CommandText = queryCreateTable
           commandCreateTable.ExecuteNonQuery()
       End If

       'Try
       If ConnDbMaster IsNot Nothing Then
           Dim queryMaster As String
           Dim queryDestinazione As String
           If intDbMasterTipologiaId = 1 Then 'Ms Access
               queryMaster = $"SELECT * FROM [{strMasterTabellaNome}]"
           Else
               queryMaster = "SHOW COLUMNS FROM " & strMasterTabellaNome ' Correggi il nome della variabile tabellaNome
           End If
           Dim CommandMaster As IDbCommand = ConnDbMaster.CreateCommand()
           CommandMaster.CommandText = queryMaster

           If intDbDestTipologiaId = 1 Then
               queryDestinazione = $"SELECT * FROM [{strMasterTabellaNome}]"
           Else
               queryDestinazione = "SHOW COLUMNS FROM " & strMasterTabellaNome ' Correggi il nome della variabile tabellaNome
           End If
           Dim CommandDest As IDbCommand = ConnDbDestinazione.CreateCommand() ' Correggi il nome della variabile ConnDbDestinazione
           CommandDest.CommandText = queryDestinazione

           Dim DataAdapterMaster As IDbDataAdapter
           If TypeOf ConnDbMaster Is MySqlConnection Then
               DataAdapterMaster = New MySqlDataAdapter(DirectCast(CommandMaster, MySqlCommand))
           ElseIf TypeOf ConnDbMaster Is FbConnection Then
               DataAdapterMaster = New FbDataAdapter(DirectCast(CommandMaster, FbCommand))
           ElseIf TypeOf ConnDbMaster Is OleDbConnection Then
               DataAdapterMaster = New OleDbDataAdapter(DirectCast(CommandMaster, OleDbCommand))
           Else
               Throw New ArgumentException("Tipologia di database non supportata")
           End If

           If ConnDbMaster.State = ConnectionState.Closed Then
               ConnDbMaster.Open()
           End If
           If ConnDbMaster IsNot Nothing AndAlso ConnDbMaster.State = ConnectionState.Open Then
               Dim readerMaster As IDataReader = CommandMaster.ExecuteReader()
               ' Carica i dati direttamente nel DataTable
               dtStrutturaMaster.Load(readerMaster)
           Else
               DataBaseCaricato.ScriviLogErrore(DateTime.Now, strNomeRoutine, "0", "Problemi di connessione: ConnDbMaster")
           End If
           Dim DataAdapterDest As IDataAdapter
           If TypeOf ConnDbDestinazione Is MySqlConnection Then ' Correggi il nome della variabile ConnDbDestinazione
               DataAdapterDest = New MySqlDataAdapter(DirectCast(CommandDest, MySqlCommand))
           ElseIf TypeOf ConnDbDestinazione Is FbConnection Then ' Correggi il nome della variabile ConnDbDestinazione
               DataAdapterDest = New FbDataAdapter(DirectCast(CommandDest, FbCommand))
           ElseIf TypeOf ConnDbDestinazione Is OleDbConnection Then ' Correggi il nome della variabile ConnDbDestinazione
               DataAdapterDest = New OleDbDataAdapter(DirectCast(CommandDest, OleDbCommand))
           Else
               Throw New ArgumentException("Tipologia di database non supportata")
           End If
           If ConnDbDestinazione.State = ConnectionState.Closed Then
               ConnDbDestinazione.Open()
           End If

           If ConnDbDestinazione IsNot Nothing AndAlso ConnDbDestinazione.State = ConnectionState.Open Then
               Dim readerDestinazione As IDataReader = CommandDest.ExecuteReader()
               ' Carica i dati direttamente nel DataTable
               dtStrutturaDest.Load(readerDestinazione)
           Else
               DataBaseCaricato.ScriviLogErrore(DateTime.Now, strNomeRoutine, "0", "Problemi di connessione: ConnDbDestinazione")
           End If

           VerificaStrutturaTabelle(dtStrutturaMaster, dtStrutturaDest)

       Else
           ' Gestisci il fallimento della connessione
           DataBaseCaricato.ScriviLogErrore(DateTime.Now, strNomeRoutine, "", "Problemi di connessione")
       End If
       Try
       Catch ex As Exception
           ' Gestisci gli errori
           DataBaseCaricato.ScriviLogErrore(DateTime.Now, strNomeRoutine, ex.HResult, ex.Message)
       Finally
           ' Chiudi la connessione solo se è stata aperta
           If ConnDbMaster IsNot Nothing AndAlso ConnDbMaster.State = ConnectionState.Open Then
               ConnDbMaster.Close()
           End If
       End Try

       Return dtStrutturaMaster ' Restituisci il DataTable della struttura del database master
   End Function

7 Risposte

  • Re: Sincronizzazione struttura tabelle database

    Ma devi allineare SOLO la struttura dei database o anche trasferire il contenuto?

    Comunque e' un'attivita' COMPLESSA! I problemi sono molti:

    1. le tabelle vanno create in un SPECIFICO ordine, non in ordine sparso
    2. le tabelle hanno degli indici, che dovrebbero essere creati
    3. in una tabella potresti
      1. aggiungere una colonna
      2. rimuovere una colonna
      3. cambiare il tipo di una colonna
    4. devono essere creati i “constraint” relativi alle “foreign keys”
    5. i dati vanno trasferiti in modo “intelligente” 
      1. non si devere avere duplicati 
      2. si deve trasferire SOLO gli aggiornamenti
        1. righe aggiunte
        2. righe cancellate
        3. righe modificate
      3. bisogna trasferire i dati in modo da essere consistente con i constraint

    .

    Se descrivi MEGLIO quello che devi fare, ti si puo' aiutare meglio.

    Diciamo che e' meglio cercare tool GIA' PRONTI (ESISTONO), che implementarlo da ZERO. 
    Spesso sono funzionalita' A PAGAMENTO disponibili in tool quali “Aqua Data Studio”, “DataGrip”, “DBeaver” ecc…

    Un'alternativa e' implementare DUE tool:

    1. uno che dal database salva su filesystem
    2. uno che dal filesystem, carica il database

    .

    e su filesystem salvi le informazioni in un formato “indipendente” dal DBMS usato, ma sufficientemente dettagliato da permettere il mapping dei tipi di colonne per OGNI DBMS. In questo modo puoi fare TUTTE le conversioni che ti servono.

    .

    Nota: NON TI SERVE descrivere la struttura del database A MANO! 
    OGNI DBMS ha un sistema per conoscere l'elenco del database, per ogni database l'elenco delle tabelle, per ogni tabella l'elenco delle colonne, il loro tipo, gli indici, le dipendenze, i constrain, e N-MILA altre informazioni.

  • Re: Sincronizzazione struttura tabelle database

    Uhm… chi deve sincronizzarsi a chi?

    Usare più database non è una scelta furba.

    Puoi esportare i dati in formato json, xml, csv o come ti viene più comodo, per poi fare l'import aggiungendo quello che serve al db di destinazione (indici, tabelle collegate ecc…).

    02/05/2024 - LC.marco ha scritto:


    . ho un problema nella lettura del formato dei campi da un tipo di database e la creazione dei campi nella tabella del database di destinazione che è di un formato diverso

    Crei l'xml a seconda del db di destinazione…

    Pensa se l'ade dovesse avere un db per ogni gestionale che invia fatture elettroniche…

  • Re: Sincronizzazione struttura tabelle database

    Diciamo anche che i vari DBMS “SERI” hanno meccanismi GIA' PRONTI per clonare database, gestire piu' istanze, creare cluster, ecc ..

    Per non parlare dei meccanismi di “federazione” (accesso ad una tabella di un'altro database memorizzato in un DBMS che sta' da tutt'altra parte del mondo).

    Insomma, ci sono N-MILA alternative, SE necessario !

  • Re: Sincronizzazione struttura tabelle database

    02/05/2024 - sihsandrea ha scritto:


    Uhm… chi deve sincronizzarsi a chi?

    Usare più database non è una scelta furba.

    Puoi esportare i dati in formato json, xml, csv o come ti viene più comodo, per poi fare l'import aggiungendo quello che serve al db di destinazione (indici, tabelle collegate ecc…).

    02/05/2024 - LC.marco ha scritto:


    . ho un problema nella lettura del formato dei campi da un tipo di database e la creazione dei campi nella tabella del database di destinazione che è di un formato diverso

    Crei l'xml a seconda del db di destinazione…

    Pensa se l'ade dovesse avere un db per ogni gestionale che invia fatture elettroniche…

    "Usare più database non è una scelta furba.": capisco e condivido, ma senza conoscere il perchè mi sembra una frase buttata li, comunque sono più database perchè sono gestionali differenti e alcuni non sono prediposti per andare online mentre alcuni dei loro dati serve che siano visti anche da soluzioni web ecco il motivo principale di sincronizzarli con ad esempio un database mysql.

    Ti chiedo se possibile di spiegarmi meglio il discorso xml, l'ade ha un suo formato e tutti si adeguano, non capisco quale sia il nesso essendo nel mio caso un necessità dovuta al cercare di gestire dati di soluzioni software diverse e fatte da terzi che devo cercare di far confluire per averne accesso altrove.

  • Re: Sincronizzazione struttura tabelle database

    02/05/2024 - migliorabile ha scritto:


    Diciamo anche che i vari DBMS “SERI” hanno meccanismi GIA' PRONTI per clonare database, gestire piu' istanze, creare cluster, ecc ..

    Per non parlare dei meccanismi di “federazione” (accesso ad una tabella di un'altro database memorizzato in un DBMS che sta' da tutt'altra parte del mondo).

    Insomma, ci sono N-MILA alternative, SE necessario !

    ok proverò a documentarmi meglio la ringrazio, se ha qualche link per approfondire ancora meglio

  • Re: Sincronizzazione struttura tabelle database

    Mi sa che stai facendo un ‘plpettone’

    I dati gestiti da un DBMS ed il DMBS stesso non c'entrano una cippa con il fatto di essere utilizzati da applicazioni standalone oppure web

    Non hai ancora spiegato PERCHE' vuoi accedere a dati memorizzati in DBMS diversi E se conosci O MENO la struttura/semantica dei dati memorizzati, SE questi database, la loro struttura e i software che li utilizzano sono sotto il TUO TOTALE controllo oppure sono database gestiti da software terzi su cui non puoi intervenire a livello di SORGENTE

    Insomma, ti stai imbarcando in un'attività che e' complessa ANCHE  per coloro che fanno quel tipo di attività PER MESTIERE.

    In generale l'operazione PRIMA da fare e' far sì che tutti i software coinvolti usino lo STESSO tipo di dbms: MySQL oppure SQL Server o Oracle, DB2, ecc

    Solo DOPO puoi usare i concetti di cui ho scritto prima. 

    Lavorare con DBMS eterogenei si può fare ma è DECISAMENTE COMPLICATO. 

  • Re: Sincronizzazione struttura tabelle database

    Ciao Marco e benvenuto tra noi (sono Sergio, quello che ti ha invitato su questo forum)
    Come hai già letto sui post precedenti dovresti spiegare meglio cosa ti serve.
    Se, come dici tu, hai gestionali diversi, copiare pari-pari le tabelle ed i dati da un gestionale all'altro sicuramente non funziona poichè tutti i gestionali hanno strutture di database diverse, tabelle con colonne diverse, nomi di tabelle e di colonne diverse, per cui impensabile portare i dati da un gestionale all'altro in modo automatico.
    Bisogna fare un programma ad hoc che trasferisce i dati, ma bisogna sapere esattamente la struttura di uno e dell'altro.

    Sono gestionali fatti tutti da te oppure sono gestionali di fornitori diversi ??

    Devi solo creare le tabelle e metterci dentro i dati, o devi creare anche eventuali relazioni tra le tabelle e/o indici ???

Devi accedere o registrarti per scrivere nel forum
7 risposte