none
IF EXISTS hohe laufzeit RRS feed

  • Frage

  • Hallo Zusammen,
    ich hoffe Ihr könnt mir weiterhelfen, denn ich bin mit meinem Latein bei folgendem Problem am Ende...

    Hintergrund:
    ich übertrage zum archivieren Sätze von einer DB in eine andere, auf physikalisch unterschiedlichen Systemen. Damit im Fehlerfall der Vorgang relativ einfachzurückgefahren werden kann benutze ich im ersten Schritt eine leere Tabelle (tmptbl1) und fülle sie mit zu archivierenden Sätzen. Im Anschluss prüfe ich ob bereits Sätze in der Archivtabelle (tbl1) mit dem gleichen PK vorhanden sind und wähle anhand des Ergebniss das auszuführende SELECT Statement aus, also entweder füge alle ein, oder füge nur die ein die noch nicht da sind.

    Da es sich um eine ArchivDB handelt die sich nicht im Userzugriff befindet habe ich keine Indizes oder Ähnliches auf den Tabellen.

    Beide Server sind mit SQL 2005 64 BIT Standard Edition ausgestattet.

    Statement:

    IF NOT EXISTS (SELECT * FROM tmptbl1 INNER JOIN tbl1 ON tmptbl1.PK = tbl1.PK)
     BEGIN
      INSERT INTO tbl1 SELECT * FROM tmptbl1
     END
    ELSE
     BEGIN
      INSERT INTO tbl1 SELECT * FROM tbltbl1 LEFT JOIN tbl1 ON tmptbl1.PK = tbl1.PK WHERE tbl1.PK IS NULL
     END

    Dieses Statement funktioniert einwandfrei nur bei einem Kunden habe ich jetzt das Problem, dass die Prüfung nicht ausgeführt wird, bzw. nicht zum Ende kommt. Das SELECT selber hat eine Antwortzeit von unter einer Sekunde, solbald aber das IF EXISTS davor steht bekomme ich auch nach einer halben Stunde kein Ergebniss.

    Das es an der IF EXISTS Klausel liegt habe ich dadurch verifiziert, dass ich die INSERT Statements durch PRINTS ersetzt habe.


    Wärend ich das Statement ausgeführt habe konnte ich keinen IOs auf den HDDs über den Perfmon feststellen nur die Auslastung einer CPU von insgesamt acht geht nach oben.

    Was habe ich bereits getan:

    1. habe ich nach Locks und ähnlichem geschaut, konnte hier aber nichts feststellen, sicherheitshalber habe ich auch alle Verbindungen getrennt und etwaiige Prozesse geschlossen

    2. dann habe ich vermutet, dass es an den Parallelismen liegen könnte, also das ein Statement von mehreren CPUs gleichzeitig abgearbeitet wird die sich dann gegenseitig blockieren, deshalb habe ich serverweit die Option "Max. Grad an Parallelität" von 0 auf 1 gesetzt

    4. habe ich den Kompatibilitäts modus von 80 auf 90 umgestellt

    5. ein DBCC CheckDB hat auch keine Fehler geliefert

    6. dann habe ich das automatische Erstellen und aktualisieren der Statistiken deaktiviert

    7. anschliessend aktiviert und die Option asynchrones Aktualisieren der Statistiken dazu genommen.

    8. habe ich einen Wartungsplan Optimierungs Task erstellt und laufen lassen mit den Tasks (DB Intigrität prüfen, Indizes neu erstellen und Statistiken aktualisieren)

    --> das alles hat keine Besserung gebracht

    Natürlich habe ich auch das Statement etwas angepasst z. B.

    - über ein TOP 1, was keine Verbesserung brachte
    - ein count Verwendet und geprüft ob dieser > 1 ist, was sehr gut funktioniert hat

    jedoch möchte ich wenn möglich vermeiden, das Statement anzupassen, da ich das bei einigen Kundenservern nachführen müsste und es ja bisher tadellos funktioniert hat.

    Ich hoffe sehr, dassIhr mir weiterhelfen könnt.

    Viele Grüsse

    Pichel



    Montag, 18. Mai 2009 17:29

Antworten

  • Hallo Zusammen,
    das Problem konnte ich folgendermassen lösen: Ich habe die entsprechenden Spalten als PK definiert, was vorher nicht der Fall war. Zwar erklärt dies noch nicht, warum die gleiche SQL unterschiedlich (Ausführungsplan) ausgeführt wurde, aber jetzt läuft es ;-)

    Vielen Dank für eure Unterstützung.

    Grüsse

    Pichel
    Freitag, 29. Mai 2009 08:30

