【问题标题】:Oracle returns decimal for a certain NUMBER(4) columnOracle 为某个 NUMBER(4) 列返回十进制
【发布时间】:2014-06-10 09:29:16
【问题描述】:

我正在使用 ODP.NET 执行查询。该查询从一个表中选择了很多列,包括几个 NUMBER(4) 列。

对本地 dev 10.2 实例执行查询时,所有 NUMBER(4) 列都作为 Int16 的实例返回。这没关系,也是意料之中的。

在对另一个 11.2 实例执行查询时,除了最后 NUMBER(4) 列之外的所有列也是 Int16 的实例,但最后一个是十进制的实例,这目前正在破坏我的代码。我可以在我的应用程序中解决这个问题,但它的任意性正在杀死我。这怎么可能? Is this even possible? 我的意思是,它是同一个查询,同一个表,所有列的类型相同,但其中一个在数据读取器中获得了不同的 C# 类型。怎么会这样?

EDIT -> 我正在运行测试。即使我从该单个表中仅选择该单个列,没有 JOIN 或 UNION 或 WHERE 或任何东西,也会出现问题。尝试删除列并重新创建它;同样的问题。尝试创建具有相同列的表的副本,问题不断发生。尝试使用 less 列创建表的副本,问题停止发生。是否可能仅由于表中行为不端的列的列数/位置而发生这种情况? (该表有 77 列,有问题的列是表中的最后一列)。

【问题讨论】:

  • 你能提供一个可重现的测试用例吗?我倾向于认为一个问题列实际上不是NUMBER(4,0)
  • 我仔细检查了类型;在 USER_TAB_COLS 中都有 DATA_TYPE = NUM​​BER,PRECISION = 4,DATA_SCALE = 0,DATA_LENGTH = 22。
  • 我回想起以前使用 Oracle ODBC 驱动程序时的类似问题,其中一些列会返回为十进制。这通常发生在 UNION 语句中。您也可以尝试在行为不端的列上使用 CAST 函数将其强制为数字 (4,0)。

标签: c# oracle odp.net


【解决方案1】:

是的,我们遇到了同样的问题 - ODP.NET 将输出 NUMBER 声明替换为 OracleDecimal,即使对于没有小数的值也是如此。

在我看来,如果 type 被声明为 NUMBER 或被 oracle 查询解释为这样,即使 value 没有分数,它也会变成十进制。这与 NUMBER(N,0) 定义相反,ODP.NET 实际上根据位数计算出 .NET 类型,并选择最小的有符号整数或浮点/十进制类型来表示它。

如果您提供了实际的代码示例,它会有所帮助,但对于任何 ExecuteDataReader() 调用,以下代码应该是安全的:

  using(var reader = cmd.ExecuteReader())
  {
    while(reader.Read())
    {
      var v = Convert.ToInt16(reader["name"]);
    }
  }

输出参数的问题变得更糟,因为 OracleDecimal 没有实现 IConvertible 并且没有通过 Convert.ToInt32() 调用转换为 Int32 或类似类型(它失败并出现异常)。

问题是 OracleDataReader.GetInt16() 有这样的实现:

  int num2 = (int) this.m_pOpoMetValCtx->pColMetaVal[i].Scale;
  int num3 = (int) this.m_pOpoMetValCtx->pColMetaVal[i].Precision;
  if (num2 > 0 || num3 - num2 >= 5)
    throw new InvalidCastException();

所以它可能仍然会失败,但至少您会知道,由于某种未知原因,该比例是正数。这种模式在多个地方重复,通常所有 NUMBER 类型都由 ODP.NET 在内部表示为 OracleDecimal,然后根据比例和精度映射到 Int16、Int32、Int64、Float、Double 和 Decimal

所以对于下面的查询

  SELECT CAST(1 AS NUMBER(3,0)) as c1, CAST(220 AS NUMBER) as c2 FROM DUAL

以下作品:

  var v = Convert.ToInt16(reader["c2"]);

虽然以下失败:

  var v = reader.GetInt16(1); // InvalidCastException

我发现的另一个解决方法是在定义输出或返回参数时,使用 OracleDbTypeEx 会有所帮助。

例如,如果 oracle 输出参数被创建为

  var p = new OracleParameter("name",OracleDbType.Int32, ParameterDirection.Output)

它以 OracleDecimal 的形式返回并导致前面描述的问题,但是当这样声明时:

  var p = new OracleParameter("name",OracleDbType.Int32, ParameterDirection.Output);
  p.OracleDbCodeEx = OracleDbType.Int32;

它工作正常,值类型实际上是 .net 类型并且是 System.Int32。

整体 ODP.NET 实现很差,包括一些方法重载的选择。对于另一个示例,OracleDataReader.GetOrdinal() 被实现为列名称上的循环。该库的用户非常小心,并特别注意功能测试。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-03-28
    • 1970-01-01
    • 1970-01-01
    • 2013-09-09
    • 2018-05-16
    • 1970-01-01
    • 2012-07-07
    • 1970-01-01
    相关资源
    最近更新 更多