DataTabels und 400.000 Datensätze

  • VB.NET

Es gibt 4 Antworten in diesem Thema. Der letzte Beitrag () ist von CaMeO.

    DataTabels und 400.000 Datensätze

    Hallo allerseits
    Ich habe bisher in Access einige umfangreiche Projekte erstellt und kann behaupten, dass ich mich in VBA als fortgeschrittenen Progger bezeichnen kann.
    Nun habe ich mich aus naheliegenden Gründen entschlossen, den Umstieg zu VB zu wagen.
    Es hat zwar einige Tage gedauert aber einen Schnell-Überblick konnte ich mir durch Openbooks und diesem Forum verschaffen.
    Als erstes Projekt habe ich natürlich eines gewählt, das ich in Access-VBA schon abgeschlossen hatte.

    Die Projekt-Aufgabe:
    Mit Mess-Geräten werden an elektrischen Anlagen verschiedene Messungen durchgeführt.
    Diese Mess-Ergebnisse werden dann vom Gerät im Computer als Mess-Datei abgelegt.
    Anschliessend soll mit der Projekt-Software diese Dateien importiert und ausgewertet werden.
    Und so befinden sich mittlerweile ca. 400.000 Mess-Ergebnisse in der Access-Datenbank.
    Grob beschrieben ist jede Messung in folgenden Feldern hinterlegt:
    Periode, Objekt, Verteiler, Gerät, Mess-Art und ca. 20 Felder mit den Mess-Ergebnissen.

    Der Benutzer der Software kann schlussendlich jede Messung betrachten und div.Befunde erstellen.
    In Access sah das aus wie in Image1.jpg

    In VB zeige ich die Daten in DataGridViews. Und da hatte ich schon beim Füllen der Listen mein erstes Problem.
    Mit folgendem Code fülle ich die erste Liste:
    (Gleich mal vorweg: Ich weiss, dass meine Benennung von Variablen nicht den üblichen Regeln entspricht...aber das sei mal meine 100. Sorge ;)

    Quellcode

    1. Public Sub ListsUpdate(ByVal vListID As fList)
    2. Using DBcon As New OleDbConnection, DBcmd As New OleDbCommand
    3. Dim DBreader As OleDbDataReader
    4. Dim DBreaderCount As Long
    5. DBcon.ConnectionString = DBsource
    6. DBcmd.Connection = DBcon
    7. DBreaderCount = 0
    8. Try
    9. DBcon.Open()
    10. If vListID = fList.fAll Or vListID = fList.fPeriode Then
    11. DBcmd.CommandText = "SELECT ResPeriode FROM tResult GROUP BY ResPeriode ORDER BY ResPeriode"
    12. Form1.fListPeriode.Rows.Clear()
    13. Form1.fListPeriode.ClearSelection()
    14. vListFillInProgress = True
    15. DBreader = DBcmd.ExecuteReader()
    16. Do While DBreader.Read()
    17. Form1.fListPeriode.Rows.Add(DBreader("ResPeriode"))
    18. DBreaderCount += 1
    19. Loop
    20. DBreader.Close()
    21. vListFillInProgress = False
    22. If DBreaderCount > 0 Then
    23. Form1.fListPeriode.ClearSelection()
    24. End If
    25. DBreaderCount = 0
    26. vPeriode = vbNullString
    27. vListID = fList.fAll
    28. End If
    29. If vListID = fList.fAll Or vListID = fList.fObject Then
    30. Form1.fListObject.Rows.Clear()
    31. vListFillInProgress = True
    32. If vPeriode <> vbNullString Then
    33. DBcmd.CommandText = "SELECT tResult.ResObject, IIf([FileIsInfo],'Info','') AS Info, tFile.FileIsInfo, tResult.ResFileID FROM tFile INNER JOIN tResult ON tFile.FileID = tResult.ResFileID WHERE (((tResult.ResPeriode)='" & vPeriode & "')) GROUP BY tResult.ResObject, IIf([FileIsInfo],'Info',''), tFile.FileIsInfo, tResult.ResFileID ORDER BY tResult.ResObject"
    34. DBreader = DBcmd.ExecuteReader()
    35. Do While DBreader.Read()
    36. Form1.fListObject.Rows.Add(DBreader("ResObject"), DBreader("Info"), DBreader("FileIsInfo"), DBreader("ResFileID"))
    37. DBreaderCount += 1
    38. Loop
    39. DBreader.Close()
    40. End If
    41. vListFillInProgress = False
    42. If DBreaderCount > 0 Then
    43. Form1.fListObject.ClearSelection()
    44. End If
    45. DBreaderCount = 0
    46. vObject = vbNullString
    47. vListID = fList.fAll
    48. End If
    49. If vListID = fList.fAll Or vListID = fList.fMesCat Then
    50. Form1.fListMesCat.Rows.Clear()
    51. vListFillInProgress = True
    52. If vPeriode <> vbNullString Then
    53. DBcmd.CommandText = "SELECT tMessureCategory.MesCatDesc, tResult.ResMesCatID FROM tMessureCategory INNER JOIN tResult ON tMessureCategory.MesCatID = tResult.ResMesCatID WHERE tResult.ResPeriode = '" & vPeriode & "' And tResult.ResObject = '" & vObject & "' GROUP BY tResult.ResMesCatID, tMessureCategory.MesCatDesc ORDER BY tResult.ResMesCatID"
    54. DBreader = DBcmd.ExecuteReader()
    55. Do While DBreader.Read()
    56. Form1.fListMesCat.Rows.Add(DBreader("MesCatDesc"), DBreader("ResMesCatID"))
    57. DBreaderCount += 1
    58. Loop
    59. DBreader.Close()
    60. End If
    61. vListFillInProgress = False
    62. If DBreaderCount > 0 Then
    63. Form1.fListMesCat.ClearSelection()
    64. End If
    65. DBreaderCount = 0
    66. vMesCatID = vbNullString
    67. vListID = fList.fAll
    68. End If
    69. If vListID = fList.fAll Or vListID = fList.fBlock Then
    70. Form1.fListBlock.Rows.Clear()
    71. vListFillInProgress = True
    72. If vPeriode <> vbNullString Then
    73. DBcmd.CommandText = "SELECT tResult.ResBlock FROM tResult WHERE tResult.ResPeriode = '" & vPeriode & "' And tResult.ResObject = '" & vObject & "' And tResult.ResMesCatID = " & vMesCatID & " GROUP BY tResult.ResBlock ORDER BY tResult.ResBlock"
    74. DBreader = DBcmd.ExecuteReader()
    75. Do While DBreader.Read()
    76. Form1.fListBlock.Rows.Add(DBreader("ResBlock"))
    77. DBreaderCount += 1
    78. Loop
    79. DBreader.Close()
    80. End If
    81. vListFillInProgress = False
    82. If DBreaderCount > 0 Then
    83. Form1.fListBlock.ClearSelection()
    84. End If
    85. DBreaderCount = 0
    86. vMesCatID = vbNullString
    87. vListID = fList.fAll
    88. End If
    89. Catch ex As Exception
    90. MessageBox.Show(ex.Message)
    91. End Try
    92. End Using
    93. End Sub

    Wenn die erste Zeile ins DataGridView geschrieben wird, dann wird diese von VB sofort markiert und natürlich das Ereignis ".CellEnter" ausgelöst.
    Bei Schreiben der 2.Zeile wird dieses Ereignis dann nicht mehr ausgeführt...logisch.
    Und das will ich nicht. Der Code soll erst alle Zeilen schreiben. Und dann auf die Auswahl des Nutzers warten.
    Erst danach soll die nächste Liste auf Grund der Auswahl des Nutzers gefüllt werden. Und so solls dann Liste für Liste weitergehen...bis ich zum einzelnen Mess-Ergebnis gelange.
    Die Variable "vListFillInProgress" nutze ich als Notbehelf nur, damit diese Ereignis-Prozedur nichts tut, bis die Liste gefüllt ist.
    Und hier gleich die erste Frage: Was mach ich da falsch?

    2.Frage
    Wie man sieht arbeite ich noch mit dauerndem Direktzugriff auf die Datenbank
    Der Test-Versuch alle Daten in DataTable zu speichern und danach zu filtern gelang einwandfrei
    aber der ".Fill"-Prozess dauert gegenüber dem Direktzugriff schweinelangsam (ca. 15 Sekunden) und das ist inakzeptabel.
    Die Daten je nach Benutzer-Auwahl gefiltert in DataTables zu holen ist sinnlos, weil nach der nächsten Benutzer-Auswahl die Daten neu geholt werden müssen.
    1.Auswahl: Nur die Perioden (Group by Periode)
    2.Auswahl: Befunde der entsprechenden Periode (Group by Periode, Group by Befund)
    3.Auswahl: Objekte der entsprechenden Periode, Befundes......
    Also müsste ich für schnellere Anzeige schon alle Daten im Speicher haben um dann mal schnell filtern zu können...Idee nicht schlecht...aber das Füllen von 400.000 Datensätzen (und noch zu übers Netzwerk) dauert zu lange.

    Hier der Test-Code zum Füllen der Periode 2012 (wenn ich alle Datensätze holen müsste, dann dauerts ewig):

    VB.NET-Quellcode

    1. Public Sub DataRead()
    2. Using DBcon As New OleDbConnection, DBcmd As New OleDbCommand
    3. Dim vSQL As String
    4. vSQL = "SELECT *"
    5. vSQL &= ", tObject.ObjDesc"
    6. vSQL &= ", tMessureArt.MesArtDesc"
    7. vSQL &= ", tMessureTyp.MesTypDesc"
    8. vSQL &= ", tHardware.HardDesc"
    9. vSQL &= ", tHardware.HardTyp"
    10. vSQL &= " FROM tHardware RIGHT JOIN ((tMessureTyp RIGHT JOIN (tResultSys RIGHT JOIN (tSuccess RIGHT JOIN (tMessureCategory INNER JOIN (tMessureArt RIGHT JOIN tResult ON tMessureArt.MesArtID = tResult.ResMesArtID) ON tMessureCategory.MesCatID = tResult.ResMesCatID) ON tSuccess.SucID = tResult.ResSucID) ON tResultSys.ResSysID = tResult.ResSYSID) ON tMessureTyp.MesTypID = tResult.ResMesTypID) LEFT JOIN tObject ON tResult.ResObject = tObject.ObjID) ON tHardware.HardID = tResult.ResHardID"
    11. vSQL &= " WHERE ResPeriode = '2012';"
    12. DBcon.ConnectionString = DBsource
    13. DBcmd.Connection = DBcon
    14. DBcon.Open()
    15. DBcmd.CommandText = vSQL
    16. DBdataAdapter.SelectCommand = DBcmd
    17. DBdataAdapter.Fill(DbdataSet)
    18. End Using
    19. End Sub

    Da ich mich als Neuling in VB verstehe, ist meine 2.Frage:
    Geh ich das falsch an?

    Verzeiht mir bitte einen ev. Hausmeister-Programmierstil aber ich bin erst ein paar Tage in diese Liga aufgestiegen und gelobe Besserung!

    mfG, Ludwig aka Wickal
    Bilder
    • Image1.jpg

      90,21 kB, 1.024×545, 69 mal angesehen
    Datenbänkerei-Einstieg

    Ich sach immer, bevor man eine DB anfasst, soll man erstmal typisiertes Dataset und Databinding kennengelernt haben - bis hin zum m:n - View.
    Weil sonst gewöhnt man sich einen Zugriffs-Stil an, der einem die Möglichkeiten von typisiertem Dataset und Databinding von vornherein verbaut.

    Ist jetzt ziemlich ungeschickt mit deiner fetten DB im Internet, denn um sowas zu handeln, muß man mit dem genannten schon wirklich gut klarkommen.
    Meine Beispiele beziehen sich immer auf sehr kleine Test-Datenbanken, die man problemlos auch komplett in den Arbeitsspeicher laden könnte.
    Eine Anwendung so zu designen, dass immer nur ein sinnvoller Ausschnitt geladen wird, zeige ich nur ansatzweise, etwa in der Northwind-Solution in DBExtensions

    Also fürn Ansatz zur Datenbänkerei mittm typDataset gugge "Datenbank in 10 Minuten" auf Movie-Tuts

    Achso - Buch - vmtl. bist du dem Galileio-Teil aufgesessen - dassis ziemlich mies, besonders der DB-Teil. Lieber dieses Buch lesen (hingegen das Galileio-Openbook ist Mist)
    Danke für die rasche Antwort
    Zum Verständnis:
    Die Daten befinden sich nicht im Internet sondern im Netzwerk einer großen Firma in einer Access-accdb (nach erfolgreicher Testphase soll sie in einen SQL-Server transferiert werden)
    Das Design (Realtions) ist schon optimal gestaltet...schon um Speicherplatz zu sparen (IDs als Verweise auf Detail-Tabellen), etc.
    Die Daten haben also einen "fixen Standort" aber das Frontend kann auf zig-Rechnern in nicht definierten Ordner installiert sein.
    Bis jetzt ist das Frontend eben auch eine Access.accdb und das funzt schon sehr gut...ist aber eben nich secure und anfällig auf Manipulation der Messwerte (die sind bei uns heilig).
    Ich bin zum jetzigen Zeitpunkt in der sog. Lernphase was die VB-Geschwindigkeit in der Verarbeitung bzw. Anzeige von Daten aus Datenbanken betrifft.
    Bei Access "arbeitet" man bestensfalls mit ADO und das wars auch schon.
    Dann starte ich mit Visual Basic 2010 und muss feststellen, dass es da schon viel "umständlicher" zur Sache geht.
    Obwohl man auch hier mit ADODB.Recordsets arbeiten kann, meidet jeder VB-Progger diesen Zugriff wie der Teufel das Weihwasser, weil Datasets (ADO.NET) sooo viel besser sein sollen. Also gut...wird ja was dran sein.
    Aber zur Zeit bin ich zum Vergleich mit Access eher enttäuscht. Das liegt mit Sicherheit auch an der Tatsache, dass ich ein VB-Azubi bin.

    Daher der Hintergrund meines Postings:
    Bevor ich mich da voll in Datasets reinhänge und dann feststellen muss, dass ich mit meinem ADODB besser dran wäre,
    frage ich mal nach ob ich was falsch verstehe.
    Die o.a. Datenbank hat etwa 40 MB und ich will natürlich die Netzlast so gering wie möglich halten (was bei Access ned möglich ist), was bei ADO.NET ja der Grundgedanke zu sein scheint. Aber wenn man zum Programmstart schon mal 10 Sekunden und mehr warten muss, bis das Teil reagiert, dann isses sinnlos.
    Aber vielleicht ist das einfach so...wenn ich mir die Software "ExtremMovieManager" ansehe (die auch mir Access-Daten arbeitet), dann dauert auch dort der Startvorgang mit meinen 4000 Filmen ewig.
    Also dann pfeif ich doch eher auf die Netzlast...Hauptsache die Daten werden schnell angezeigt.
    Wie angedeutet: Ich muss Daten nur anzeigen und nicht verändern und bis jetzt dauert die Anzeige der ersten Liste (mit den Group-Jahren 4x länder als in Access)
    Mir gehts daher vorwiegend um den Speed (die Datenbank wird ja künftig immer grösser)
    Das Designen und der Code in VB gefällt mir natürlich wesentlich besser als VBA (is ja recht einfach zu erlernen) aber zur Zeit bremst meine Begeisterung immer diese verdammte Geschwindigkeit der Datenaufbereitung.

    Verzeiht mir bitte meine kleinen Romane...aber ich habe einfach zuviele Fragen auf zuwenig Antworten ;)
    Ich erinnere nochmal auf die Frage zum DataGridView und der automatischen Auswahl.

    thanx for reading

    Achja...das Galileio-Wissen habe ich mir nach Durchlesen diverser Threats rasch abgewöhnt und daher "Löffelmann" reingezogen. (ist auch in Sachen Hintergrund-Wissen besser)
    Wie gesagt: Problem ist, dass deine Anforderung schon sehr speziell ist, und erforderte, den typDataset-Ansatz bereits zu beherrschen (was du erst noch lernen müsstest).
    Ich zB könnte mir vorstellen, deine Query als View im Access anzulegen, und aus diesem View dann eine typDataTable zu generieren, mit gleich einem TableAdapter dran, der eine parametrisierte Funktion FillValuesByDateRange(dataset, date1, date2) bereitstellen würde.
    Ich muß zugeben, noch nie einen Access-View als Datenquelle genutzt zu haben, aber wenn das hinhaute, dann wären deine 94 Zeilen aus deim 1.Post reduziert auf im Grunde eine Zeile:

    VB.NET-Quellcode

    1. Me.ValueTableAdapter.FillByDateRange(Me.MeasureDataset, Date.Now - Timespan.FromHours(24), Date.Now)
    und der würde dir alle Datensätze der letzten 24h reinschaufeln.
    Ohne Flachs - eine Zeile Code.
    Das konfigurieren des TableAdapters, die Query inne Access.mdb, das daran gebundene Datagridview - das wäre alles in verschiedenen Designern und Assistenten zusammengeklickst.

    Aber dazu muss man die Technologie halt erstmal erlernen.
    Und es ist eine Spezial-Lösung, nämlich nicht rückspeicherbar. Für Standard-Probleme würde man wieder bisserl anners vorgehen, um sich die Option von Änderungen etc. offenzuhalten, die du hier ja nicht brauchst.