LinQ gruppieren nach Monat und Jahr

  • VB.NET

Es gibt 19 Antworten in diesem Thema. Der letzte Beitrag () ist von ErfinderDesRades.

    LinQ gruppieren nach Monat und Jahr

    Habe das gruppieren nach Monat und Jahr schon in C# gesehen, bekomme es aber nicht in VB.net übersetzt.


    Momentan mache ich es so:

    VB.NET-Quellcode

    1. Dim results = From u In tblUmsatz _
    2. Where u("Datum") >= CDate(dtpVon.Value) And u("Datum") <= CDate(dtpBis.Value) _
    3. Group u By Key = u("Datum").Month Into Group _
    4. Order By Key
    5. Select tblumsatz2 = Key, _
    6. euro = Group.Sum(Function(u) u("Euro")), _
    7. kilo = Group.Sum(Function(u) u("Kilo")), monat = Key


    So gruppiere ich aber nur nach dem Monat, ich hätte aber gern nach Monat und Jahr gruppiert, sowas wurde, wie gesagt hier schon mal mit C# gemacht:
    stackoverflow.com/questions/21…q-group-by-month-question

    Weiß jemand wie es geht?
    Jo, Linq2Sql ist die modernere Alternative zur Dataset-Technologie.

    Und du bist glaub schon gleich bei den Nachteilen angekommen ;).

    Im Dataset könnte man die BindingSource.Sort - Property setzen, und die bereits abgerufenen Daten wären neu sortiert.
    Bei L2S ist jede Abfrage eine neue Abfrage an die Datenbank.

    Aber vlt. habe ich das Gruppierungs-Feature nicht recht verstanden - was ist der Unterschied zw. Gruppieren einerseits und andererseits Sortieren nach mehreren Kriterien?

    Also wie sieht das Ergebnis aus, wie möchtest du es anzeigen?
    Naja es sind nach aktuellem Stand ca. 600.000 Datensätze, Tendenz steigend und das geht mit Linq einfach viel, viel schneller als mit anderen Methoden.

    Was Linq aktuell ausgibt ist folgendes:
    Beispieldatensätze:
    1 € - 1.1.2009
    3 € - 1.4.2010
    1,5 € - 15.1.2010
    5 € - 1.7.2010
    2,5 € - 17.1.2010

    Ausgabe:
    Monat 1: 5 €
    Monat 4: 3 €
    Monat 7: 5 €

    Ich hätte aber gerne:
    1/09 : 1 €
    1/10 : 4 €
    4/10 : 3 €
    7/10 : 5 €

    PS: Gruppieren und Sortieren sind unterschiedliche Dinge, sortieren bringt Daten in eine Reihenfolge, gruppieren vereinigt Daten nach einem Kritierium nach bestimmten Wünschen (Durchschnitt, Summe, Maximum, etc.).
    Wieso sollte das mit Linq ausgerechnet schneller gehen? Wie gesagt: Das macht im Hintergrund jedesmal eine DB-Abfrage, und dassis extrem langsam.
    Und wenn andere Technologien DB-Abfragen machen, sind die natürlich genauso schnell oder langsam, denn der DB-Provider ist derselbe.

    Jetzt habich auch kapiert, warum ich mit Gruppieren nie was am Hut hatte: Vom Prinzip her ist Gruppieren nämlich die Schaffung einer neuen übergeordneten Entität - da muß man am Datenmodell was schrauben.

    Kannst du deine c#-Lösung posten? kannich vlt. übersetzen, wenn die korrekt war.
    Warum Linq schneller ist? Keine Ahnung, habe es vorher mit .Compute versucht - absolut kein Vergleich. Linq ist deutlich schneller.

    In C# haben sie es so gemacht:

    VB.NET-Quellcode

    1. var grouped = from p in posts
    2. group p by new { month = p.Create.Month,year= p.Create.Year } into d
    3. select new { dt = string.Format("{0}/{1}",d.Key.month,d.Key.year), count = d.Count() };


    de.w3support.net/index.php?db=so&id=762899

    Diese Seite nützte dabei aber nicht viel:
    developerfusion.com/tools/convert/csharp-to-vb/
    Achso - in VB muß man bei anonymen Typen einen Punkt vor deren Property setzen, also

    VB.NET-Quellcode

    1. var grouped = from p in posts
    2. group p by new { .month = p.Create.Month, .year= p.Create.Year } into d
    3. select new { .dt = string.Format("{0}/{1}", d.Key.month, d.Key.year), .count = d.Count() }
    könnt gehen.

    (aber das ist iwie was anneres, als was duda mitte Umsatzstatistik oder was das ist, werkelst, oder?)

    Was meinst du mit .Compute()? DataTable.Compute()?
    Ja das passt nicht mit dem zusammen was ich mache, ich wollte es vom Prinzip her ja wissen, habe es jetzt so umgebaut:

    VB.NET-Quellcode

    1. Dim results = From u In tblUmsatz _
    2. Where u("Datum") >= CDate(dtpVon.Value) And u("Datum") <= CDate(dtpBis.Value) _
    3. Group u By New { .month = u("Datum").Month, .year = u("Datum").Year } Into Group _
    4. Select New { .dt = string.Format("{0}/{1}",Group.Key.month, Group.Key.year), _
    5. euro = Group.Sum(Function(u) u("Euro")), _
    6. kilo = Group.Sum(Function(u) u("Kilo"))}


    Leider beschwert sich der Compiler bei der öffnenden geschweiften Klammer:

    VB.NET-Quellcode

    1. Group u By New { .month


    Mit folgender Fehlermeldung:
    "Typ oder "With" erwartet."

    Mit Compute meine ich Datatable.Compute.
    Pest, ja, da hatter recht. Die Syntax ist

    VB.NET-Quellcode

    1. Dim results = From u In tblUmsatz _
    2. Where u("Datum") >= CDate(dtpVon.Value) And u("Datum") <= CDate(dtpBis.Value) _
    3. Group u By New With { .month = u("Datum").Month, .year = u("Datum").Year } Into Group _
    4. Select New With { .dt = string.Format("{0}/{1}",Group.Key.month, Group.Key.year), _
    5. euro = Group.Sum(Function(u) u("Euro")), _
    6. kilo = Group.Sum(Function(u) u("Kilo"))}

    Andere Frage: Hat so ein u-Item einer tblUmsatz-Entity nicht eine vernünftige Property, etwa u.Datum As DateTime?

    Oder was ist tblUmsatz für ein Ding?

    CDate(dtpVon.Value) - wenn dtpVon ein DateTimePicker ist - muß man dann dessen Value wirklich noch mit CDate umwandeln? - ich würde erwarten, das sei bereits ein Date.

    u("Datum").Month - das sieht mir auch sehr nach Option Strict Off aus, oder?
    Option Strict On!
    tblUmsatz ist:

    VB.NET-Quellcode

    1. Dim tblUmsatz = ds.Tables("UmsatzM").AsEnumerable


    Bei dem Quellcode:

    VB.NET-Quellcode

    1. Dim results = From u In tblUmsatz _
    2. Where u("Datum") >= dtpVon.Value And u("Datum") <= dtpBis.Value _
    3. Group u By New With {.month = u("Datum").Month, .year = u("Datum").Year} Into Group _
    4. Select New With {.dt = String.Format("{0}/{1}", Group.Key.month, Group.Key.year), .euro = Group.Sum(Function(u) u("Euro")), _
    5. .kilo = Group.Sum(Function(u) u("Kilo"))}


    Markiert der Compiler diesen Bereich:

    VB.NET-Quellcode

    1. New With {.month = u("Datum").Month, .year = u("Datum").Year}


    Quellcode

    1. Der Name einer Bereichsvariablen kann nur von einem einfachen oder qualifizierten Namen ohne Argumente abgeleitet werden.



    Mit dem CDate hast du Recht.

    Da ich das Dataset zur Laufzeit binde kennt tblUmsatz in dem Fall nicht die Propertys, folglich funktioniert nur u("Datum") und nicht u.Datum. Bzgl. Option Strict On muss ich wohl noch CStr setzen.

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

    Ja, ich hab mich gestern bisserl mitte Group - Klausel beschäftigt, und gefunden, die gibt ja immer einen anonymen Typen zurück. Naja-egal, jdfs. probierma:

    VB.NET-Quellcode

    1. Dim results = _
    2. From u In tblUmsatz _
    3. Let dt = CDate(u("Datum")) _
    4. Where dt >= dtpVon.Value AndAlso dt <= dtpBis.Value _
    5. Let sDate = String.Format("{0:yyyy}-{0:MM}", dt) _
    6. Group CDbl(u("Euro")) By sDate Into Group, euro = Sum()

    Ich kanns nicht probieren, habe ja nicht dein Dataset
    Danke, danke, danke.

    Sehr stark von dir. :)

    So ist jetzt das finale Ergebnis:

    VB.NET-Quellcode

    1. Dim results = _
    2. From u In tblUmsatz _
    3. Let dt = CDate(u("Datum")) _
    4. Where dt >= dtpVon.Value AndAlso dt <= dtpBis.Value _
    5. Let sDate = String.Format("{0:MM}/{0:yyyy}", dt) _
    6. Group u By sDate Into Group _
    7. Order By sDate _
    8. Select euro = Group.Sum(Function(u) CDbl(u("Euro"))), _
    9. kilo = Group.Sum(Function(u) u("Kilo")), monat = sDate
    Problem war mir auch aufgefallen, habe es so gelöst:

    VB.NET-Quellcode

    1. Dim results = _
    2. From u In tblUmsatz _
    3. Let dt = CDate(u("Datum")) _
    4. Where dt >= dtpVon.Value AndAlso dt <= dtpBis.Value _
    5. Let sDate = String.Format("{0:yy}{0:MM}", dt) _
    6. Group u By sDate Into Group _
    7. Order By sDate _
    8. Select euro = Group.Sum(Function(u) CDbl(u("Euro"))), _
    9. kilo = Group.Sum(Function(u) u("Kilo")), monat = sDate.Substring(2, 2), jahr = sDate.Substring(0, 2)


    Das baue ich mir dann in "For each item As Object in results" wieder zusammen.
    guck - vlt. kannichdich für Option Strict On! und für typisierte Datasets interessieren ;)

    Edit: Jdfs hatmichdas Linqmäßig echt weiter gebracht :)
    Dateien

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

    Ich schalte Option Strict On bei größeren Modulen per Quellcode ein, korrigiere dann die Fehler die zu korrigieren sind und schalte es danach wieder ab.

    Aber permanent kann ich es eben nicht nutzen.

    Bleiben wir bei unserem Linq Beispiel um auf die Daten des Linq Select Befehls zuzugreifen, folgt nach dem Linq Befehl folgendes:

    VB.NET-Quellcode

    1. For Each item As Object In results
    2. tmpdtRow(0) = item.Euro
    3. tmpdtRow(1) = item.kilo
    4. tmpdtRow(2) = item.monat
    5. tmpdt.Rows.Add(tmpdtRow)
    6. Next


    Das resultierende Datatable wird dann an ein Chart gebunden.

    Problem bei Option Strict On ist dann allerdings:

    Quellcode

    1. "Option Strict On" lässt spätes Binden nicht zu.


    Ich kann mit Option Strict On nicht mehr auf die Items der Linq Abfrage zugreifen und daher schalte ich es, nachdem ich überall meine CDate, CDbl, CInt, etc. gesetzt habe, wieder aus.

    VB2010 User schrieb:

    Ich kann mit Option Strict On nicht mehr auf die Items der Linq Abfrage zugreifen

    Ich denke doch - mit Option Infer On:

    VB.NET-Quellcode

    1. For Each item In results
    2. tmpdtRow(0) = item.Euro
    3. tmpdtRow(1) = item.kilo
    4. tmpdtRow(2) = item.monat
    5. tmpdt.Rows.Add(tmpdtRow)
    6. Next
    item ist im Schleifenkopf nicht auf Typ Object fixiert, und daher erkennt der Compiler den anonymen Datentyp selbst (wie er ihn im Linq-Ausdruck schon erkannt hat).

    noch Vereinfachung:

    VB.NET-Quellcode

    1. For Each item In results
    2. tmpdt.Rows.Add(item.euro, item.kilo, item.monat)
    3. Next