Struktur / ForEach

Anfängerfragen zum Programmieren mit PureBasic.
Benutzeravatar
Kiffi
Beiträge: 10711
Registriert: 08.09.2004 08:21
Wohnort: Amphibios 9

Beitrag von Kiffi »

so, hier mal mein erster Versuch. Ich denke, es ist ausreichend
kommentiert. Falls Fragen sind -> immer her damit.

(benötigt PB4.3 und die ExDataBase-Lib von Michael)

Code: Alles auswählen

EnableExplicit

UseODBCDatabase()

Procedure CompareDatabases(Database1.s, Database2.s)
  
  Protected DBDSN1.s, DBDSN2.s
  Protected DB1, DB2
  Protected ColCounter
  Protected Fieldnames.s, Query.s
  
  Structure s_Info
    Name.s
    IsIn.l
  EndStructure
  
  NewList Tableinfo1.s_Info()
  NewList Tableinfo2.s_Info()
  
  NewList ColumnInfo1.s_Info()
  NewList ColumnInfo2.s_Info()
  
  NewList Record1.s()
  NewList Record2.s()
  
  ; DB1 -> Tabellennamen auslesen
  
  DBDSN1 = AddDSN(Database1)
  
  If ExamineTables(DBDSN1)
    While NextTable()
      If GetTableType() = "TABLE"
        AddElement(Tableinfo1())
        Tableinfo1()\Name = GetTableName()
      EndIf
    Wend
  EndIf 
  
  ; DB2 -> Tabellennamen auslesen
  
  DBDSN2 = AddDSN(Database2)
  
  If ExamineTables(DBDSN2)
    While NextTable()
      If GetTableType() = "TABLE"
        AddElement(Tableinfo2())
        Tableinfo2()\Name = GetTableName()
      EndIf
    Wend
  EndIf 
  
  ; Vergleich Tabellen DB1 <-> DB2
  
  ForEach Tableinfo1()
    ForEach Tableinfo2()
      If Tableinfo1()\Name = Tableinfo2()\Name
        Tableinfo1()\IsIn=#True
        Tableinfo2()\IsIn=#True
        Break
      EndIf
    Next
  Next
  
  ; Ausgabe: welche Tabellen sind nicht vorhanden
  
  ForEach Tableinfo1()
    If Not Tableinfo1()\IsIn
      Debug "Tabelle in DB1, aber nicht in DB2: " + Tableinfo1()\Name
      DeleteElement(Tableinfo1()) ; nicht vorhandene Tabellen werden entfernt
    EndIf
  Next
  
  ForEach Tableinfo2()
    If Not Tableinfo2()\IsIn
      Debug "Tabelle in DB2, aber nicht in DB1: " + Tableinfo2()\Name
      DeleteElement(Tableinfo2()) ; nicht vorhandene Tabellen werden entfernt
    EndIf
  Next
  
  ; Spaltennamen vergleichen
  ; Dateninhalte vergleichen
  
  DB1 = OpenDatabase(#PB_Any, DBDSN1, "", "", #PB_Database_ODBC)
  If DB1
    
    DB2 = OpenDatabase(#PB_Any, DBDSN2, "", "", #PB_Database_ODBC)
    If DB2
      
      ForEach Tableinfo1() ; Tableinfo1 und Tableinfo2 sind nun synchron
        
        ClearList(ColumnInfo1())
        ClearList(ColumnInfo2())
        
        ; Spaltennamen Tabelle X in DB 1 auslesen
        
        If DatabaseQuery(DB1, "Select * From [" + Tableinfo1()\Name + "] Where 1=2")
          NextDatabaseRow(DB1)
          For ColCounter = 1 To DatabaseColumns(DB1)
            AddElement(ColumnInfo1())
            ColumnInfo1()\Name = DatabaseColumnName(DB1, ColCounter - 1)
          Next
          FinishDatabaseQuery(DB1)
        EndIf
        
        ; Spaltennamen Tabelle X in DB 2 auslesen
        
        If DatabaseQuery(DB2, "Select * From [" + Tableinfo1()\Name + "] Where 1=2")
          NextDatabaseRow(DB2)
          For ColCounter = 1 To DatabaseColumns(DB2)
            AddElement(ColumnInfo2())
            ColumnInfo2()\Name = DatabaseColumnName(DB2, ColCounter - 1)
          Next
          FinishDatabaseQuery(DB2)
        EndIf
        
        ; Vergleich Spalten Tabelle X in DB1 <-> Tabelle X in DB2
        
        ForEach ColumnInfo1()
          ForEach ColumnInfo2()
            If ColumnInfo1()\Name = ColumnInfo2()\Name
              ColumnInfo1()\IsIn=#True
              ColumnInfo2()\IsIn=#True
              Break
            EndIf
          Next
        Next
        
        ; Ausgabe: welche Spalten sind nicht vorhanden
        
        ForEach ColumnInfo1()
          If Not ColumnInfo1()\IsIn
            Debug "Spalte in Tabelle " + Tableinfo1()\Name + " in DB1, aber nicht in DB2: " + ColumnInfo1()\Name
            DeleteElement(ColumnInfo1()) ; nicht vorhandene Spalten werden entfernt
          EndIf
        Next
        
        ForEach ColumnInfo2()
          If Not ColumnInfo2()\IsIn
            Debug "Spalte in Tabelle " + Tableinfo1()\Name + " in DB2, aber nicht in DB1: " + ColumnInfo2()\Name
            DeleteElement(ColumnInfo2()) ; nicht vorhandene Spalten werden entfernt
          EndIf
        Next
        
        ; Nun Zeileninhalte miteinander vergleichen
        
        ; Query zusammenbasteln        
        
        Fieldnames = ""
        
        ForEach ColumnInfo1() ; ColumnInfo1 und ColumnInfo2 sind nun synchron  
          Fieldnames + "[" + ColumnInfo1()\Name + "]"
          If ListIndex(ColumnInfo1()) < ListSize(ColumnInfo1()) - 1
            Fieldnames + ", "            
          EndIf
        Next
        
        Query = "Select " + Fieldnames + " From " + Tableinfo1()\Name + " Order By " + Fieldnames
        
        ClearList(Record1())
        ClearList(Record2())
        
        If DatabaseQuery(DB1, Query)
          While NextDatabaseRow(DB1)
            AddElement(Record1())
            ForEach ColumnInfo1() ; ColumnInfo1 und ColumnInfo2 sind nun synchron  
              Record1() + GetDatabaseString(DB1, ListIndex(ColumnInfo1()))
              If ListIndex(ColumnInfo1()) < ListSize(ColumnInfo1()) - 1
                Record1() + Chr(10)
              EndIf
            Next
          Wend
          FinishDatabaseQuery(DB1)
        EndIf
        
        If DatabaseQuery(DB2, Query)
          While NextDatabaseRow(DB2)
            AddElement(Record2())
            ForEach ColumnInfo1() ; ColumnInfo1 und ColumnInfo2 sind nun synchron  
              Record2() + GetDatabaseString(DB2, ListIndex(ColumnInfo1()))
              If ListIndex(ColumnInfo1()) < ListSize(ColumnInfo1()) - 1
                Record2() + Chr(10)
              EndIf
            Next
          Wend
          FinishDatabaseQuery(DB2)
        EndIf
        
        If ListSize(Record1()) <> ListSize(Record2())
          
          ; todo
          ; anzahl datensätze unterschiedlich
          ; vergleich theoretisch möglich
          
        Else
          
          ForEach Record1()
            
            SelectElement(Record2(), ListIndex(Record1()))
            
            If Record1() <> Record2()
              
              ForEach ColumnInfo1() 
                
                Protected Field1.s = StringField(Record1(), ListIndex(ColumnInfo1()) + 1, Chr(10))
                Protected Field2.s = StringField(Record2(), ListIndex(ColumnInfo1()) + 1, Chr(10))
                
                If Field1 <> Field2
                  Debug "Tabelle: " + Tableinfo1()\Name + " / Spalte: " + ColumnInfo1()\Name
                  Debug Field1 + " / " + Field2
                  Debug ""
                EndIf
                
              Next ; ForEach ColumnInfo1() 
              
            EndIf ; If Record1() <> Record2()
            
          Next ; ForEach Record1()
          
        EndIf
        
      Next
      
      CloseDatabase(DB2)
      
    Else
      
      Debug "Konnte " + Database1 + " nicht öffnen"
      
    EndIf
    
    CloseDatabase(DB1)
    
  Else
    
    Debug "Konnte " + Database2 + " nicht öffnen"
    
  EndIf
  
  ; Schlussendlich die DSN wieder entfernen
  
  RemoveDSN(Database2)
  RemoveDSN(Database1)
  
EndProcedure

CompareDatabases("D:\db1.mdb", "D:\db2.mdb")
Grüße ... Kiffi

// Edit: Code korrigiert. Verschobene Spalten sollten nun nicht mehr vorkommen.
Zuletzt geändert von Kiffi am 11.01.2009 22:32, insgesamt 1-mal geändert.
a²+b²=mc²
TheSaint
Beiträge: 143
Registriert: 21.12.2008 18:59

Beitrag von TheSaint »

Hallo Kiffi,

wow, wahnsinn.
Super vielen Dank für Deine Antwort und den Code.
Ich werde mich gleich dran setzen und durcharbeiten.

Nochmals vielen Dank und viele Grüße
Benutzeravatar
Kiffi
Beiträge: 10711
Registriert: 08.09.2004 08:21
Wohnort: Amphibios 9

Beitrag von Kiffi »

@TheSaint: Funktioniert die Routine denn so, wie Du Dir das vorgestellt
hast? Wann ja: Mich würde auch interessieren, wie die Geschwindigkeit
gegenüber Deiner Routine aussieht.

Grüße ... Kiffi
a²+b²=mc²
TheSaint
Beiträge: 143
Registriert: 21.12.2008 18:59

Beitrag von TheSaint »

Hallo Kiffi,

ich habe jetzt zum x-gsten mal versucht Dein Code zum laufen zu bekommen.

Jedoch habe ich jetzt herausgefunden, woran es liegt, dass es bei mir nicht funktioniert, und zwar:

Code: Alles auswählen

  ==> DBDSN1 = AddDSN(dbName0, uId, Passw, "", client)
 
  If ExamineTables(DBDSN1)
    While NextTable()
      If GetTableType() = "TABLE"
        AddElement(Tableinfo1())
        Tableinfo1()\Name = GetTableName()
      EndIf
    Wend
  EndIf
 
  ; DB2 -> Tabellennamen auslesen
 
  ==> DBDSN2 = AddDSN(dbName1, uId, Passw, "", client) ; DSN hinzufügen
ergibt das folgendes Problem:

In der Systemsteuerung / Verwaltung / Datenquellen ODBC / Benutzer DSN (oder ich habs auch unter System DSN versucht)

erscheint immer: "PureBasic_Compilation2_CASA70"

DBDSN1 = "PureBasic_Compilation2_CASA70"
DBDSN2 = "PureBasic_Compilation2_CASA70"

Es müsste aber:

DBDSN1 = "PureBasic_Compilation2_CASA70"
DBDSN2 = "PureBasic_Compilation3_CASA70" erscheinen.

Das heißt zwei DSN müssten angelegt werden in der ODBC Verwaltung.

Das bedeutet, nach dem zweiten AddDSN ist in der Zeile:
Database immer die letzte Quelle des Datenbankpfades erfasst.

Es müsste aber zwei DSN angelegt werden.

Hast du das Problem auch???

Hoffe ich konnte das verständlich rüberbringen.

Schon mal vielen Dank für Deine Hilfe.

Viele Grüße
Benutzeravatar
Kiffi
Beiträge: 10711
Registriert: 08.09.2004 08:21
Wohnort: Amphibios 9

Beitrag von Kiffi »

TheSaint hat geschrieben:Hast du das Problem auch???
nee, bei mir wird Purebasic0_DB1 und Purebasic0_DB2 angelegt (sonst
hätte ich den Code ja nicht schreiben können ;-)).

Du verwendest die neueste Version von ExDatabase? Du hast
ausreichende Rechte? Die Datenbanken liegen lokal auf Deiner Platte?

Die ExDatabase liegt ja auch im Source vor. Du kannst also die
ExDatabase-Lib aus Deinem Userlibraries-Ordner an einen anderen Ort
verschieben, den Compiler neustarten und danach mit XInclude den
ExDatabase-Source in Deinen Code einbinden. Dann kannst Du auch
testen, woran es scheitert.

Ich sehe grade: Du willst Firebird-Datenbanken vergleichen? Ich bin
davon ausgegangen, dass Du mit Access-MDB arbeitest. Für andere DBs
kann ich nicht garantieren, dass mein Code funktioniert.

Grüße ... Kiffi
a²+b²=mc²
TheSaint
Beiträge: 143
Registriert: 21.12.2008 18:59

Beitrag von TheSaint »

Hallo Kiffi,

hm, das Problem mit den DSN bleibt bestehen, wenn ich auf
die Firebird Datenbank zugreifen will.
Dein Code wird mit sicherheit super funktionieren,
aber die Pbosl ExDatabase scheint ein Problem zu
verursachen.
Oder irgendwas mache ich falsch.
Könnte man bei demjenigen nachfragen ob das
ein Problem in der Lib ist bei Firebird?

Könntest Du da noch mal helfen?

Vielen Dank und viele Grüße
Benutzeravatar
Kiffi
Beiträge: 10711
Registriert: 08.09.2004 08:21
Wohnort: Amphibios 9

Beitrag von Kiffi »

Du kannst das ja selber relativ einfach kontrollieren:
Kiffi hat geschrieben:Die ExDatabase liegt ja auch im Source vor. Du kannst also die
ExDatabase-Lib aus Deinem Userlibraries-Ordner an einen anderen Ort
verschieben, den Compiler neustarten und danach mit XInclude den
ExDatabase-Source in Deinen Code einbinden. Dann kannst Du auch
testen, woran es scheitert.
;-)