Alle Antworten

  • Ueber welche Anzahl von Zeilen in der Archivtabelle reden wir hier? Sollte diese Zahl in die Millionen gehen, kann das bei einer Tabelle ohne Indizes schon mal dauern. Dennoch erscheint mir 30 Minuten deutlich zu lange.

    Was war denn dein Ergebnis, als du die INSERTs durch PRINTs ersetzt hast? Wie hast du denn die Locks und Prozesse kontrolliert?

    Was ich aber nicht wirklich verstehe, ist deine EXISTS Logik:

    IF NOT EXISTS (SELECT * FROM tmptbl1 INNER JOIN tbl1 ON tmptbl1.PK = tbl1.PK)
     BEGIN
      INSERT INTO tbl1 SELECT * FROM tmptbl1 
     END
    ELSE
     BEGIN
      INSERT INTO tbl1 SELECT * FROM tbltbl1 LEFT JOIN tbl1 ON tmptbl1.PK = tbl1.PK WHERE tbl1.PK IS NULL
     END

    Falls keine Uebereinstimmung existiert, füge alles ein, ansonsten nur die, die noch nicht existieren. Warum machst du das nicht gleich so in einem einzigen kombinierten Statement?


    INSERT INTO tbl1 SELECT * FROM tbltbl1 LEFT JOIN tbl1 ON tmptbl1.PK = tbl1.PK WHERE tbl1.PK IS NULL

    erfüllt genau diesen Zweck. Oder:


    INSERT INTO tbl1 
    SELECT 
        T.*
    FROM 
        dbo.tmptbl1 T
    WHERE NOT EXISTS (SELECT 1 
        FROM 
            dbo.tbl1 T2
        WHERE T.PK = T2.PK);

    -- Frank Kalis Microsoft SQL Server MVP Webmaster: http://www.insidesql.org
    • Bearbeitet Frank Kalis Dienstag, 19. Mai 2009 06:48 Layout, die 2.te
    Dienstag, 19. Mai 2009 06:46
  • Hi Frank,
    Vielen Dank für Deine Antwort. Die Anzahl der Sätze beläuft sich im Schnitt auf 20000. Ein Ergebniss habe ich keines erhalten, da ich das Statement vorher abgebrochen habe. Im Aktivitätsmonitor kann ich wärend der Ausführung nicht viel erkennen, die Tabelle tbl1 wird gelockt, dazu noch interne Objekte, jedoch kann ich nicht erkennen, dass auch die Tabelle tmptbl1 gelockt wird, was meines Erachtens eigentlich geschehen sollte.

    Der springende Punkt ist, dass das SELECT was zur Prüfung herangezogen wird allein ausgeführt eine Antwortzeit von weniger als einer Sekunde hat und das obwohl keine Indizes vorhanden sind. Sobald ich aber ein IF EXISTS oder ein IF NOT EXISTS davor setze scheint es so, dass kein Ausführungsplan erstellt wird.

    zur Logik: ein INSERT INTO sollte eigentlich ohne WHERE Klausel deutlich schneller sein, gerade wenn keine Indizes vorhanden sind und in 90 % der Fälle ist es auch der Fall, dass ich diesen verwenden kann. In den übrigen 10 % der Fälle dürfen nur die Sätze eingefügt werden die noch nicht vorhanden sind, damit Redundanzen vermieden werden.

    Grüsse

    Pichel
    Dienstag, 19. Mai 2009 10:20
  • Bei gerade mal 20.000 Zeilen sollte das eigentlich binnen kürzester Zeit abgearbeitet sein. Die einzigen Locks die auch tmptbl1 zu sehen sein sollten, sind Shared Locks, da von dieser Tabelle nur gelesen wird. Kannst du mal den "Estimated Execution Plan" posten?


    -- Frank Kalis Microsoft SQL Server MVP Webmaster: http://www.insidesql.org
    Dienstag, 19. Mai 2009 12:04
  • Sorry, aber wie kann ich hier ein File posten?
    Dienstag, 19. Mai 2009 15:25
  • Das mit dem File kann ich Dir leider auch nicht sagen, aber Du kannst auch den Ausführungsplan in Textform ausgeben lassen und den dann (evtl. als Codeblock) posten.

    Welchen ServicePack-Stand hat eigentlich die Datenbank ?
    Einen schönen Tag noch, Christoph Muthmann SQLServer MVP
    Freitag, 22. Mai 2009 09:23
  • Hallo Zusammen,
    das Problem konnte ich folgendermassen lösen: Ich habe die entsprechenden Spalten als PK definiert, was vorher nicht der Fall war. Zwar erklärt dies noch nicht, warum die gleiche SQL unterschiedlich (Ausführungsplan) ausgeführt wurde, aber jetzt läuft es ;-)

    Vielen Dank für eure Unterstützung.

    Grüsse

    Pichel
    Freitag, 29. Mai 2009 08:30
  • Hallo Zusammen,
    das Problem konnte ich folgendermassen lösen: Ich habe die entsprechenden Spalten als PK definiert, was vorher nicht der Fall war. Zwar erklärt dies noch nicht, warum die gleiche SQL unterschiedlich (Ausführungsplan) ausgeführt wurde, aber jetzt läuft es ;-)

    Vielen Dank für eure Unterstützung.

    Grüsse

    Pichel

    Doch, genau das ist die Erklärung!

    Viele Grüße
    Christoph
    Samstag, 11. Juli 2009 18:49