Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/277.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/oracle/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 处理要包含在oracle select语句中的大量数据_C#_Oracle - Fatal编程技术网

C# 处理要包含在oracle select语句中的大量数据

C# 处理要包含在oracle select语句中的大量数据,c#,oracle,C#,Oracle,最近的错误报告指出,正在调用的方法正在使服务崩溃,导致服务重新启动。故障排除后,发现原因是一个令人讨厌的Oracle SQL调用传递了数千个字符串。有一组字符串从外部服务传递给一个方法,这些字符串通常超过10000条记录。原始代码使用LIKE关键字在传递的集合上使用where子句,我认为这非常非常糟糕 public IList<ContainerState> GetContainerStates(IList<string> containerNumbers) {

最近的错误报告指出,正在调用的方法正在使服务崩溃,导致服务重新启动。故障排除后,发现原因是一个令人讨厌的Oracle SQL调用传递了数千个字符串。有一组字符串从外部服务传递给一个方法,这些字符串通常超过10000条记录。原始代码使用LIKE关键字在传递的集合上使用where子句,我认为这非常非常糟糕

public IList<ContainerState> GetContainerStates(IList<string> containerNumbers)
{
    string sql = 
    String.Format(@"Select CTNR_NO, CNTR_STATE FROM CONTAINERS WHERE CTRN_SEQ = 0 AND ({0})", 
        string.Join("OR", containerNumbers
                .Select(item => string.Concat(" cntr_no LIKE '", item.SliceLeft(10), "%' ")))
    );
    return DataBase.SelectQuery(sql, MapRecordToContainerState, new { }).ToList();
}
澄清可能令人困惑的内部方法:

DataBase.SelectQuery是一种内部库方法,使用泛型传递sql字符串、将记录映射到.NET对象的函数以及传递的参数,并返回映射函数返回的IEnumerable类型的对象

SliceLeft是来自另一个内部帮助程序库的扩展方法,它只返回字符串的第一部分,最多返回参数指定的字符数

显然使用LIKE语句的原因是,传递的字符串和数据库中的字符串只能保证匹配前10个字符。正在传递的字符串中的示例xx000000-1应该与类似xx000000-8的数据库记录匹配

我相信使用SUBSTR的IN子句比使用多个LIKE子句更有效,并将代码替换为:

public IList<ContainerRecord> GetContainerStates(IList<string> containerNumbers)
{
    string sql = 
String.Format(@"Select CTNR_NO, CNTR_STATE FROM CONTAINERS WHERE CTRN_SEQ = 0 AND ({0})", 
              string.Format("SUBSTR(CNTR_NO, 1, 10) IN ({0}) ", 
                            string.Join(",", containerNumbers.Select(item => string.Format("\'{0}\'",  item.SliceLeft(10) ) ) )
                            )
              );
    return DataBase.SelectQuery(sql, MapRecordToContainerState, new { }).ToList();
}
这有点帮助,在我的测试中出现的问题也较少,但是当传递了大量记录时,仍然会抛出异常并发生核心转储,因为SQL的长度超过了服务器在这些时间内可以解析的长度。DBA建议保存传递到临时表的所有字符串,然后根据临时表进行连接

根据该建议,我将功能更改为:

public IList<ContainerRecord> GetContainerStates(IList<string> containerNumbers)
{
    string sql = 

@"
        CREATE TABLE T1(cntr_num VARCHAR2(10));
        DECLARE GLOBAL TEMPORARY TABLE SESSION.T1 NOT LOGGED;
        INSERT INTO SESSION.T1 VALUES (:containerNumbers);

        SELECT 
                DISTINCT    cntr_no, 
                            '_IT' cntr_state 
        FROM 
                tb_master 
        WHERE 
                cntr_seq = 0 
            AND cntr_state IN ({0})
            AND adjustment <> :adjustment
            AND SUBSTR(CTNR_NO, 1, 10) IN (SELECT CNTR_NUM FROM SESSION.T1);
";

        var parameters = new
        {
            @containerNumbers = containerNumbers.Select( item => item.SliceLeft(10)).ToList()
        };
    return DataBase.SelectQuery(sql, MapRecordToContainerState, parameters).ToList();
}

现在我得到一个ORA-00900:无效的SQL语句。这真是令人沮丧,我如何才能正确地编写一个SQL语句,将此字符串列表放入临时表中,然后在SELECT语句中使用它来返回我需要的列表?

有几个地方可能会导致此错误,如果声明全局临时性是JAVA API,我认为.net没有这个功能。请改为尝试创建全局临时表。而且,我不知道您的内部API是否可以在一个select sql中处理多个sql。据我所知,ODP.net命令类每次调用只能执行一个sql。此外,CREATETABLE是一个DDL,因此它有自己的事务。我看不出有什么理由我们应该把它们放在同一个sql中执行。下面是ODP.net的示例代码

using (OracleConnection conn = new OracleConnection(BD_CONN_STRING))
        {
            conn.Open();
            using (OracleCommand cmd = new OracleCommand("create global temporary table t1(id number(9))", conn))
            {
                // actually this should execute once only
                cmd.ExecuteNonQuery();
            }

            using (OracleCommand cmd = new OracleCommand("insert into t1 values (1)", conn)) {
                cmd.ExecuteNonQuery();
            }

            // customer table is a permenant table 
            using (OracleCommand cmd = new OracleCommand("select c.id from customer c, t1 tmp1 where c.id=tmp1.id", conn)) {
                cmd.ExecuteNonQuery();
            }
        }

一些简要说明:如果要使用全局临时表,请在使用它的任何过程或方法之外创建它一次,然后在过程内部使用它insert/select/delete。此外,它的会话特定,这可能会影响您如何使用它取决于您的设置。例如,看到我现在在家,我回到办公室后会尝试这样做。我使用的是答案中相同的sql,带有硬编码的值,而不是从列表中添加项目。我第一次运行它时,一切正常,下一次我与oracle中的现有对象发生冲突时,临时表在方法完成后没有消失。我可以在末尾添加一个放置台吗?或者在事务中运行它,并在获得结果集后回滚。这通常是如何处理的?当您在第二次运行代码时,会出现一个错误,因为表不会自动删除。在oracle中,临时表的内容将根据您选择的选项在会话结束或事务结束时被擦除。你用完桌子后可以把它放下。但这不是一个推荐的方法。它会导致一些问题,例如并发问题。例如,当您的第一个线程创建了表并运行了查询,然后您的第二个线程想要运行相同的代码并再次尝试创建表时,它将失败。像普通表一样维护临时表要简单得多。下面是关于如何使用临时表的一个很好的讨论。还有一个链接