Oracle Abfrage sehr langsam

  • VB.NET

Es gibt 17 Antworten in diesem Thema. Der letzte Beitrag () ist von schoeler.k.

    Oracle Abfrage sehr langsam

    Hallo,
    ich habe in meinem Programm eine Abfrage über eine Oracle Datenbank:

    VB.NET-Quellcode

    1. Dim ConnectionStringOracle as String = "Data Source=<server>:port/db; User ID=user;Password=Password;"
    2. Dim OracleConnection As New OracleConnection
    3. OracleConnection.ConnectionString = ConnectionStringOracle
    4. Dim OracleCmd As New OracleCommand()
    5. Dim oracleAdapter As New OracleClient.OracleDataAdapter
    6. OracleCmd.Connection = OracleConnection
    7. OracleCmd.CommandText = queryStringAnzFälle
    8. OracleCmd.CommandType = CommandType.Text
    9. OracleConnection.Open()
    10. OracleCmd.CommandText = queryStringOracle
    11. OracleCmd.CommandType = CommandType.Text
    12. OracleReader = OracleCmd.ExecuteReader()
    13. While (OracleReader.Read())
    14. MACH WAS
    15. End While

    Nun benötigt er aber für die Schleife am Ende enorme Zeit ... habe ich hier etwas falsch gemacht?

    Vielen Dank

    Nun benötigt er aber für die Schleife am Ende enorme Zeit ... habe ich hier etwas falsch gemacht?


    Wie groß ist den die Schleife, bzw. wieviele DataSets beinhaltet der Reader? Könnte sein das der Speicher ausgelastet ist und immer wieder Austausch zwischen RAM und Festplatte vornehmen muss wenn es zuviele Datensätze sind.

    Da ich das SQL-Command und die Umstände nicht kenne, schiesse ich mal mit der folgenden Ausführung ins Blaue:

    Es ist NIE eine gute Idee aus einem SQL-Server sich komplettes Tables zu laden, da VB.NET die im Speicher vor hält. Klar bei einigen hundert macht das noch nichts, aber wenn da einige x00.000 zusammen kommen wird das recht schnell sehr voll im Speicher.

    Daher macht es immer mehr Sinn sich aus der DB nur die Datensätze anliefern zu lassen die man auch wirklich braucht, also quasi im SQL-Statement WHERE-Klauseln zu nutzen. Und lieber nochmal aus der DB Datensätze nachliefern lassen als auf Verdacht mal gleich eine größtmöglichste Menge auf Vorrat zu halten.

    Hier musst Du wirklich nach dem Minimal-Prinzip vorgehen, nur das vorhalten was auch gerade gebraucht wird und keinen Datensatz mehr. Aber das wird ja auch durch SQL-Server optimal unterstützt, die serverseitige Abfrageausführung ist extremst effektiv und erreicht maximale Performance. Und wenn Du Dir immer nur die kleinstmögliche Anzahl an DS anliefern lässt, dann ist auch der Zeitbedarf für den Transport der DS vom Server zum Client minimalst.

    Aber eh klar, sowas wird nur wichtig wenn Du mit DB's arbeitest die auch wirklich große Anzahlen an Datensätzen verwalten.

    Gruß

    Rainer
    Ich hole schon nur die Datensätze, die ich benötige über eine SELECT-Abfrage ... es handelt sich hierbei um so ca. 200 Datensätze die Ausgegeben werden. Die Datenbank ist schon recht groß, aber die Dauer der Abfrage geht zu lange ... wenn ich im SQL-Developer die Abfrage ausführe, erhalte ich sofort die Ergebnisse.
    kann auch an vb- oder server-konfigurationen liegen. ich hab zb jahrelang nur access gemacht, weil mein sqlserver 2s gebraucht hat, um eine connection zu öffnen. mit 2008 gings dann - k.A., was mti vb2005 falsch war.

    lauf mal in der machwas-schleife nur die datensätze durch (oder zaähl sie nur), und miss nach, wie lang das dauert.

    picoflop schrieb:

    Da vermuten wir dann mal, dass der inhalt der Schleife ("machwas") das Problem ist
    zb: Einzelnes Eintragen in eine Lsitbox. Dann triggert bei jedem Einfügen das neuzeichnen und das Ding wird zäh wie Gummi

    Ja, da liegst Du garnicht so falsch ... ich schreibe Datensätze in eine Datei:

    VB.NET-Quellcode

    1. sw.Write(Trim(OracleReader.GetString(0)) & "|" & Trim(OracleReader.GetString(1)) & "|" & Trim(OracleReader.GetString(2)) & "|" & Trim(Format(OracleReader.GetValue(3), "0000-00-00") & "|" & OracleReader.GetString(4)) & "|")
    2. AuftragNr=Oraclereader.GetString(0)
    3. sw.WriteLine(Aufträge(AuftragsNr))

    ich denke mal, dann sollte man das anderst lösen? Ich hab schon probiert alles in einer Textbox zu sammeln und dann auf einmal in die Datei zu schreiben, aber das klappt auch nicht besser.

    Edit:
    Wenn ich die Schleife wie folgt abhandle:

    VB.NET-Quellcode

    1. While (OracleReader.Read())
    2. u=u+1
    3. End While

    dann dauert es genausolange, er kommt auf 136, also soviel datensätze sind es ja nicht.

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

    nemesis schrieb:

    wie kann man die zeit messen lassen?

    zb StopWatch Klasse.

    zb

    VB.NET-Quellcode

    1. Dim stp As New Stopwatch
    2. Dim l As New List(Of Long)
    3. stp.Start()
    4. l.Add(stp.ElapsedTicks)
    5. Threading.Thread.Sleep(100)
    6. l.Add(stp.ElapsedTicks)
    7. Threading.Thread.Sleep(200)
    8. l.Add(stp.ElapsedTicks)
    9. Threading.Thread.Sleep(300)
    10. l.Add(stp.ElapsedTicks)
    11. Dim alt As Long = -1
    12. For Each i As Long In l
    13. If alt <> -1 Then
    14. Debug.Print(i - alt)
    15. End If
    16. alt = i
    17. Next

    27.019ms für die ganze Schleife und wenn ichs ohne schleife mache, und ihn nur einmal OracleReader.Read() ausführen lasse, benötigt er sogar 21.151ms.

    für den Inhalt der Schleife, also wo er das in das File schreibt benötigt er pro Durchgang etwa 45ms

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

    200 Datensätze in einer Schleife zu durchlaufen dürfte im schlimmsten Falle 100 ms dauern und Feierabend (ohne die Verarbeitung innerhalb der Schleife zu rechnen).

    Ich habe mal kurz bei MSDN nachgelesen zum OracleReader und dort wird er als ein Stream beschrieben. Das führt mich zu der Vermutung das der Reader nur Referenzen hält und bei Abruf sich die Daten direkt aus der DB streamt. Das könnte das Problem sein wenn die Verbindung zur DB sehr langsam ist das sich dann die Abrufung der einzelnen Datensätze hin zieht.

    Ich würde mal empfehlen eine Testmodell mit einer OCDB-Connection und einem ADODB.Recordset aufzubauen und zu gucken wie schnell dort die Abarbeitung ist. Beim ADODB.Recordset wäre der Vorteil das die Daten nach dem Auslesen aus der DB tatsächlich vollständig im Recordset enthalten sind (es kann sogar disconnected abgearbeitet werden) und daher das Auslesen der Daten aus dem Recordsets komplett ohne weitere Zugriffe auf die DB erfolgt.

    Alternative wäre zu prüfen wie Du die Daten auf einen Schluck aus der DB rausziehst und im Memory für die Abarbeitung vorhälst. Vielleicht wäre hier ja das DataTable das richtige Abarbeitungsobjekt da das wenn ich es richtig verstanden haben auch die abgerufenen DS im Memory vorhält. Bin mir aber nicht sicher.

    EDIT:


    27.019ms für die ganze Schleife und wenn ichs ohne schleife mache, und ihn nur einmal OracleReader.Read() ausführen lasse, benötigt er sogar 21.151ms.


    21-27 Sekunden für die Abarbeitung von 136 Datensätzen? o_O

    Das geht ja mal gar nicht.

    Gruß

    Rainer
    Ok, ich wolltze jetzt mal Testweise eine ADO.Net Verbindung nutzen:

    VB.NET-Quellcode

    1. Dim connetionString As String
    2. Dim cnn As SqlConnection
    3. connetionString = "Data Source=<server>;User ID=<name>;Password=<passwort>"
    4. cnn = New SqlConnection(connetionString)
    5. Dim cmd As SqlCommand
    6. cmd = New SqlCommand(queryStringOracle, cnn)
    7. Dim reader As SqlDataReader
    8. cnn.open()
    9. reader = cmd.ExecuteReader
    10. While (reader.Read)
    11. 'schreibe reader.GetString(0) in Datei'
    12. End While
    13. cnn.close()

    aber da mekkert er, dass es die Tabelle aus der Abfrage nicht gibt: "Invalid object name"
    wie muss der Connectionstring zur Datenbank hier aussehen?´Ist dieser So nicht korrekt, oder liegt der Fehler wo anderst?

    Danke!
    Das was Du da erstellt ist immer noch eine OleDB-Connection und keine OCDB per Adodb. ;)

    Eine OCDB-Connection sieht ungefähr (aus dem Kopf raus) wie folgt aus:

    VB.NET-Quellcode

    1. Dim cnn As New ADODB.Connection
    2. cnn.ConnectionString = "<ADODB-Verbindungsstring zur Oracle DB<"
    3. cnn.open
    4. Dim rst As New ADODB.Recordset
    5. Dim strSQL As String = "SELECT * FROM tblMeinTable"
    6. rst.open strSQL, cnn, adOpenKeyset, adLockOptimistic '(oder eben andere Optionen)
    7. rst.MoveFirst
    8. Do Until rst.EOF
    9. ' tu was ...
    10. Loop
    11. '// wenn bei einem der beiden noch Dispose angeboten wird, dann auch Dispose zwischen Close und = Nothing einfügen
    12. rst.close: rst = nothing
    13. cnn.Close: cnn = nothing


    Dazu braucht Du den .NET-Verweis auf "Adodb".

    Hier mal eine Auflistung von Oracel-Verbindungsstrings:

    connectionstrings.com/oracle#p21

    Du brauchst den für OCDB. Brauchst aber den dort angegebenen Import nicht zu machen wenn Du den Verweis auf Adodb gesetzt hast.

    Gruß

    Rainer
    Hm, ok, aber wenn ich nun den ConnectionString verwende, dann sagt er mir:
    Schlüsselwort wird nicht unterstützt:'Driver'


    Vielen Dank!

    edit:
    ok, jetzt bin ich etwas weiter ... jetzt mekkert er
    [Microsoft][ODBC Driver Manager] Der Datenquellenname wurde nicht gefunden, und es wurde kein Standardtreiber angegeben

    obwohl der String wie folgt lautet:
    Driver={Oracle in OraHome92};Server=myServerAddress;Dbq=myDataBase;Uid=myUsername;Pwd=myPassword;

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

    Wenn das jetzt mit den vorher genannten Methoden auf die 100ms kommt, dann ok.

    Ansonsten wäre es gut zu wissen, ob dein SQL_Statement sehr Umfangreich ist, oder
    ob die Daten die durchsucht werden nach den speziellen WHERE-Klauseln mehr sind als 136.
    Dazu solltest du dann vielleicht einen Index in die Datenbank setzen.
    Der müsste natürlich intelligent verwendet werden, da du sonst ewig und drei Tage suchen
    kannst.

    Am besten mal das Statement, sofern nicht irgendwelche Firmengeheimnisse dranhängen, und
    die Masse an Daten die in der Datenbank vorhanden sind aufzeigen.