【问题标题】:How to let "select *" work accordingly after table was modified?修改表格后如何让“select *”相应地工作?
【发布时间】: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,因为如果你不这样做,它可能会为你创建一个语句缓存。

标签: c# oracle odp.net


【解决方案1】:

尝试在连接字符串中设置 Metadata Pooling = false、Self Tuning = False 和 Statement Cache Size = 0。

https://docs.oracle.com/database/122/ODPNT/featConnecting.htm#GUID-0CFEB161-68EF-4BC2-8943-3BDFFB878602

【讨论】:

    【解决方案2】:

    看起来这是某种缓存问题,无论是 ODP.net 还是 Oracle DBMS 本身。但是,如果第二个语句与第一个语句略有不同,则可以绕过缓存。可以修改上面的代码,将别名分配给不影响结果但让 DBMS 认为它会收到“新”语句的表:

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

    当然,这只是一种解决方法。 真正的解决方案是告诉 DBMS 不需要缓存。

    【讨论】:

    • 关于如何关闭元数据池和语句缓存的建议,请参阅我在上述问题中的评论。
    • @ChristianShay 您在上面的 cmets 中的提示指向正确的方向:添加“元数据池=false;自调整=false;”连接字符串解决了这个问题。但是,我必须找出是否有任何不需要的副作用,尤其是在查询性能方面。
    【解决方案3】:

    尝试修改事务中的表。在您选择之前提交。

    【讨论】:

    • 在 Oracle 中,CREATE TABLE 和 ALTER TABLE 不知道事务,即它们立即发生。但是,我尝试将语句包装在事务中,但行为是相同的。
    猜你喜欢
    • 1970-01-01
    • 2010-10-24
    • 1970-01-01
    • 1970-01-01
    • 2021-01-30
    • 2020-01-07
    • 2013-04-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多