myCSharp.de - DIE C# und .NET Community
Willkommen auf myCSharp.de! Anmelden | kostenlos registrieren
 
 | Suche | FAQ

» Hauptmenü
myCSharp.de
» Startseite
» Forum
» FAQ
» Artikel
» C#-Snippets
» Jobbörse
» Suche
   » Plugin für Firefox
   » Plugin für IE
   » Gadget für Windows
» Regeln
» Wie poste ich richtig?
» Datenschutzerklärung
» wbb-FAQ

Mitglieder
» Liste / Suche
» Stadt / Anleitung dazu
» Wer ist wo online?

Angebote
» ASP.NET Webspace
» Bücher
» Zeitschriften
   » dot.net magazin

Ressourcen
» guide to C#
» openbook: Visual C#
» openbook: OO
» MSDN Webcasts
» Search.Net

Team
» Kontakt
» Übersicht
» Wir über uns
» Impressum

» Unsere MiniCity
MiniCity
» myCSharp.de Diskussionsforum
Du befindest Dich hier: Community-Index » Diskussionsforum » Entwicklung » Datentechnologien » [Anders gelöst] Linq where Abfrage aus einer Liste
Letzter Beitrag | Erster ungelesener Beitrag Druckvorschau | Thema zu Favoriten hinzufügen

Antwort erstellen
Zum Ende der Seite springen  

[Anders gelöst] Linq where Abfrage aus einer Liste

 
Autor
Beitrag « Vorheriges Thema | Nächstes Thema »
Reggi
myCSharp.de-Mitglied

Dabei seit: 26.10.2016
Beiträge: 22


Reggi ist offline

[Anders gelöst] Linq where Abfrage aus einer Liste

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

verwendetes Datenbanksystem: MS SQL 2012, Entity Framework 6, .NET Framework 4.0

Hallo zusammen,

ich versuche gerade die Performance zu verbessern für eine Datenintegration, die User später einmal am Tag ausführen. Bisher dauert das bei ca. 60.000 Zeilen um die 3,5 Minuten, was noch zu lang ist.
Immerhin bin ich Dank Indexierung schon von 5 Minuten weggekommen.
Nachdem ich den Profiler genutzt habe, ist mir aufgefallen, dass ich sehr viel Zeit verschwende für An- und Abmelden auf der Datenbank durch das EF.
Das habe ich wiederum nur gemacht, weil ich pro 1.000 Datensätze, jeweils pro Datensatz geprüft habe, ob ein Datensatz mit diesem Schlüssel schon existiert.
Heißt ich habe eine Liste der zu prüfenden Objekte als Parameter der Funktion übergeben, die inserten soll, was noch nicht vorhanden ist. Bisher habe ich für jeden Datensatz die Nummer an diese Funktion übergeben:

C#-Code:
public bool CheckExistingGHVReceiver(string no)
        {
            bool result = false;

            try
            {
                using (var ctx = GetDbContext())
                {
                    GHV_Receiver_intern check = (from receiver in ctx.GHV_Receiver_intern
                                 where receiver.No_ == no
                                 select receiver).First();
                    result = true;
                }
            }
            catch (InvalidOperationException)
            {
                result = false;
            }

            return result;
        }

So viel zur Vorgeschichte und zum Reindenken...
Um zu verhindern, dass tausende Male auf die Datenbank verbunden wird, will ich meine Datensätze, die übrigens aus einer anderen Datenbank kommen, nach einer Liste filtern.
Dafür habe ich mir nun eine Sicht erstellt, die nur noch die Nummern liest, statt der kompletten Daten. Zurück kommt eine Liste mit Strings.
Wäre ich in SQL, würde ich diese einfach mit where....in oder noch besser exists überprüfen, da ich ja nur wissen muss, ob es diese Nummer schon gibt und wenn nicht, eben hinzufügen kann zu der Liste, die nachher mit SaveChanges() in die DB geschrieben wird.

