Code zur Anzeige von Daten aus DataSet optimieren (Ausführung dauert zu lange)

  • VB.NET
  • .NET (FX) 4.5–4.8

Es gibt 98 Antworten in diesem Thema. Der letzte Beitrag () ist von DerSmurf.

    Danke für deine Geduld mit mir :o)

    ErfinderDesRades schrieb:

    Hab ich das überhaupt richtig verstanden: die Sub WriteDataFirstYear() in dem Download arbeitet richtig, aber zu langsam.

    Das ist so ganz genau richtig. Das Ergebnis dieser Sub stimmt zu 100% - aber die Ausführungszeit ist einfach zu lange.
    Siehe Zeitmessungen Post#7 und Post#9

    Damit das für euch nachvollziehbar ist, und ihr nicht wie @VaporiZed was zusammenbasteln müsst, habe ich die Demo Solution hochgeladen
    Diese ist (mit wenigen Ausnahmen) eine 1 zu 1 Kopie der Originalform.
    Unterschiede: Im Originalprogramm wird die Form als Subform dargestellt (in der Demo gibt es ja nur eine)
    Die Demo hat DataSet speichern/laden Code. Die Originalform wird geschlossen, wenn es keine Daten zu Anzeige gibt.
    Hier habe ich in der Demo nur das entsprechende Me.Close auskommentiert, damit klar ist, warum ich an manchen Stellen im Code auf Prüfungen ob Daten vorhanden sind verzichte. Daher kommt die Meldung zu beginn, weils DataSet ja außer der 8 Warengruppen keine Daten hält.
    Damit die ganze DemoApp einen Sinn hat müssen natürlich noch Daten rein. Meine Originaldaten kann, bzw. möchte ich aber nicht mitliefern.
    Also habe ich rechts (unterhalb des Labels "Alles wie im OriginalProgramm...") den Button und die beiden Textboxen erstellt.
    Diese finden sich im Originalprogramm natürlich nicht.
    Mithilfe dieser sollten pseudodaten ins DataSet.
    Also Startdatum und die Anzahl der Tage soll eingetragen werden und dann entsprechend Daten ins DataSet geschrieben werden.
    Also für jeden Tag eine Gesamttageseinnahme und eine dazu zugeordnete Einnahme für jede Warengruppe.
    An der Erstellung diese Codes Sub BTNAddData_Click()(derwie gesagt nur dafür da ist pseudo Daten einzutragen, damit die Demo App Sinn hat) bin ich aber gescheitert.

    ErfinderDesRades schrieb:

    Die Fehlerzeile auskommentiert, dann startet das, und eine Messagebox sagt mir:

    Keine Ahnung was für einen Fehler - bei mir liefs Fehlerfrei.

    ErfinderDesRades schrieb:

    Nach Bestätigen geht ein Form auf:
    Was soll ich nun damit tun

    Das ist eine Kopie der originalForm, welche eben die fragliche Sub WriteDataFirstYear() ausführt.
    Ist quasi einfach nur, damit du eine Solution hast und nicht Code irgendwo verbesserst, sondern eben in der Solution.

    Du sagst - wenn ich recht versteh - Im Download die Sub WriteDataFirstYear() täte richtig arbeiten, aber zu langsam. Wie kriege ich die überhaupt ans arbeiten??

    Zwei Jahre auswählen (Jahr und Vergleichsjahr) und den Button Jahr betätigen.

    Aber wie gesagt, meine Demo Solution hat keine Daten, weil ich zu doof bin Code zu schreiben, der das macht.
    ich kann das Daten-Generieren dir nicht abnehmen.
    Theoretisch kann man zu jedem Datum beliebig viele IncomeRows anlegen, und jede dieser IncomeRow kann über die Distribution_Table mit allen ProductGroups verknüpft sein.
    Da gäbs zB für die Jahre 2010 - 2020 für jeden Tag 30 IncomeRows, und jede(!) dieser IncomeRow wäre mit mw. 5 ProductGroupRows verknüpft.
    Ich glaube aber nicht, dass das ein sinnvolles Daten-Szenario ist.

    Ich kann aber derzeit auch nicht nachvollziehen, warum du diese 3 Tabellen nicht herausgeben kannst. Die enthalten doch keinerlei Information, die man jemals irgendjemandem zuordnen kann.
    Tatsächlich ist einzig die spalte ProductGroup.Name vom typ String, also wo man überhaupt etwas lesen könnte.
    Und wenns nicht soo viele ProductGroups gibt, dann kannste deren Name ja sogar händisch anonymisieren.

    Also imo könntest du deine echten Daten als xml speichern (wenn nicht schon geschehen), und dann diese 3 Tabellen auskopieren in die Data.xml.
    Dann - innerhalb der Data.xml - alle ProductGroup.Names ändern und wäre keinem Datenschützer ein Leid geschehen.

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „ErfinderDesRades“ ()

    Huhu. Mir wäre aber ein Leid geschehen, wenn ich meine Tagesumsätze in einem Forum poste. Auch wenn ja keiner weiß, welche Firma das betriff und es dir wahrscheinlich auch einfach total egal ist, bin ich dafür wohl zu paranoid.
    Aber alles gut - ich kann zwar keine Schleife erstellen, die mir automatisch Daten ins DataSet schmeißt, aber ich bin ja immerhin in der Lage Textboxen automatisch zu beschreiben.
    Ich habe also flux einen DatenSatz per Hand erstellt. Mir tut der linke Zeigefinger etwas weh, weil ich 1.000 linksklicks ausgeführt habe - aber die Daten sind da :o) - und auch in der DemoApp hier drinne.
    Also die DemoSolution enthält nun einen vergleichbar großen DatenSatz wie die MainApp. Nur die Daten sind halt sinnbefreit, aber das ist ja egal.
    Es ist die gleiche Anzahl an Daten (ca.) und die Daten sind auf die gleiche Art (also mit der gleichen speichern Sub) ins DataSet gekommen, wie in der Main App.
    Ich habe jetzt auch Umsätze für Sonn- und Feiertage angelegt (aus Faulheitsgründen), aber das ist der Sub ja auch egal.

    ErfinderDesRades schrieb:

    Theoretisch kann man zu jedem Datum beliebig viele IncomeRows anlegen, und jede dieser IncomeRow kann über die Distribution_Table mit allen ProductGroups verknüpft sein.

    Es tut mir leid, aber aus diesem Grund habe ich eine neue DemoSulution hochgeladen.
    Diese enthält nun auch eine Form zum eintragen von Umsätzen (Button "Umsätze eintragen").
    Wenn du auf den Button "Umsätze anzeigen" klickst, landest du in der bekannten Form zur Anzeige. Diese habe ich natürlich nicht verändert (zur hochgeladenen Version von oben)
    Hier wählst du dann "Jahr" und "Vergleichsjahr" aus (Daten sind für die Jahre 2011 - 2013 drin) und klickst auf den Button Jahr. Dann startet die fragliche Sub WriteDataFirstYear()
    Natürlich sind Controls und Code des BEispielapp hier identisch mit meiner Hauptapp (die ich nicht hochlade, um nicht vom Thema abzulenken, denn hier ist noch sehr, sehr viel mehr Code enthalten)

    Ich bin mir zwar sicher, dass die Sub WriteDataFirstYear() verantwortlich ist, dass die Ausführung zu lange dauert. Denn diese Sub wird ja eben ausgeführt und sammelt die Daten zusammen.
    ABER! wenn meine Art die Daten ins DataSet zu schreiben schon Mist ist (oder das DataSet ansich) - bringt alle Verbesserung an der "AnzeigeSub" nichts. Außer dass die Scheiße vielleicht nicht so stinkt.
    Daher habe ich beides hier rangeschmissen. Denn es bringt uns beiden ja nix, wenn du mir die WriteDataFirstYear Sub verbesserst, aber das ganze immer noch schlecht ist, weile meine Art der Speicherung einfach doof ist.
    Um das zu beurtilen fehlen mir aber die Skilz.

    Und nochmal zusammenfassend:
    Spoiler anzeigen
    • Menge und Namen der Warengruppen sollen variabel sein. In meinem konkreten Fall sind - und bleiben - es 8 Stück
    • Für jeden Tag - in meinem Fall ab dem 03.03.2011 - soll eine Gesamt Tageseinnahme (Summe aller Warengruppeneinnahmen), sowie die Kundenzahl dieses Tages gespeichert werden
    • und für jeden Tag soll eine Warengruppeneinnahme gespeichert werden
    • die Daten sollen dann Monatsweise und Jahresweise angezeigt werden können (siehe Button "Umsätze anzeigen" in DemoApp), und jeweils mit einem auswählbaren Monat (oder eben Jahr) verglichen werden.
    also Beispiel 30.12.2011:
    Tageseinnahme: 80€ / Kundenzahl: 354
    Tageseinnahme Kategorie1: 10€
    Tageseinnahme Kategorie2: 10€
    usw. für jede Kategorie


    Dateien

    Dieser Beitrag wurde bereits 7 mal editiert, zuletzt von „DerSmurf“ ()

    Es ließe sich ein wenig rausholen, wenn Du Folgendes machst.

    VB.NET-Quellcode

    1. For i = 1 To 12
    2. DateSelected = Date.Parse("01." & i & "." & year)
    3. Dim MonthRows = DtsSettings.DailyIncome.Where(Function(x) x._Date.Month = i AndAlso x._Date.Year = year).OrderBy(Function(x) x._Date)
    4. 'Schleife für Row mit Einträgen des Monats
    5. For Each MonthRow In MonthRows
    6. 'Kunden speichern
    7. ArrCustomers(i - 1) += MonthRow.CustomerCount
    8. Dim RelevantDistributionRows = DtsSettings.Distribution_Table.Where(Function(x) x.DailyIncomeRow._Date = MonthRow._Date)
    9. For k = 0 To groupcount - 1 'Schleife durch Product Group DataTable
    10. 'Name aus BindingSource lesen
    11. Dim ProductGroup = DirectCast(DirectCast(ProductGroupBindingSource(k), DataRowView).Row, DtsSettings.ProductGroupRow)
    12. Groupname = ProductGroup.Name
    13. 'Warengruppenumsatz für Datum ermitteln und in Array addieren
    14. Dim ProductGroupRow = RelevantDistributionRows.SingleOrDefault(Function(x) x.ProductGroupRow.Name = Groupname)
    15. If ProductGroupRow Is Nothing Then Continue For
    16. ArrIncomeMonth(i - 1, k) += CDbl(ProductGroupRow.ProductGroupIncome / 100)
    17. Next
    18. 'Gesamtumsatz ins Array schreiben
    19. ArrIncomeMonth(i - 1, groupcount) += CDbl(MonthRow.DailyIncome / 100)
    20. Next
    21. 'Umsätze addieren und Daten ins DGV schreiben
    22. For L = 0 To groupcount
    23. ArrIncomeYear(L) += ArrIncomeMonth(i - 1, L)
    24. DGVEvaluation.Rows(i - 1).Cells(L + 1).Value = ArrIncomeMonth(i - 1, L).ToString("#,##0.00")
    25. Next
    26. 'Kunden ins DGV schreiben
    27. DGVEvaluation.Rows(i - 1).Cells(DGVColumnCount - 1).Value = ArrCustomers(i - 1).ToString
    28. Next

    Aber: Die vielen Aufrufe von SingleOrDefault und Co. bremsen weiterhin. Aber das sagte ja EdR schon.
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.
    So, hab mal bischen umgebaut.
    Das betexten des DGVs ist nun eine andere Sub als das Auswerten und Aufsummieren der DataTables - letzteres:

    VB.NET-Quellcode

    1. Private Sub GetDataSummaries(year As Integer, incomePerMonthAndProductGroup(,) As Double, customersPerMonth() As Integer, incomePerProductGroup() As Double)
    2. Dim dt0 = New Date(year, 1, 1), dt1 = dt0.AddYears(1) ' Zeit-Bereich
    3. 'Distribution_Tables auswerten
    4. 'Dim productGroups = Enumerable.Range(0, groupcount).Select(Function(k) DirectCast(DirectCast(ProductGroupBindingSource(k), DataRowView).Row, ProductGroupRow)).ToArray
    5. Dim productGroups = DtsSettings.ProductGroup.ToArray
    6. Dim groupcount = productGroups.Length
    7. Dim distributionTables = From distTbl In productGroups.SelectMany(Function(x) x.GetDistribution_TableRows)
    8. Let dt = distTbl.DailyIncomeRow._Date
    9. Where dt >= dt0 AndAlso dt < dt1 Select distTbl
    10. For Each rwDistrTbl In distributionTables
    11. Dim rwPG = rwDistrTbl.ProductGroupRow
    12. Dim month = rwDistrTbl.DailyIncomeRow._Date.Month
    13. Dim iProdGroup = Array.IndexOf(productGroups, rwPG)
    14. incomePerMonthAndProductGroup(month - 1, iProdGroup) += CDbl(rwDistrTbl.ProductGroupIncome / 100)
    15. Next
    16. 'IncomeRows auswerten
    17. For Each rwIncome In DtsSettings.DailyIncome.Where(Function(x) x._Date >= dt0 AndAlso x._Date < dt1)
    18. customersPerMonth(rwIncome._Date.Month - 1) += rwIncome.CustomerCount 'monatliche Kunden aufsummieren
    19. Next
    20. For month = 1 To 12 ' Jahres-Income bilden: je ProductGroup, aber auch "gesamt"
    21. For iProdGroup = 0 To groupcount - 1
    22. incomePerMonthAndProductGroup(month - 1, groupcount) += incomePerMonthAndProductGroup(month - 1, iProdGroup) 'monatliches Income je ProductGroup in die letzte Spalte("gesamt") aufsummieren
    23. Next
    24. For iProdGroup = 0 To groupcount
    25. incomePerProductGroup(iProdGroup) += incomePerMonthAndProductGroup(month - 1, iProdGroup) 'monatliches Income übers Jahr aufsummieren (quasi die Zeile "gesamt")
    26. Next
    27. Next
    28. End Sub
    Unschön ist nachwievor, dass incomePerProductGroup() - also die JahresSumme der MonatsEinkommen, dass diese Werte im eigenen Array gehalten werden.
    Die Werte hätten ebensogut eine zusätzliche Zeile von incomePerMonthAndProductGroup(,) sein können - quasi die Summen-Zeile - wie man sie von Excel vlt. kennt.
    Zumal incomePerMonthAndProductGroup(,) eine Summen-Spalte bereits enthält, welche je Monat die Summe aller ProductGroup-Einkommen aufnimmt.

    Aber das umzubasteln ist mir jetzt echt zuviel - da hängt dann noch eine Compare-Methode dran, die erwartet Parameter eben in diesem Format.

    Aber man erkennt vielleicht auch mein Bemühen, die Anzahl von Methoden-Argumenten niedrig zu halten.
    Dateien

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „ErfinderDesRades“ ()

    Also zunächst mal vielen, vielen Dank! @ErfinderDesRades
    Ich brauche noch ein wenig Zeit und Ruhe, deinen Code komplett zu verstehen.
    Da fehlte mir gestern Abend die Ruhe zu.
    Aber ich denke, das meiste (was ich aktuell nicht verstehe) werde ich mir selbst erlesen können.
    Wenns dazu fragen gibt, schreie ich einfach. Aber ich muss dich ja nicht mit Fragen nerven, die ich mir selbst erarbeiten kann.

    Allerdings hätte ich paar Fragen (zu anderen Themen) vorweg:
    1. nur aus Interesse. Was bedeutet die "08" am Ende des Dateinmanes "SlowCodeDemoApp08.zip"?
    2. du nutzt meine "alte" Version der hochgeladenen Demo App und hast dir die Daten xml da rein kopiert.
    Liegt das dran, dass mit der neu hochgeladenen Solution (Post 23) etwas verqueer ist, oder liegts einfach nur daran, dass du in der alten schon
    angefangen hast zu coden?
    3. Es stand im Raum (oder ich habs so verstanden), dass DataSet an sich und / oder meine Füllmethode schlecht sind.

    ErfinderDesRades schrieb:

    ich kann das Daten-Generieren dir nicht abnehmen.
    Theoretisch kann man zu jedem Datum beliebig viele IncomeRows anlegen, und jede dieser IncomeRow kann über die Distribution_Table mit allen ProductGroups verknüpft sein.
    Da gäbs zB für die Jahre 2010 - 2020 für jeden Tag 30 IncomeRows, und jede(!) dieser IncomeRow wäre mit mw. 5 ProductGroupRows verknüpft.
    Ich glaube aber nicht, dass das ein sinnvolles Daten-Szenario ist.


    Die Idee zum DataSet stammt von jemandem der echt Plan hat - also denke ich, geht das so in Ordnung.
    Die Methode zum Füllen des DataSets ist von mir. Also denkbar, dass diese Mist ist.
    Hast du hier mal rüber geschaut (DemoApp aus Post 23), ob die Art des DataSet füllens in Ordnung geht?
    ​Denn es bringt uns beiden ja nix, wenn du mir die WriteDataFirstYear Sub verbesserst, aber das ganze immer noch schlecht ist, weile meine Art der Speicherung einfach doof ist.


    Dieser Beitrag wurde bereits 6 mal editiert, zuletzt von „DerSmurf“ ()

    Hi,

    kann man diese Berechnung nicht in Parallel ausführen zu lassen? Stichwort Parallel.For.
    Ich habe damit zwar noch nie gearbeitet, aber solange nur die Berechnung Parallel ausgeführt wird und anschließend das DGV mit dem Ergebnis einmal befüllt wird, sollte das doch klappen?

    Ich denke, das probiere ich mal aus, wenn nichts dazwischen kommt :D

    DerSmurf schrieb:

    1. nur aus Interesse. Was bedeutet die "08" am Ende des Dateinmanes "SlowCodeDemoApp08.zip"?
    Ich verwende SolutionExplorer - OpenSource als BackupProgramm für meine Solutions, und immer wenn ich einen Stand erreicht habe, der gut funktioniert, und dann im Begriff bin, einen nächsten Entwicklungs-Schritt zu tun, klickse ich auf "Backup", und dann erstellt der ein Backup der Sources.
    So ein Backup kann ich auch gleich als Datei hier im Forum anhängen.
    Also 08 im Dateinamen zeigt, dass ich im Verlauf der Entwicklung der Performance-Verbesserung acht mal auf "Backup" geklickst habe.


    DerSmurf schrieb:

    2. du nutzt meine "alte" Version der hochgeladenen Demo App und hast dir die Daten xml da rein kopiert.
    Die alte Version öffnet mir sofort den infragekommenden Dialog, und - das habe ich auch eingebaut - erstellt auch eine Vorbelegung der Eingaben.
    Also wenn ich das mit F5 starte brauche ich nur noch ein Klick zu tätigen, damit die zu entwickelnde Methode denn auch mit sinnvollen Argumenten aufgerufen wird.
    Ich kanns überhaupt nicht leiden, wenn ich für jeden besch... Testlauf iwelche immer dieselben Klicks, und immer dieselben Eingaben tätigen muss, bevor das was ich testen will, denn auch wirklich aufgerufen wird.
    Das ist bei mir immer der erste Schritt einer Entwicklung, dass ich den Testlauf so gestalte, dass er unverzüglich und möglichst sogar ganz ohne jede weitere Eingabe zum Punkt kommt.


    DerSmurf schrieb:

    3. Es stand im Raum (oder ich habs so verstanden), dass DataSet an sich und / oder meine Füllmethode schlecht sind.
    Ich zumindest hab da fast garnet drüber nachgedacht.
    Ich hab die Performance der angegebenen Methode in Ordnung gebracht - die war wirklich unter aller Sau, v.a. wg. den .SingleOrDefault-Aufrufen - da stelle ich mit meiner Entwicklung bessere Verfahren vor, die benötigten DataRows zu ermitteln.

    Im Nachhinein denke ich, in der DailyIncomeTable könnte man die Spalte DailyIncome entfernen, weil das Einkommen eines Tages ist genau die Summe der produktGroup-bezogenen Tageseinkünfte.
    Die Spalte DailyIncomeRow.DailyIncome ist also überflüssig, bzw. könnte als berechneter Wert dargestellt werden, der nicht für jedes DailyIncome angegeben werden müsste, sondern sich eben selbst berechnet.
    Die Tabelle DailyIncome ist aber dennoch nötig, weil ja auch der KundenVerkehr festgehalten werden soll.

    Was allerdings ziemlicher Mist ist, ist die Benamung:
    DailyIncome geht noch fast - allerdings - wenn die Spalte DailyIncome tatsächlich entfernt oder auf berechnete Spalte umgestellt würde - wäre ein Name angemessener, der auf die Protokollierung des Kundenverkehrs abhebt - weil das ist ja die eigentliche Daseinsberechtigung der DailyIncomeTable.
    Distribution_Table ist als Name 100% Mist.
    Tatsächlich stellt die Tabelle das ProductGroup-spezifische TagesEinkommen dar - und so sollte sie auch heissen.

    Also auch beim drüber Nachdenken stosse ich nicht wirklich auf einen Design-Fehler des Datenmodells - wenigstens nicht bei diesen 3 Tabellen.

    Wie du das befüllst interessiert in diesem Thread überhaupt nicht.
    Wenn die Befüllung ein Problem macht, oder dir nicht gefällt (was ja auch ein Problem wäre), machn neuen Thread auf.

    KingLM97 schrieb:

    kann man diese Berechnung nicht in Parallel ausführen zu lassen?
    Nicht nötig.
    Leidlich effizient programmiert ist die Daten-Ermittlung ein Klacks (wenn du das mit "Berechnung" gemeint hast).
    Befüllung des DGVs hingegen würde problematisch, denn Controls kannste nicht nebenläufig befüllen.

    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „ErfinderDesRades“ ()

    ErfinderDesRades schrieb:

    Das ist bei mir immer der erste Schritt einer Entwicklung, dass ich den Testlauf so gestalte, dass er unverzüglich und möglichst sogar ganz ohne jede weitere Eingabe zum Punkt kommt.

    Das ist für zukünftige Uploads gemerkt.

    ErfinderDesRades schrieb:

    Wie du das befüllst interessiert in diesem Thread überhaupt nicht.

    Dann ist das Thema auch vom Tisch, denn ich habe mit meiner Befüllung kein Problem.
    Hab dich dann falsch verstanden.

    OK. Evtl. nerve ich dann später nochmal, wenn ich deinen Code verstanden (oder eben nicht) :) habe.
    Aber an dieser Stelle noch einmal. Vielen, vielen Dank für deine Bemühung!

    Edit zur Spalte DailyIncomeRow.DailyIncome
    Ich habe mir gedacht, da ich die Table DailyIncome ja wegen der Kundenzahlspeicherung ohnehin brauche, kann ich dort auch die Tageseinnahme speichern, um diese eben nicht berechnen zu müssen.
    Aber natürlich weiß ich nicht, was da sinnvoller ist - das speichern der Tageseinnahmen, oder das Errechnen der Tageseinnahmen.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „DerSmurf“ ()

    DerSmurf schrieb:

    Aber natürlich weiß ich nicht, was da sinnvoller ist - das speichern der Tageseinnahmen, oder das Errechnen der Tageseinnahmen.
    Eine berechnete Spalte wäre sinnvoller.
    Derzeit besteht an diesem Punkt Datenredundanz - also es gibt 2 Wege, die Tageseinnahmen zu erhalten:
    • Ich lese DailyIncome aus
    • Ich lese Distribution_Table aus, und summiere die productGroup-bezogenen Tageseinkünfte auf.
    Letzteres geschieht in meiner Methode.
    Meine Methode prüft aber nicht, ob die Summe der productGroup-bezogenen Tageseinkünfte tatsächlich identisch ist mit dem, was in DailyIncome.DailyIncome für den Tag angegeben ist.
    Sollte da jemals ein Unterschied auftreten kanns sein, dass du den einen Wert siehst, aber irgendeine Datenverarbeitung mit dem anderen Wert rechnet.

    Sowas kann mit einer berechneten Spalte nicht auftreten, denn die speichert die Tageseinnahmen nicht selbst, sondern verwendet ebenfalls Variante 2, um die Tageseinnahmen zu erhalten.
    Huhu
    Mir ist noch etwas eingefallen.
    Es gibt eine Form zur Anzeige der Rekorde.
    Also höchste Tageseinnahme, höchste Monatseinnahme pro Warengruppe, meiste Kundenzahl usw.
    Hier ratter ich ebenfalls das DataSet durch und suche mir die entsprechenden Werte.
    Mit gespeicherter Tagesseinahme - also mit DailyIncomeRow.DailyIncome kann ich hier den größten Wert suchen.
    Aktuell gehe ich da mit einer Schleife durch - aber dafür gibts ja eine Linq Funktion, sodass es ein Einzeiler wäre.

    Wenn ich die Tagesseinnahme nicht mehr im DataSet halte, sondern immer errechne, muss ich ja zur Suche der höchsten Tageseinnahme, jede Tageseinnahme errechnen - also mit einer Schleife durchs DataSet.

    Wäre dies evtl. ein Grund, die DailyIncomeRow.DailyRow zu behalten?
    Jo, da haste Recht.
    Ich meine natürlich die Spalte DailyIncome in der Table DailyIncome, also DailyIncomeRow.DailyIncome

    Hat sie eine daseinsberechtigung, wenn ich hier mittels Linq (max) den höchsten Wert suche?
    Hier würde mir diese Spalte im DataSet doch dann einiges ersparen?
    Wenn sie gespeichert bleibt, Linq mit max drüber, Datum auslesen fertig. Ist ein zweizeiler.
    Wenn sie nicht gespeichert ist, bleibt ja nur das gesamte DataSet mit einer Schleife durchzurattern und jede Tageseinnahme zu errechnen.

    Das die Summe der gespeicherten WarengruppenTagesEinnahmen ungleich der gespeicherten GesamtTageseinnahme ist, kann ich dabei sehr sicher ausschließen, da die DailyIncomeRow.DailyIncome beim Speichern ihre Daten aus einem Label bekommt, welches sich durch die Warengruppeneinnahmen errechnet.
    ich glaub du weisst nicht, was eine "berechnete spalte" ist.
    Das ist eine Spalte die da ist, wo aber kein Wert gespeichert wird. Also so eine Summen-Spalte rechnet die Summe aus einer anderen Tabelle immer selbst aus. So kann der Wert nie falsch sein, wenn in der anneren Tabelle sich was ändert - weil die Summenspalte rechnet sich dann ja automatisch neu aus.
    Ist bischen wie bei Excel, wenn du eine SummenFormel in eine Zelle einträgst. Dann zeigt die Zelle die Summe an, und die Summe ist bei Daten-Änderungen trotzdem immer richtig.

    Also wenn du DailyIncomeRow.DailyIncome als berechnete Spalte ausführt, kann obige Datenredundanz nicht auftreten, und da ist die Spalte immer noch.
    gugge DataExpressions: Filter und berechnete Spalten im Dataset
    Hmm. Doch weiß ich schon, ich hab aber überlesen, dass ich diese vorhandene Spalte in eine berechnete Spalte ändern soll(te).
    Ich dachte es geht darum, sie komplett zu entfernen, weil sie ja im hier geposteten Beispiel einfach überflüssig ist.

    Hast du eine Erfahrung, wie sich die Performance von berechneten Spalten in größeren DataSets verhält?
    OK - dann ändere ich diese Spalte einfach in eine berechnende.

    Edit: @ErfinderDesRades
    So, ich bin jetzt dabei deinen Code zu vestehen.
    Dazu aber direkt zwei Fragen:

    1. Du hast die Form frmEvaluation.DataSummary eingefügt.
    Warum diese Form und den Code nicht einfach in die frmEvaluation? (das habe ich versucht, bekomme aber tonnen Fehlermeldungen). Und wenn der Code separat sein muss, warum dann in einem Formular und nicht in einem Modul oder einer Klasse?

    2. Es gibt eine Funktion GetDistributionTables
    Spoiler anzeigen
    ''' <summary>returnt alle Distribution_Tables des angegebenen Jahres, die den angegebenen ProductGroups zugeordnet sind</summary>
    Private Function GetDistributionTables(year As Integer, productGroups As ProductGroupRow()) As IEnumerable(Of Distribution_TableRow)
    Dim dt0 = New Date(year, 1, 1), dt1 = dt0.AddYears(1)
    Return From distTbl In productGroups.SelectMany(Function(x) x.GetDistribution_TableRows)
    Let dt = distTbl.DailyIncomeRow._Date
    Where dt >= dt0 AndAlso dt < dt1 Select distTbl
    End Function

    Was die macht steht ja da, aber ich finde keinen Aufruf der Funktion. Wozu ist sie gut?

    Edit: Frage Nr.3
    Wenn ich deinen Code einkopiere, ärgert mich mein Visual Studio!
    Die Sub GetDataSummaries an der Schleife durch die Monate:

    VB.NET-Quellcode

    1. For Month = 1 To 12 ' Jahres-Income bilden: je ProductGroup, aber auch "gesamt"
    2. For iProdGroup = 0 To groupcount - 1
    3. incomePerMonthAndProductGroup(Month() - 1, groupcount) += incomePerMonthAndProductGroup(Month() - 1, iProdGroup) 'monatliches Income je ProductGroup in die letzte Spalte("gesamt") aufsummieren
    4. Next
    5. For iProdGroup = 0 To groupcount
    6. incomePerProductGroup(iProdGroup) += incomePerMonthAndProductGroup(Month() - 1, iProdGroup) 'monatliches Income übers Jahr aufsummieren (quasi die Zeile "gesamt")
    7. Next
    8. Next

    Im Oriignal ist dein "month" klein geschrieben. Bei mir wirds nach dem kopieren automatisch groß geschrieben, und es werden bei Verwendung die Klammern hinzugefügt.
    Ich kann machen was ich will - das geht nicht weg. Ich muss die Variable umbenennen, damit es funktioniert. Was ist das für ein Schmu?
    In der Sub DisplayMonthSummaries - passiert genau das gleiche. Es wirkt so als würde der Code bereits so reinkopiert... (was ja aber nicht der Fall ist).
    Wenn ich month in Monat umbenenne, gehts.

    Dieser Beitrag wurde bereits 6 mal editiert, zuletzt von „DerSmurf“ ()

    Und nochmal ich :o)
    Deinen Code habe ich verstanden, und mache mich jetzt noch daran die ArrIncomeYear(0 To groupcount) und die arrIncomeYearComp(0 To groupcount) zu entfernen, bzw. in ArrIncomeMonth (umbenannt in ArrIncome) zu integriegen.
    Dabei haben sich aber noch zwei Fragen aufgetan:
    Frage Nr.4 du deklarierst Dim ArrIncomeMonth(0 To 12, 0 To groupcount) As Double . Das sind ja 13 "Zeilen", aber du greifst immer mit einer Schleife for month = 1 to 12 arr(month -1, 1groupcount darauf zu. Warum deklarierst du dann nicht von 0 to 11?

    Frage Nr. 5:
    @VaporiZed sagte dass, etwas falsch läuft, wenn ich soviel Variablen unbefüllt ByRef an eine neue Sub übergebe, damit sie dort gefüllt wieder rauskommen.
    Du hast das so gelassen. Liegt es dran, dass ihr da anderer Meinung seid (also dass du das nicht so eng siehst wie er), oder wäre eine Änderung dieser Stelle zu aufwendig?

    Frage Nr. 6:
    Ich habe eine ähnlich schlechte Sub wie die FillYear() - diese nennt sich FillMonth().
    Diese tut das gleiche wie die von dir überarbeitete Sub, nur eben nicht für ein ausgewähltes Jahr, sondern einen ausgewählten Monat (inklusive Vergleich mit gleichem Monat aus einem auswählbaren Jahr).
    Diese Sub ist ziemlich genau so schlecht geschrieben, wie die original FillYear(), es fällt hier nur nicht so sehr auf, da die Datenmenge ja kleiner ist.
    Trotzdem ist die Ausführungszeit von FillMonth() nun länger als von FillYear() - was ja eigentlich traurig ist.
    Diese werde ich dann auch überarbeiten müssen. Würdest du hier einmal rüber schauen, wenn sie fertig ist?

    Edit: ArrIncomeYear und ArrIncomeYearComp habe ich erfolgreich beide aus der Sub verbannt :o)
    Spoiler anzeigen

    VB.NET-Quellcode

    1. ​Private Sub FillYear()
    2. 'Prüfen ob Eingaben in ComboBoxen numerisch sind
    3. Dim year As Integer
    4. Dim yearcomp As Integer
    5. 'Prüfen ob cbyear numerisch ist
    6. If Not Integer.TryParse(CBYear.Text, year) Then
    7. Exit Sub
    8. End If
    9. 'Prüfen ob cbcompyear numerisch ist
    10. If Not Integer.TryParse(CBCompare.Text, yearcomp) Then
    11. Exit Sub
    12. End If
    13. Dim groupcount = DtsSettings.ProductGroup.Count
    14. 'DGV löschen
    15. DelDGV()
    16. 'Spalten im DGV anlegen
    17. DrawDGV(groupcount)
    18. 'Daten für das erste Jahr ins DataSet schreiben - und angelegte Variablen ByRef füllen
    19. Dim ArrIncome(0 To 12, 0 To groupcount) As Double
    20. Dim ArrCustomers(0 To 11) As Integer
    21. GetDataSummaries(year, ArrIncome, ArrCustomers)
    22. DisplayMonthSummaries(ArrIncome, ArrCustomers)
    23. Dim CustomersTotal = ArrCustomers.Sum
    24. DisplayYearSummaries(CustomersTotal, ArrIncome, summaryRow:=13)
    25. 'Werte für das Vergleichsjahr ins DataSet schreiben - und angelegte Variablen ByRef füllen
    26. Dim ArrIncomeComp(0 To 12, 0 To groupcount) As Double
    27. Dim ArrCustomersComp(0 To 11) As Integer
    28. GetDataSummaries(yearcomp, ArrIncomeComp, ArrCustomersComp)
    29. DGVEvaluation.Rows.Add("")
    30. DGVEvaluation.Rows.Add(yearcomp.ToString)
    31. Dim CustomersTotalComp = ArrCustomersComp.Sum
    32. DisplayYearSummaries(CustomersTotalComp, ArrIncomeComp, summaryRow:=16)
    33. Dim DGVColumnCount = DGVEvaluation.ColumnCount
    34. 'Umsatzentwicklung in % ins DGV schreiben und Zuwachs blau färben
    35. CompareYears(DGVColumnCount, CustomersTotal, CustomersTotalComp, groupcount, ArrIncome, ArrIncomeComp, ArrCustomers, ArrCustomersComp)
    36. 'DGV Spalten formatieren
    37. FormatDGV(DGVColumnCount)
    38. 'Größe von DGV und Form anpassen
    39. ChangeSizes(DGVColumnCount)
    40. End Sub

    Dafür habe ich die ​GetDataSummaries wie folgt verändert:

    VB.NET-Quellcode

    1. Private Sub GetDataSummaries(year As Integer, ArrIncome(,) As Double, customersPerMonth() As Integer)
    2. Dim dt0 = New Date(year, 1, 1), dt1 = dt0.AddYears(1) ' Zeit-Bereich
    3. Dim productGroups = DtsSettings.ProductGroup.ToArray
    4. Dim groupcount = productGroups.Length
    5. Dim distributionTables = From distTbl In productGroups.SelectMany(Function(x) x.GetDistribution_TableRows)
    6. Let dt = distTbl.DailyIncomeRow._Date
    7. Where dt >= dt0 AndAlso dt < dt1 Select distTbl
    8. For Each rwDistrTbl In distributionTables
    9. Dim rwPG = rwDistrTbl.ProductGroupRow
    10. Dim month = rwDistrTbl.DailyIncomeRow._Date.Month
    11. Dim iProdGroup = Array.IndexOf(productGroups, rwPG)
    12. ArrIncome(month - 1, iProdGroup) += CDbl(rwDistrTbl.ProductGroupIncome / 100)
    13. ArrIncome(12, iProdGroup) += CDbl(rwDistrTbl.ProductGroupIncome / 100) 'hier die Befüllung der Warengruppen Gesamtumsätze
    14. Next
    15. 'IncomeRows auswerten
    16. For Each rwIncome In DtsSettings.DailyIncome.Where(Function(x) x._Date >= dt0 AndAlso x._Date < dt1)
    17. customersPerMonth(rwIncome._Date.Month - 1) += rwIncome.CustomerCount 'monatliche Kunden aufsummieren
    18. Next
    19. 'Schleife geht nicht mehr von 1 bis 12 und Zugriff mit "-1", sondern von 0 bis 12, um auch Gesamtumsatz zu addieren
    20. For Monat = 0 To 12 ' Jahres-Income bilden: je ProductGroup, aber auch "gesamt"
    21. For iProdGroup = 0 To groupcount - 1
    22. ArrIncome(Monat, groupcount) += ArrIncome(Monat, iProdGroup) 'monatliches Income je ProductGroup in die letzte Spalte("gesamt") aufsummieren
    23. Next
    24. Next
    25. End Sub

    Das sollte doch so Sinn machen, oder?
    Der Rest war ja dann einfach - Zugriffe auf die alten Arrays entfernen und entsprechend auf das "neue" Array setzen.

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „DerSmurf“ ()

    DerSmurf schrieb:


    1. Du hast die Form frmEvaluation.DataSummary eingefügt.
    Warum diese Form und den Code nicht einfach in die frmEvaluation? (das habe ich versucht, bekomme aber tonnen Fehlermeldungen). Und wenn der Code separat sein muss, warum dann in einem Formular und nicht in einem Modul oder einer Klasse?
    Das ist kein Form, was ich eingefügt habe - nur eine Datei. Darin ist eine sog. Partial Class frmEvaluation. Das ist eigentlich keine eigene Klasse oder Form, sondern ist Bestandteil deiner Class frmEvaluation - halt in einer Extra-Datei.
    Ich hab den Code, auf den ich mich konzentrierte in einer Datei zusammengehalten, und wollte das nicht zwischen deine wohl 600 Zeilen immer suchen müssen.
    Du kannst den Code ausschneiden, und in deine Form-Datei einfügen - das gibt keinen Fehler.

    DerSmurf schrieb:

    2. Es gibt eine Funktion GetDistributionTables
    Spoiler anzeigen
    ''' <summary>returnt alle Distribution_Tables des angegebenen Jahres, die den angegebenen ProductGroups zugeordnet sind</summary>
    Private Function GetDistributionTables(year As Integer, productGroups As ProductGroupRow()) As IEnumerable(Of Distribution_TableRow)
    Dim dt0 = New Date(year, 1, 1), dt1 = dt0.AddYears(1)
    Return From distTbl In productGroups.SelectMany(Function(x) x.GetDistribution_TableRows)
    Let dt = distTbl.DailyIncomeRow._Date
    Where dt >= dt0 AndAlso dt < dt1 Select distTbl
    End Function

    Was die macht steht ja da, aber ich finde keinen Aufruf der Funktion. Wozu ist sie gut?
    Tja, kann sein, dass ich die vergessen habe zu löschen, nachdem ich die Logik woanners hinverfrachtet hab.
    Lösch die bitte, wenn die nicht gebraucht wird.

    DerSmurf schrieb:

    Wenn ich deinen Code einkopiere, ärgert mich mein Visual Studio!
    Die Sub GetDataSummaries an der Schleife durch die Monate:

    VB.NET-Quellcode

    1. For Month = 1 To 12 ' Jahres-Income bilden: je ProductGroup, aber auch "gesamt"
    2. For iProdGroup = 0 To groupcount - 1
    3. incomePerMonthAndProductGroup(Month() - 1, groupcount) += incomePerMonthAndProductGroup(Month() - 1, iProdGroup) 'monatliches Income je ProductGroup in die letzte Spalte("gesamt") aufsummieren
    4. Next
    5. For iProdGroup = 0 To groupcount
    6. incomePerProductGroup(iProdGroup) += incomePerMonthAndProductGroup(Month() - 1, iProdGroup) 'monatliches Income übers Jahr aufsummieren (quasi die Zeile "gesamt")
    7. Next
    8. Next

    Im Oriignal ist dein "month" klein geschrieben.
    Das liegt wohl daran, dass bei dir der vb6-Namespace noch einen GeneralImport hat. Month ist eines der tausend vb6-Ranz-Worte.
    Visual Studio - Empfohlene Einstellungen
    soweit erstmal.

    DerSmurf schrieb:

    du deklarierst Dim ArrIncomeMonth(0 To 12, 0 To groupcount) As Double . Das sind ja 13 "Zeilen", aber du greifst immer mit einer Schleife for month = 1 to 12 arr(month -1, 1groupcount darauf zu. Warum deklarierst du dann nicht von 0 to 11?
    Keine Ahnung (doch, eine Ahnung schon)
    Entweder war das vorher schon.
    Oder ich hab das rückzubauen übersehen. Weil ich hatte schon angefangen, den 13.Monat einzubauen (wie erwähnt), dann aber aufgegeben, weil da noch so einiges dran hängt, und ich keine Lust mehr hatte.

    DerSmurf schrieb:

    @VaporiZed sagte dass, etwas falsch läuft, wenn ich soviel Variablen unbefüllt ByRef an eine neue Sub übergebe, damit sie dort gefüllt wieder rauskommen.
    Du hast das so gelassen. Liegt es dran, dass ihr da anderer Meinung seid (also dass du das nicht so eng siehst wie er), oder wäre eine Änderung dieser Stelle zu aufwendig?
    keine Ahnung, was du meinst.
    Sub GetDataSummaries() hat keinen ByRef-Parameter.

    FillMonth()...
    Diese werde ich dann auch überarbeiten müssen. Würdest du hier einmal rüber schauen, wenn sie fertig ist?
    Kann ich machen - klar.

    DerSmurf schrieb:

    ArrIncomeYear und ArrIncomeYearComp habe ich erfolgreich beide aus der Sub verbannt :o)
    Cool! :thumbup:
    Ich war ja wie gesagt davon zurückgetreten, weil das auch eine Überarbeitung von CompareYears() nach sich zieht/zog - aber das ging dann wohl so vom Aufwand her?