Kennst Du Dich mit dem Debugger aus?

Grüße ... Kiffi
a²+b²=mc²
TheSaint
Beiträge: 143
Registriert: 21.12.2008 18:59

Beitrag von TheSaint »

Hallo Kiffi,

danke für Deine Nachricht.
Du verwendest die neueste Version von ExDatabase? Du hast
ausreichende Rechte? Die Datenbanken liegen lokal auf Deiner Platte?
Ja zu allen Fragen :)
Du kannst das ja selber relativ einfach kontrollieren:
Kiffi hat Folgendes geschrieben:
Die ExDatabase liegt ja auch im Source vor. Du kannst also die
ExDatabase-Lib aus Deinem Userlibraries-Ordner an einen anderen Ort
verschieben, den Compiler neustarten und danach mit XInclude den
ExDatabase-Source in Deinen Code einbinden. Dann kannst Du auch
testen, woran es scheitert.

Wink

Kennst Du Dich mit dem Debugger aus?
Hm, muss mal sehen, wie ich das mit der Kontrolle hinkriege...
Weis noch nicht so genau.
Werde das mal probieren, aber wie krieg ich raus wo die Lib nicht
funktioniert? So gut kenn ich mich mit Libs nicht aus.

Ich versuchs mal.

Schon mal vielen Dank und viele Grüße
Benutzeravatar
Kiffi
Beiträge: 10711
Registriert: 08.09.2004 08:21
Wohnort: Amphibios 9

Beitrag von Kiffi »

TheSaint hat geschrieben:Werde das mal probieren, aber wie krieg ich raus wo die Lib nicht
funktioniert? So gut kenn ich mich mit Libs nicht aus.
Diese Lib ist nichts anderes, als ein PureBasic-Code, der mit einem
speziellen Programm (Tailbite) 'kompiliert' wurde. Der Source also liegt in
einem für Dich verständlichen Format vor. Du brauchst Dir also nur die
Procedure Add_DSN() in der Datei PBOSL_ExDatabase.pb genauer anzuschauen.

Grüße ... Kiffi
a²+b²=mc²
TheSaint
Beiträge: 143
Registriert: 21.12.2008 18:59

Beitrag von TheSaint »

Hallo Kiffi,

ich werde es mal versuchen.

Viele Grüße
Antworten