Nun ist die Frage, kann ich das sinnvoll in Linq abbilden? Bisher finde ich zwar die ganzen Erklärungen zu Find, Exists und wie sie alle heißen, aber alle haben gemeinsam, dass sie nach bestimmten Werten filtern.
Also Liste.Exists(x => x.No == 15) mal so als Beispiel.
Ich will aber meine Liste prüfen Liste.Exists(x => x.No in NoListe). Das wäre soooooo perfekt. Ich will natürlich auch in meiner Funktion eine foreach-Schleife möglichst verhindern und jede Nummer einzeln prüfen.

Ich hab auch schon versucht einfach die vorhandenen Nummern auszulesen (Hab jetzt eine Sicht, die mir nur die Nummern ausgibt als List<String>) und einfach aus der Liste zu löschen, die ich prüfe, aber dann muss ich durch die restlichen Einträge ja auch wieder komplett durch.... Arrg, zum Mäuse melken.

Und wenn es da nichts gescheites gibt, ist der letzte Ausweg wohl eine SP zu erstellen und den SQL Server das machen zu lassen.

Ich hoffe Jemand von euch weiß einen Rat.

Liebe Grüße
Reggi

Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von Reggi am 17.11.2017 15:32.

17.11.2017 11:31 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Palladin007
myCSharp.de-Mitglied

Dabei seit: 03.02.2012
Beiträge: 992
Entwicklungsumgebung: Visual Studio 2010 Express


Palladin007 ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Also wenn ich mich nicht ganz doll irre, kann LINQ to SQL so spannende Methoden wie Contains auch parsen ;) Da kommt dann "x IN (1, 2, 3)" raus

Du kannst also einfach myList.Contains(column) schreiben.

Das würde ich aber nicht machen, je nachdem wie viele Nummern Du prüfen willst.
Es gibt nämlich eine Obergrenze, was maximal an Parametern übergeben wird und deine Liste wird in viele Parameter aufgelöst.

Daher würde ich schon weiter versuchen, alle vorhandenen Nummern zu downloaden und darin zu suchen.
Da brauchst Du aber auch keine Sicht, es reicht, wenn Du schreibst:

C#-Code:
var myNumbers = ctx.GHV_Receiver_intern.Select(receiver => receiver.No_).ToList();

Die Query-Syntax geht zwar auch, aber dann müsstest Du danach nochmal ToList aufrufen, um alle in den RAM zu holen.
Für so kleine Querys bevorzuge ich daher die Erweiterungsmethoden direkt.



Ob nun das vorherige Downloaden alles Nummern oder das Contains die schnellere Variante ist, musst Du messen.
Aber ich hab dich gewarnt: Es kann sein, dass Du deine Nummern nicht alle auf einmal übertragen kannst.
Maximal 2100 (siehe  hier) kannst Du übertragen, Du musst deine Nummern also in 2100 (oder besser etwas kleiner) große Gruppen durchsuchen.

Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von Palladin007 am 17.11.2017 11:53.

17.11.2017 11:53 Beiträge des Benutzers | zu Buddylist hinzufügen
Reggi
myCSharp.de-Mitglied

Dabei seit: 26.10.2016
Beiträge: 22

Themenstarter Thema begonnen von Reggi

Reggi ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Also erstmal natürlich danke.
Diese kurze Schreibweise ist mir sehr sympatisch ;).

Und das mit dem Contains will bei mir einfach nicht, so wie ich das will.
Hier mal die Funktion, wie sie bisher aussieht:

C#-Code:
public bool InsertGHVReceiverList(List<GHV_Receiver_intern> receiverList)
        {
            bool result = false;
            int counter = 0;
            GHV_Receiver_intern tmpReceiver = new GHV_Receiver_intern();

            using (var ctx = GetDbContext())
            {
                try
                {
                    // Liste bereits vorhandener Receiver
                    var ReceiverNoList = ctx.GHV_Receiver_intern.Select(receiver => receiver.No_).ToList();
                    tmpReceiver = receiverList.Contains(ReceiverNoList.No_);

                    // tmpReceiver zum Context hinzufügen
                    ....
                    ....

                    ctx.SaveChanges();
                    result = true;
                }
.
.
.

Egal was ich beim Contains anstelle, ich habe immer die Fehlermeldung, dass ich eine Liste nicht in GHV_Receiver_intern konvertieren kann.
Und wenn ich in so einem Contains wirklich nur 2100 Nummern vergleichen könnte, dann habe ich ein Problem.
Oder geht das in meinem Fall nicht, weil ich mit LINQ to Entity arbeite? Ich muss gestehen, ich habe mich mit den Unterschieden nicht wirklich befasst.

Und wenn ich mir so ansehe, dass ich die ganze Zeit Nummern suche, obwohl ich eine Liste brauche, wo die NICHT drin stehen, würde ich sagen, muss ich nochmal die Logik überdenken.
17.11.2017 14:07 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
ThomasE. ThomasE. ist männlich
myCSharp.de-Mitglied

images/avatars/avatar-178.gif


Dabei seit: 26.11.2013
Beiträge: 308
Entwicklungsumgebung: Visual Studio 2010/2015 Pro


ThomasE. ist online

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Hallo,

ich denk du hast hier einen Denkfehler:

C#-Code:
// So wirds eindeutlicher, WENN .No_ ein int ist, weiß ich nicht:
//var ReceiverNoList = ctx.GHV_Receiver_intern.Select(receiver => receiver.No_).ToList();
List<int> ReceiverNoList = ctx.GHV_Receiver_intern.Select(receiver => receiver.No_).ToList();
// Hier versuchst du mit einer Liste auf .No_ zuzugreifen, wie geht das?
// Zudem vergleichst du hier eine Objektliste vom Typ 'GHV_Receiver_intern' mit dem was auch immer Rückgabewert...
tmpReceiver = receiverList.Contains(ReceiverNoList.No_);

Müßte vorerst entschlüsselt werden bevor es hier weitergeht großes Grinsen
17.11.2017 14:24 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Reggi
myCSharp.de-Mitglied

Dabei seit: 26.10.2016
Beiträge: 22

Themenstarter Thema begonnen von Reggi

Reggi ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Die Liste besteht aus Strings, da die Nummern einen Buchstaben-Präfix haben, ergo --> List<String>.

Und sorry, ich hatte vergessen das ".No_" weg zu nehmen. Der Fehler tritt natürlich auf, wenn ich die Stringliste dort als Filter nutzen will.

Ich versuche irgendwie eine Möglichkeit zu finden und ich würde eher sagen, dass da generell ein Denkfehler besteht. Sagt mir zumindest mein Bauchgefühl, wenn ich Schritt für Schritt diese Dinge in meinem Kopf durchgehe, aber ich bin jetzt so festgefahren, dass mir partout nichts anderes einfallen will. enttäuscht
17.11.2017 14:34 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
ThomasE. ThomasE. ist männlich
myCSharp.de-Mitglied

images/avatars/avatar-178.gif


Dabei seit: 26.11.2013
Beiträge: 308
Entwicklungsumgebung: Visual Studio 2010/2015 Pro


ThomasE. ist online

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Aha, also so?:

C#-Code:
List<string> ReceiverNoList = ctx.GHV_Receiver_intern.Select(receiver => receiver.No_).ToList();
tmpReceiver = receiverList.Contains(ReceiverNoList);

Wenn das so ist, dann versuchst du in der 2ten Zeile eine List<string> in einer List<GHV_Receiver_intern> zufinden, das geht natürlich nicht...
17.11.2017 14:39 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
chilic
myCSharp.de-Poweruser/ Experte

Dabei seit: 12.02.2010
Beiträge: 1.893


chilic ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Ich muss das einfach fragen...

Zitat:
Wäre ich in SQL

Und warum bist du dann nicht in SQL?

Datenverarbeitungen mit Abgleich usw. mache ich immer in SQL. Das ist zwar aus Sicht mancher längst nicht mehr cool und in und was auch immer. Aber schnell ist es. Also irgendwie vielleicht doch noch cool?

Zitat:
ist der letzte Ausweg wohl eine SP zu erstellen und den SQL Server das machen zu lassen.

Warum der letzte? Das wäre für mich der erste Ansatz, denn für sowas ist der SQL Server da.

Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von chilic am 17.11.2017 15:13.

17.11.2017 15:08 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Reggi
myCSharp.de-Mitglied

Dabei seit: 26.10.2016
Beiträge: 22

Themenstarter Thema begonnen von Reggi

Reggi ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Hey chilic,

naja cool sind SP's glaube ich immer noch ;). Also zumindest machen sie nach dem Insert, den ich hier brauche auch die ganze Arbeit mit Triggern und noch vielen anderen SP's.

Und ja ich muss dir da Recht geben, nichts könnte effektiver vergleichen als der SQL Server selbst...
Ich schätze ich habe es nur so versucht zu lösen, weil es vorher auch so war und funktioniert hat.

Dann würde ich sagen, kann das Thema abgeschlossen werden.
17.11.2017 15:31 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Palladin007
myCSharp.de-Mitglied

Dabei seit: 03.02.2012
Beiträge: 992
Entwicklungsumgebung: Visual Studio 2010 Express


Palladin007 ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Naja, Du suchst doch nach einer string-Liste in einer string-Liste.
Die eine Liste ist die, die vom Server kommt, die Andere die, die Du lokal hast.

Du müsstest eigentlich in einer Schleife über die Liste der lokalen Nummern iterieren und pro Durchlauf prüfen ob diese Nummer in der Liste vom Server existiert.
Je nach Ergebnis handelst Du dann.

Ist das vielleicht dein Denkfehler?


Und was Stored Procedures angeht:
Die sind auch toll und vor allem kann man da ganz hervorragend optimieren.
Aber sie sind grausam zu testen und sie können nicht direkt von einem Source-Verwaltungssystem verwaltet werden.
Wenn Du unbedingt plain SQL schreiben willst, dann würde ich eher in C# das Script schreiben und dann so raus schicken.
Dann ist es zwar immer noch grausam zu testen, aber wenigstens hat ein Source-Verwaltungssystem die Hoheit drüber.

Ich würde immer erst einmal mit C# anfangen und versuchen, es dort zu lösen.
In den aller aller meisten Fällen reicht das auch völlig aus und in vielen Fällen ist es da sogar einfacher, weil Du einfachere Schleifen und automatische JOINs (bei ORMs wie EF) hast.
In diesen paar wenigen Fällen, wo eine SP z.B. aus Performance-Gründen wichtig ist, dann kann man da ja immer noch drüber nach denken.
17.11.2017 15:52 Beiträge des Benutzers | zu Buddylist hinzufügen
Reggi
myCSharp.de-Mitglied

Dabei seit: 26.10.2016
Beiträge: 22

Themenstarter Thema begonnen von Reggi

Reggi ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Palladin007,

ja genau das war der Denkfehler, den hatte ich gerade aufgedeckt, als ich die SP angefangen habe.
Also für alle, die es interessiert:

C#-Code:
                    List<String> ReceiverNoList = ctx.GHV_Receiver_intern.Select(receiver => receiver.No_).ToList();

                    foreach (GHV_Receiver_intern receiverListItem in receiverList)
                    {
                        if (!ReceiverNoList.Contains(receiverListItem.No_))
                            ctx.GHV_Receiver_intern.Add(receiverListItem);
                    }

SP's sind in der Tat nicht so einfach zu testen, aber wenn man sich da einmal durchgeschlängelt hat, dann geht es irgendwann. Die Fehlermeldungen sind teilweise echt "toll".
Aber wie du schon sagst, aus Performance Gründen kann man drüber nachdenken. Und genau das ist hier das Stichwort. Ich habe gerade mal die Zeit gemessen...
Ich muss natürlich in erster Linie wieder splitten, denn die Auswertung aller Daten hat kompakt jetzt mal 27 Minuten gedauert. (Vorher 3,5)

Ich bin dann mal meine SP erstellen cool .

Danke, und schönes WE an euch.
17.11.2017 16:23 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Baumstruktur | Brettstruktur       | Top 
myCSharp.de | Forum
Antwort erstellen


© Copyright 2003-2017 myCSharp.de-Team. Alle Rechte vorbehalten. 12.12.2017 07:28