Announcement

Collapse
No announcement yet.

Optimierung einer querry unter c#

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

  • Optimierung einer querry unter c#

    hallo erstmal ich bin neu hier.

    ich habe dieses problem schon in einem anderen entwicklerforum angesprochen, aber da konnte man mir leider nicht weiterhelfen. es geht um folgendes:

    in unserer firmendatenbank liegen ca 3 mio datensätze. es sind messdaten die zur verwaltung aufgenommen wurden.
    jeder dieser datensätze besteht aus einem GUID der als primary key fungiert. dann noch ein datum, ein zahlenwert und die zuordnung dieses zahlenwertes zu einem von insgesamt 4 zeitsparten.

    wie wir herausgefunden haben, sind die zeitsparten falsch zugeordnet. nun hab ich die aufgabe eine lösung für dieses problem zu finden. das naheliegendsde war die erstellung eines c#-programmes. das funktioniert auch ganz gut, ich lese die entsprechenden werte mittels eines ganz einfachen SQLCommands aus und verändere sie.
    das problem liegt aber jetzt im updatebefehl. das codefragment:

    String querry = "update edsrawdatas set fascia = '" + newFascia + "' ,status2 = '" + oldFascia + "' where rawid = '" + ID + "'";
    SqlCommand com = new SqlCommand(querry, con);
    affected += com.ExecuteNonQuery();

    ist eigentlich nichts besonderes, ich update jedoch jeden datensatz einzelln und wie man sieht benütze ich eine einfache whereklausel die den primary-key vergleicht. das problem ist, dass das updaten JEDES datensatzes ca 3-4(!!!) sekunden dauert, was bei einer menge von einigen 10000 datensätzen klarerweise nicht tragbar ist.

    meine frage ist: gibt es unter c# eine möglichkeit so einen update-befehl irgendwie zu optimieren? bringen datasets irgendwelche vorteile (meiner erfahrung nach sind sie zwar sehr praktisch jedoch eher langsamer als eine lösung wie die obige).

    wäre für jede hilfe wirklich dankbar!

    mfg
    soulspirit

  • #2
    Hallo,
    die Anwendung tritt auch in jedes "Fettnäpchen", dass der SQL Server anbietet :-)

    a) Dynamisches SQL (als Zeichenketten zusammengebaut) anstelle einer UPDATE-Anweisung, die mir typisierten Parametern arbeitet.

    b) AutoCommit-Modus der Datenbank, der jeden UPDATE-Aufruf in der Schleife als separate Transaktion durchführt (d.h. der in der Log-Datei der Datenbank separat vermerkt werden muss).

    c) Schleifenaufruf durch 3 Millionen Datensätze aus einer externen Anwendung (C#) heraus und nicht als updatefähigen CURSOR (T-SQL-Implementierung) innerhalb der Datenbank.

    d) GUID-Werte (UNIQUEIDENTIFIER-Spalte mit 16-Byte-Werten) als Primärschlüssel.

    Eine alternative Technik (die mit den Bordmitteln des MS SQL Server auskommt) könnte so aussehen:

    <div style="font-family: Courier New; font-size: 10pt; color: black; background: white; border-top: windowtext 1pt solid; padding-top: 0pt; border-left: windowtext 1pt solid; padding-left: 0pt; border-right: windowtext 1pt solid; padding-right: 0pt; border-bottom: windowtext 1pt solid; padding-bottom: 0pt;"><p style="margin: 0px;"><span style="color: green;">-- Testtabelle anlegen</span></p><p style="margin: 0px;"><span style="color: blue;">CREATE TABLE </span>CursorTestTbl </p><p style="margin: 0px;">( </p><p style="margin: 0px;">&nbsp; recid <span style="color: blue;">INT </span>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <span style="color: blue;">NOT NULL IDENTITY PRIMARY KEY</span>, </p><p style="margin: 0px;">&nbsp; wert&nbsp; NVARCHAR(99) <span style="color: blue;">NOT NULL</span>, </p><p style="margin: 0px;">&nbsp; datum <span style="color: blue;">DATETIME </span>&nbsp;&nbsp;&nbsp; <span style="color: blue;">NOT NULL DEFAULT CURRENT_TIMESTAMP </span></p><p style="margin: 0px;">) </p><p style="margin: 0px;">GO </p><p style="margin: 0px;"><span style="color: green;">-- Testdatensätze einfügen </span></p><p style="margin: 0px;"><span style="color: blue;">SET NOCOUNT ON </span></p><p style="margin: 0px;"><span style="color: blue;">DECLARE </span>@iCount <span style="color: blue;">INTEGER </span></p><p style="margin: 0px;"><span style="color: blue;">DECLARE </span>@Wert <span style="color: blue;">VARCHAR</span>(99) </p><p style="margin: 0px;"><span style="color: blue;">SET </span>@iCount = 1 </p><p style="margin: 0px;"><span style="color: blue;">WHILE </span>@iCount &lt; 200 </p><p style="margin: 0px;"><span style="color: blue;">BEGIN </span></p><p style="margin: 0px;">&nbsp; <span style="color: blue;">SET </span>@Wert = <span style="color: maroon;">'Test ' </span>+ <span style="color: blue;">CAST</span>(@iCount <span style="color: blue;">AS VARCHAR</span>) </p><p style="margin: 0px;">&nbsp; <span style="color: blue;">INSERT INTO </span>CursorTestTbl (wert) <span style="color: blue;">VALUES </span>(@Wert) </p><p style="margin: 0px;">&nbsp; <span style="color: blue;">SET </span>@iCount = @iCount + 1 </p><p style="margin: 0px;"><span style="color: blue;">END </span></p><p style="margin: 0px;">GO</p><p style="margin: 0px;">&nbsp;</p><p style="margin: 0px;"><span style="color: green;">-- ################################################## #####</span></p><p style="margin: 0px;"><span style="color: green;">-- Update-CURSOR tauscht den Wert der Spalte »wert« aus</span></p><p style="margin: 0px;"><span style="color: green;">-- ################################################## #####</span></p><p style="margin: 0px;">&nbsp;</p><p style="margin: 0px;"><span style="color: blue;">DECLARE </span>@recid <span style="color: blue;">INT</span>; </p><p style="margin: 0px;"><span style="color: blue;">DECLARE </span>@wert <span style="color: blue;">VARCHAR</span>(99); </p><p style="margin: 0px;"><span style="color: blue;">DECLARE </span>@datum <span style="color: blue;">DATETIME</span>; </p><p style="margin: 0px;"><span style="color: blue;">DECLARE </span>cr <span style="color: blue;">CURSOR FOR </span></p><p style="margin: 0px;">&nbsp; <span style="color: blue;">SELECT </span>recid,wert,datum <span style="color: blue;">FROM </span>CursorTestTbl <span style="color: blue;">ORDER BY </span>recid </p><p style="margin: 0px;">&nbsp; <span style="color: blue;">FOR UPDATE</span>; </p><p style="margin: 0px;"><span style="color: blue;">OPEN </span>cr </p><p style="margin: 0px;"><span style="color: blue;">FETCH NEXT FROM </span>cr <span style="color: blue;">INTO </span>@recid, @wert, @datum; </p><p style="margin: 0px;"><span style="color: blue;">WHILE </span>(@@FETCH_STATUS = 0) </p><p style="margin: 0px;"><span style="color: blue;">BEGIN </span></p><p style="margin: 0px;">&nbsp; <span style="color: blue;">UPDATE </span>CursorTestTbl <span style="color: blue;">SET </span>wert = @wert + <span style="color: maroon;">'CursorUpdate' </span></p><p style="margin: 0px;">&nbsp; <span style="color: blue;">WHERE CURRENT OF </span>cr; </p><p style="margin: 0px;">&nbsp; <span style="color: blue;">FETCH NEXT FROM </span>cr <span style="color: blue;">INTO </span>@recid, @wert, @datum; </p><p style="margin: 0px;"><span style="color: blue;">END</span>; </p><p style="margin: 0px;"><span style="color: blue;">CLOSE </span>cr; </p><p style="margin: 0px;"><span style="color: blue;">DEALLOCATE </span>cr;</p></div>

    &#10

    Comment


    • #3
      hi, danke erstmal für die antwort!

      zu a)
      ja das hab ich inzwischen auch schon ausgebessert. hat sich aber perforance-mäßig praktisch 0 ausgewirtk :P

      zu b)
      ich habe inzwischen auch probiert das ganze als transaktion ablaufen zu lassen it abschließendem commit, auch ohne wirkung. ich weiß aber nicht genau ob es das ist was du damit gemeint hast..

      zu c)
      naja, das liegt daran: ich hab von cursors gar keine ahnung.. welchen vorteil bringt ein cursor? hab den tip nämlich auch schon aus nem anderen forum gekriegt (cursor unter c#?? kA..) aber hab damit eben nicht viel anfangen können...

      zu d)
      darüber könnte ich mich auch stundenlang aufregen. ich hab die datenbank ja nicht auf die beine gestellt und sie jetzt noch zu ändern wäre einfach zu aufwendig, da bereits diverse applikationen darauf zugreifen... aber damit muss ich wohl leben :P

      naja, das ganze in sql zu realisieren, das hab ich mir auch schon überlegt. das problem ist aber: das datum ist doppelt abgespeichert: einmal als normales datum und dann nochmal im UTC-format. um die datensätze auszubessern, muss ich das normale datum nochmal in UTC umrechnen und diesem datum dann einen anderen wert zuordnen. eine DateTime.ToUniversalTime (die dann auch sommer und winterzeit berücksichtigt wie in C#) hab ich in SQL aber vergebens gesucht, deshalb glaub ich mal bin ich gezwungen das ganze in c# zu machen außer jemand bringt einen besseren vorschlag (was ich inständig hoffe. ich sitz schon seit 3 tagen an dem problem mit der performance...)

      ps: 2 fragen noch am rande:
      wie kann ich quellcode so highlighten wie du?
      warum zum geier sind 3 themen von mir offen, obwohl ich nur einmal auf kommentar abschicken geklickt habe? :

      Comment


      • #4
        zu a) Wird die Update-Anweisung auch Prepared? Ohne eine Prepare ist der Vorteil nicht gegeben.

        zu c) Wie wäre es mit Einarbeitung?

        Hast du eigentlich schon mal Kontrolliert wie der Speicherverbrauch deines SQL-Servers im Bezug auf den maximal verfügbaren Speicher des Servers kontrolliert. Ich vermute mal das bei deiner Abfrage der Server voll am Anschlag fährt und die Indize nicht komplett im Speicher liegen sondern von Festplatte nachgeladen werden müssen. Sind evtl. auch noch weitere Tabellen betroffen die mit Kaskatierten Update-Trigger ebenfalls nachgezogen werden?

        Dein "einfaches" SQLCommando das die Ursprungsdaten liest wird das die ganze Zeit offen gehalten und wie sieht dieses aus? Evtl. werden damit dem Server auch noch einige 100 MB unnötigerweise aufgehalst

        Comment


        • #5
          Hallo soulspirit,

          kenne zwar deine Datenbank nicht, aber das Performance-Problem könnte durch einen Update-Trigger in der Tabelle "edsrawdatas" verursacht werden. Also die Trigger dort als erstes anschauen.

          Grüße,
          Neven Zovk

          Comment


          • #6
            @bernhard
            ja sie wir auch prepared
            und ja ich hab auch vor mich einzuarbeiten. aber durch den ganzen stress komm ich momentan nicht wirklich dazu...

            die datenbank enthält keine trigger, wären auch nicht nötig. einzig die datenmenge macht mir zu schaffen... und ja der rechner ist wirklich total ausgelastet, die testdatenbank läuft auf dem gleichen rechner wie das c#-programm und das VS studio. der rechner ist total überfordert (unter mindestanforderungen) und ich bestreite auch nicht, dass meine momentanen SQL-kenntnisse nur die grundlagen erfassen.

            die select-commandos haben nichts mit dem update zu tun. sprich: ich verbinde mich, füre die selects zum auslesen aus, trenne die verbindung und behandle erstmal die ausgelesenen daten.. liegen dann halt auch im arbeitsspeicher, aber was soll ich auch dagegen tun?

            wie gesagt ist die datenbank auf der ich arbeite eine testdatenbank. an den richtigen server gehe ich erst ran, wenn das programm akzeptabel läuft. das gibt dann natürlich auch einen großen performance schub, aber das problem liegt woanders, nämlich darin, dass ich das update-statement nicht hinkriege...

            aber wenn ich jetzt das ganze in SQL realisieren will, ist immer noch das problem mit der datumsumwandlung da! wenn ich das lösen kann, bin ich gern bereit das ganze in SQL zu machen bzw. wär mir auch liebe.

            Comment


            • #7
              Wieviel Ram hast Du? Falls der Rechner noch mehr verdrägt dann rein damit. Nichts bremst eine DB sosehr vie zu wenig Ram und das kann locker Facktor 10 oder mehr ausmachen wenn die eigentlichen Performancebooster von Datenbanken, die Indize, nicht komplett im Speicher gehalten werden können.

              Ich würde sagen 2 GB solltest Du schon haben

              Comment


              • #8
                ich glaub der server ist ein pentium mit 3 ghz und 1,5 gb ram. auch nicht grad berauschend. hei aber lassen wir die hardware mal auf seite... ich hoff immer noch dass es beim update-statement ne bessere lösung gibt.

                Comment


                • #9
                  Ich glaube nicht das es beim Update-Statement eine bessere Lösung gibt. Ich mach sowas auch (jedoch mit Prepared Statements + Parameter) aber fast immer auf Primärfelder vom type nvarchar und habe Updatezeiten im Einstelligen ms (0-20 ms) bereich.
                  Es gibt bekannte Performanceprobleme wenn man mit nvarchar-Typ auf eine Spalte vom Typ varchar (ich glaube bei der 2000er-Version) bei der die Datenbank nicht erkennt das es einen passenden Index gibt.

                  Du kannst mal einen Test mittels dem Queryanalyser (u. evtl. auch mit dem Tracer). Nimm obige Anweisung in fertiger Form

                  update edsrawdatas set fascia = 'xyz', status2 = 'abc' where rawid = 'def...';

                  und lass dir mal den Ausführungsplan zeigen. Wird der Primärindex verwendet. Anschließend lasse die Anweisung ausführen (Zeit?) und lass dir die einen Trace mit dem Trace-Tool anzeigen. Was steht dort bezüglich Readoperationen oder ähnlichen

                  Comment


                  • #10
                    so ich glaub ich habs jetzt so hingekriegt, dass das ganze doch eher zufriedenstellend abläuft:
                    auf meiner kiste (hat übrigends 512 mb sdram ^^ und 1,5 ghz) aktualisiert es die datensätze so ungefähr im sekundentakt. hab das geschafft, weil ich nicht mehr so viele werte auf einmal im arbeitsspeicher hab.
                    auf dem server dürfte das ganze dann nochmal viel schneller gehen, ich schätze mal dass es die daten von einem monat innerhalb 1-2 stunden schaffen wird (ja ich weiß, nicht grad berauschend :P)

                    danke eure die hilf

                    Comment


                    • #11
                      512 MB auf deinem Arbeitsrechner. Am besten noch mit XP. Da wird fast nichts mehr für die Datenbank übrig bleiben.

                      Und du wunderst dich über die Performance

                      Comment


                      • #12
                        optimierung geht so net

                        ein Update pro Sekunde - wieviele tausend Jahre willste denn warten?

                        Am besten du ermittelt die betroffenen IDs, schreibst die zusammen mit den neuen WErten für diese IDs in eine neue Tabelle auf dem Server, baust dir ein View und machst den Updatevorgang auf dem View mit set a = b where c=d.

                        Views bauen machst du mit dem Enterprise manager oder dem Management Studio je nach dem welche Version du hast.

                        Wenn das immer noch klemmt - Index erzeugen - top(1000) zwischen Update und dem Rest schreiben.

                        Comment


                        • #13
                          Hallo Shadowmaster,

                          schon mal auf das Erstellungdsdatum des Threads geschaut?

                          Comment

                          Working...
                          X