Announcement

Collapse
No announcement yet.

[WPF] Best Practice für DataGrid mit > 1.000.000 Rows

Collapse
X
  • Filter
  • Time
  • Show
Clear All
new posts

  • [WPF] Best Practice für DataGrid mit > 1.000.000 Rows

    Hi,

    Ich habe eine kleine Kundensuche, die aus einer TextBox und einem DataGrid besteht.
    Das DataGrid lädt > 1.000.000 Datensätze, wie folgt:
    Code:
    using (SqlConnection conn = new SqlConnection(Properties.Resources.SqlConnectionString))
    {
        using (SqlDataAdapter adp = new SqlDataAdapter(Properties.Resources.SqlLoadCustomerSearchList, conn))
        {
            adp.SelectCommand.Parameters.AddWithValue("@mandant", 1);
            adp.Fill(this.CustomerData); // CustomerData ist eine DataTable
        }
    }
    Wenn nun in die TextBox etwas eingegeben wird, soll das DataGrid gefiltert werden, das bewerkstellige ich aktuell über den RowFilter der DefaultView beim TextBox-Ereignis TextChanged:
    Code:
        if (this.CustomerData.Rows.Count < 1)
            return;
    
        this.CustomerData.DefaultView.RowFilter = string.Format( "SearchMatchcode like '%{0}%'", this.txtCustomerFilter.Text.Trim() );
    Grundsätzlich funktioniert das auch und verwende ich in vielen anderen Tools auch so, da sind allerdings deutlich weniger Datensätze.
    Das Problem hierbei ist, dass durch die Datenmenge der RowFilter sehr langsam ist, tlw. bis zu 3 Sekunden pro Zeichen-Eingabe in der TextBox.

    Die Anzahl der Datensätze kann ich an der Stelle nicht weiter verringern, weil in all denen gesucht werden muss.

    Gib es eine bessere Variante, die Liste zu filtern, damit es flüssiger läuft?

    Danke
    PHP rocks!
    Eine Initiative der PHP Community

  • #2
    Bloß weil in dem Grid gesucht werden soll, ist doch eine Anzeige von 1 Mio Datensätzen nicht erforderlich.
    Wer soll diese am Bildschirm sichten? Wie oft soll da geblättert werden?

    Das ist das falsche Vorgehen.

    Wenn ohnehin erst gesucht werden soll ist das Grid leer. Nach Eingabe der Suchbegriffe wird gesucht und das Ergebnis dargestellt.
    Will man schon während der Eingabe suchen, beginnt man erst nach einer gewissen Zeichenanzahl (bsp >3)
    Christian

    Comment


    • #3
      Hi,

      Das würde aber implizieren, dass ich bei jedem TextChanged-Event ein SQL-Statement abfeuere. Auch wenn ich das erst ab 3 Zeichen mache, die Kundennummer ist bspw. 7 Zeichen lang und die geben die Mitarbeiter gern mal komplett ein ohne Blick auf den Bildschirm.
      Dann würden quasi in ca. 2s 5 Queries gefuert und das Ergebnis in der DataTable überschrieben.

      Ich teste das aber nachher mal, danke für die Info.

      PHP rocks!
      Eine Initiative der PHP Community

      Comment


      • #4
        Nun, dann macht man das ohne "suchen während der Eingabe" und sucht erst, wenn die Kundennummer vollständig ist
        Christian

        Comment


        • #5
          Ne, das ist leider keine Option :-)
          Das mit der Kundennummer war nur ein Beispiel. Im Feld SearchMatchcode, in dem gefiltert wird befindet sich Kundennummer, Name und Ort mit PLZ und Land, bspw:
          Code:
          1234567 | Arne Drews, D-25840 Friedrichstadt
          Nun soll es ja möglich sein, nicht nur über die Kundennummer, sondern auch als Freitext im gesamten String zu suchen, also auch bei Eingabe von "Frie" sollte dieser Datensatz bereits mitgefiltert werden.

          D.h., ich benötige mind. eine Lösung, wie von Dir in #2 ( noch nicht getestet ) oder sogar wie in #1 beschrieben.
          Wenn es nur um die Kundennummer gehen würde, hätte ich nur ein Textfeld, das bei Anzahl von 7 Zeichen eine Query macht und gut. ;-)
          PHP rocks!
          Eine Initiative der PHP Community

          Comment


          • #6
            vielleicht geht auch ein Mix mit Timer zwischen den Tasteneingaben. Ab > 333 ms oder sowas wird gesucht.
            Müsste man mal in der Praxis einen realistischen Wert suchen. Ich habdas noch nie umgesetzt, aber schon gelesen.
            Eine andere Variante wäre (wenn Du eh einmal alles geladen hast). Der Filter im Grid geht nicht auf die Gesamtmenge, sondern auf die zuvor gefundene Menge. Beim (richtigen) Eintippen eines Suchbegriffs ist die Implementierung auch übersichtlich, bei Backspace, Del oder Cursorgebrauch wird es umständlicher.
            Das Verfahren könnte sich so oder ähnlich lohnen, wenn es eine relativ geringe Änderungsfrequenz der Basisdaten gibt und die nur 1x pro Tag (lazy, bei 1. Nutzung, ..) geladen werden.

            Comment


            • #7
              Nach deinen Demodaten könnte man ab 5 Ziffern den ersten SQL absetzen (Teil der Kundennummer oder Ort)
              Oder wenn nicht nur Ziffern in der Eingabe dann nach 4 (testen) Zeichen
              Christian

              Comment


              • #8
                Hallo,

                für WPF ist hier das Stichwort "Virtualization". So wird nur das gerendet, das am Bildschirm Platz hat und erst beim Reinscrollen / in die UI bringen das Rendering durchgeführt.
                Über die Sinnhaftigkeit soviele Datensätze überhaupt in die View zu bringen wurde hier ja schon berichtet.

                BTW: baue bitte eine Schichtentrennung ein, so schaut es aus als ob Datenzugriff mit UI-Code vermischt ist.

                mfG Gü
                "Any fool can write code that a computer can understand. Good programmers write code that humans can understand". - Martin Fowler

                Comment


                • #9
                  Hi,

                  Wie meinst Du das mit dem Datenzugriff mit UI-Code vermischt ist?

                  Das DataGrid ist über eine DataTable gebunden und für die Filterung muss ich ja auf den Text der TextBox zurückgreifen.
                  Meinst Du es wäre besser, hier auch ein Binding anzulegen und mit einem OnPropertyChangedEventHanlder zu arbeiten?

                  Die Virtualisierung sollte beim DataGrid lt. Doku eigentlich built-in sein, aber selbst wenn ich explizit
                  Code:
                  EnableRowVirtualizing="True"
                  VirtualizingPanel.IsVirtualizing="True"
                  VirtualizingPanel.VirtualizingMode="Receycling"
                  setze, ändert sich nichts.
                  Selbst nach dem 6. Zeichen, bei dem ich nur noch 7 sichtbare Datensätze hatte, hat der nächste Tastenanschlag fast 2s gedauert.

                  PHP rocks!
                  Eine Initiative der PHP Community

                  Comment


                  • #10
                    Hallo,

                    ich meine mit der Schichtentrennung eher sowas wie Das Model-View-ViewModel (MVVM) Entwurfsmuster für WPF (das wäre ganz stark getrennt, wieweit du die Trennung vornimmst obliegt dir).
                    Somit wäre es schon mal besser nicht direkt auf die TextBox zuzugreifen.

                    Es ist deshalb so langsam, da über den RowFilter gefiltert wird und der ist nicht für solche Datenmenge gedacht. Beim Virtualisieren gibt es Events, und wenn so eines feuert, so lädst du die betreffenden Daten erst aus der DB (nach).
                    Also nicht alles auf einmal in die UI, dann in der UI filtern, sondern nur eine kleine Grundmenge (od.nichts) in die UI und dann den gefilterten Rest aus der DB nachladen (am besten noch asynchron), dann ist es flüssig und schnell.

                    Du könntest auch die Daten im Hintergrund (d.h. in einem Background-Thread) laden und dann im UI anzeigen. Wichtig ist, dass das UI nicht blockiert und keine Aktion durchführen muss die länger als ein paar Millisekunden dauert, sonst hat der Benutzer den Eindruck dass die Anwendung hängt und das ist suboptimal.

                    mfG Gü
                    "Any fool can write code that a computer can understand. Good programmers write code that humans can understand". - Martin Fowler

                    Comment


                    • #11
                      Hi,

                      Entschuldige die späte Rückmeldung, bin aktuell an einem weiteren Projekt dran, das fertig muss.

                      Ja ok, MVVM nutze ich in anderen Projekten auch, da hauptsächlich mit dem Caliburn Micro Framework. Das habe ich in diesem Projekt nicht drin.
                      Aber Deinen Link arbeite auch mal durch, evtl. falle ich da ja noch über Dinge, die ich nicht berücksichtige.

                      Die Daten lade ich bereits asynchron, das Problem mit dem Filtern bleibt allerdings.

                      Ihr habt natürlich vollkommen recht, wenn ihr sagt, so eine Datenmenge will keiner scrollen, die kann nach Eingabe von einer Anzahl Zeichen geladen werden.
                      Das Problem bleibt dann aber bestehen, da die Datenmenge dann immer noch zu groß ist. Der Grund dafür ist, dass das Textfeld eine Art Instant-Suche sein soll. Also egal, was der Kollege eingibt, er soll im kompletten Text danach suchen. Da bei uns in der Region Orte mit nur drei Buchstaben existieren, müssen diese auch gefunden werden, also muss ich spätestens nach Eingabe des dritten Zeichen anfangen zu suchen. Das wiederum bricht mir bei den Kundennummern das Genick, denn weil die Datenmenge für die Kombination von bspw. 100 immer noch zu groß ist.
                      D.h. an irgendeiner Stelle muss ich tatsächlich eine große Datenmenge in die UI laden, wenn ich das richtig sehe.

                      Der Einwand von Christian ist an der Stelle natürlich auch korrekt, dass ich je nach eingegebener Zeichen die Länge bestimme, nach der gesucht wird.
                      Sind es u.a. Buchstaben, wird nach drei Zeichen gesucht, bei nur Zahlen erst ab Länge 5. Das würde manches zwar abdecken, aber bei Eingabe von sta würde ich wieder haufenweise Datensätze bekommen, aufgrund der Ortsnamen mit stadt.

                      Ich lese mich da aber noch mal ein und gebe Feedback, für andere, die ähnliches Problem haben.

                      Danke für die Infos, weiter sind natürlich weiterhin willkommen
                      PHP rocks!
                      Eine Initiative der PHP Community

                      Comment


                      • #12
                        Hallo,

                        aber bei Eingabe von sta würde ich wieder haufenweise Datensätze bekommen, aufgrund der Ortsnamen mit stadt.
                        Auch das könntest du explizit ausnehmen. Und andere Stoppwörter, damit die Suche nicht sofort beginnt.

                        mfG Gü
                        "Any fool can write code that a computer can understand. Good programmers write code that humans can understand". - Martin Fowler

                        Comment


                        • #13
                          oder auch
                          Die Filterung beginnt bei 5-6 Buchstaben. Gibt der Kunde weniger ein, muss er ENTER oder einen Button drücken. Dann wird auch explizit nach Orten mit 3 Buchstaben gesucht. Also Eingabe+<Leer>
                          Christian

                          Comment

                          Working...
                          X