【发布时间】:2017-01-26 06:29:21
【问题描述】:
我开发了一个基于外部定义创建表的应用程序。如果这些定义发生变化,应用程序会删除并重新创建受影响的表。它必须在打开连接时执行此操作,因为该任务是一组任务的一部分。但是,如果我查询一个后来用“select *”更改的表,我会得到旧定义的列。如果列已添加到表中,“选择 *”不会看到它。下面是一个示例代码来演示这一点:
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);
}
}
使用的软件版本:.net 4.5.1 和 Oracle 12.1
如果重置连接的部分被激活,“select *”会起作用,但是临时表中的所有数据都会被刷新,这是不需要的。
我想知道是否有某种方法可以告诉 ODP.net 或 Oracle——无论是导致这种奇怪行为的原因——表的定义已被更改,并且它应该在执行之前重新读取表定义“选择 *"。
【问题讨论】:
-
你为什么要这样做?似乎它违背了关系数据库系统中的要点,您应该研究 DocumentDB 解决方案...如果您确实需要这样做,请使用数据库提供的元数据视图来获取列的显式列表并将其用于查询而不是 *
-
@Milney 这些表中的数据与标准化数据库上的 SQL 查询结合使用。虽然将其放在其他地方是可能的,但它也会导致查询变得更加复杂(即代码将更难以阅读和维护)。
-
问题可以解决:诀窍是在不影响结果的情况下稍微修改语句,如下所示:stackoverflow.com/a/16861388/3424360
-
如果在连接字符串中使用 Metadata Pooling = false 和 Statement Cache Size = 0 会发生什么?见:docs.oracle.com/database/122/ODPNT/…
-
也可以试试 Self Tuning = False,因为如果你不这样做,它可能会为你创建一个语句缓存。