2017-01-23 1 views
0

Ich habe eine Anwendung entwickelt, die Tabellen basierend auf externen Definitionen erstellt. Wenn sich diese Definitionen ändern, löscht die Anwendung die betroffenen Tabellen und erstellt sie neu. Dies muss geschehen, während die Verbindung geöffnet wird, da diese Aufgabe Teil einer Gruppe von Aufgaben ist. Wenn ich jedoch eine Tabelle abfrage, die sich später mit "select *" geändert hat, bekomme ich die Spalten der alten Definition. Wenn der Tabelle Spalten hinzugefügt wurden, wird "select *" nicht angezeigt. Hier ist ein Beispielcode, dies zeigt:Wie kann "select *" nach dem Ändern der Tabelle entsprechend funktionieren?

private static void OraSelectStarProblem() 
{ 
    var tabDef1 = "CREATE TABLE TEMP_PS_000 (SESSIONID RAW(16), EXECNR NUMBER, DUEDATE DATE)"; 
    //var tabDef2 = "CREATE TABLE TEMP_PS_000 (YASESSIONID RAW(16), YAEXECNR NUMBER, YADUEDATE DATE, NEWFIELD VARCHAR2(100))"; 
    var tabDef2 = "CREATE TABLE TEMP_PS_000 (YADUEDATE DATE, NEWFIELD VARCHAR2(100), SOMEOTHER integer)"; // causes ORA-00932 when querying 
    var selectCmd = "select * from TEMP_PS_000"; 
    using (var conn = new OracleConnection(GetConnStr())) 
    { 
     conn.Open(); 
     //PerformSelectStar(selectCmd, conn); 
     DropTempTable(conn); 
     Console.Write("Creating TEMP_PS_000 with 3 columns... "); 
     using (var cmd = conn.CreateCommand()) 
     { 
      cmd.CommandText = tabDef1; 
      cmd.ExecuteNonQuery(); 
     } 
     Console.WriteLine("Done!"); 
     PerformSelectStar(selectCmd, conn); 
     //conn.PurgeStatementCache(); // no effect 
     //conn.FlushCache(); // no effect 
     /*Console.WriteLine("Resetting connection"); 
     conn.Close(); 
     OracleConnection.ClearPool (conn); 
     conn.Open();*/ 
     DropTempTable(conn); 
     //conn.PurgeStatementCache(); // no effect 
     //conn.FlushCache(); // no effect 
     Console.Write("Creating TEMP_PS_000 with 4 columns... "); 
     using (var cmd = conn.CreateCommand()) 
     { 
      cmd.CommandText = tabDef2; 
      cmd.ExecuteNonQuery(); 
     } 
     Console.WriteLine("Done!"); 
     PerformSelectStar(selectCmd, conn); 
    } 
} 

private static void DropTempTable(OracleConnection conn) 
{ 
    using (var cmd = conn.CreateCommand()) 
    { 
     cmd.CommandText = "begin drop_table('TEMP_PS_000'); end;"; 
     cmd.ExecuteNonQuery(); 
    } 
} 

private static void PerformSelectStar(string selectCmd, OracleConnection conn) 
{ 
    try 
    { 
     using (var cmd = conn.CreateCommand()) 
     { 
      cmd.CommandText = selectCmd; 
      var dataAdapter = new OracleDataAdapter(cmd); 
      var dataSet = new DataSet(); 
      dataAdapter.Fill(dataSet); 
      var columns = ""; 
      foreach (DataColumn column in dataSet.Tables[0].Columns) 
      { 
       columns += column.ColumnName + " "; 
      } 
      Console.WriteLine("Columns: " + columns); 
     } 
    } 
    catch (Exception ex) 
    { 
     Console.WriteLine("PerformSelectStar: " + ex.Message); 
    } 
} 

Software-Versionen verwendet: .net 4.5.1 und Oracle 12.1

Wenn der Teil, der die Verbindung zurücksetzt aktiviert, „select *“ funktioniert, aber dann Alle Daten in temporären Tabellen werden gelöscht, was nicht erwünscht ist.

Ich würde gerne wissen, ob es eine Methode gibt, ODP.net oder Oracle zu sagen - was auch immer der Grund für dieses seltsame Verhalten ist - dass die Definition einer Tabelle geändert wurde und dass sie die Tabellendefinition vor der Ausführung erneut lesen sollte "wählen *".

+0

Warum würden Sie das tun? Scheint, als ob es den Punkt in einem relationalen Datenbanksystem trotzt und Sie sollten sich eine DocumentDB-Lösung ansehen ... Wenn Sie dies tun müssen, verwenden Sie die von der Datenbank bereitgestellten Metadatenansichten, um eine explizite Liste der Spalten zu erhalten und diese zu verwenden die Abfrage anstelle von * – Milney

+0

@Milney Die Daten in diesen Tabellen werden in Verbindung mit SQL-Abfragen in einer normalisierten Datenbank verwendet. Während es anderswo möglich wäre, würde es auch dazu führen, dass die Abfragen komplizierter werden (d. H. Der Code wäre viel schwerer zu lesen und zu warten). –

+1

Problem könnte gelöst werden: Der Trick besteht darin, die Anweisung in einer Weise zu ändern, die das Ergebnis nicht beeinflusst, wie hier gezeigt: http://stackoverflow.com/a/16861388/3424360 –

Antwort

-1

Versuchen Sie, die Tabellen innerhalb einer Transaktion zu ändern. Übernehmen Sie vor Ihrer Auswahl.

+0

In Oracle sind CREATE TABLE und ALTER TABLE Transaktionen nicht bekannt, d. H. Sie treten sofort auf. Ich habe jedoch versucht, die Anweisungen in Transaktionen zu verpacken, aber das Verhalten ist dasselbe. –

0

Es sieht so aus, als ob es sich um ein Caching-Problem handelt, entweder von ODP.net oder von Oracle DBMS selbst. Wenn sich die zweite Anweisung jedoch geringfügig von der ersten unterscheidet, kann das Caching umgangen werden. Der obige Code kann auf eine Weise modifiziert werden, die Aliasnamen in der Tabelle zugeordnet sind, die das Ergebnis nicht beeinflussen, sondern macht das DBMS dachte, es wäre eine „neue“ Anweisung erhalten:

... 
PerformSelectStar(selectCmd + " alias1", conn); 
... 
PerformSelectStar(selectCmd + " alias2", conn); 
... 

Natürlich, das ist nur ein Problemumgehung. Eine echte Lösung wäre, dem DBMS mitzuteilen, dass Caching nicht erwünscht ist.

+0

Sehen Sie sich meinen Kommentar in der obigen Frage an, um Vorschläge zum Deaktivieren von MetaData Pooling und Statement Caching zu erhalten. –

+0

@ChristianShay Ihre Hinweise in den obigen Kommentaren in die richtige Richtung gerichtet: Hinzufügen von "Metadatenpooling = false; Self Tuning = false;" zu der Verbindungszeichenfolge löst das Problem. Ich muss jedoch herausfinden, ob es irgendwelche unerwünschten Nebenwirkungen gibt, insbesondere in Bezug auf die Abfrageleistung. –

Verwandte Themen