Oracle返回特定NUMBER(4)列的小数

本文关键字:小数 返回 NUMBER Oracle | 更新日期: 2023-09-27 18:06:24

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

当对本地dev 10.2实例执行查询时,所有NUMBER(4)列都作为Int16的实例返回。

当对另一个11.2实例执行查询时,除了最后一个NUMBER(4)列之外的所有列都是Int16的实例,但最后一个是decimal的实例,这目前正在破坏我的代码。我可以在我的应用程序中解决这个问题,但它的随意性正在杀死我。这怎么可能呢?这可能吗?我的意思是,这是相同的查询,相同的表,所有列都是相同的类型,但其中一个在数据阅读器中得到不同的c#类型。这是怎么发生的?

EDIT ->我正在运行测试。即使我只从单个表中选择单个列,而没有join或union或WHERE或任何东西,问题也会发生。试着放下柱子,重新创建它;同样的问题。尝试创建具有相同列的表的副本,问题不断发生。尝试创建具有较少列的表的副本,问题停止发生。是否有可能发生这种情况仅仅是因为表中行为不端的列的数量/位置 ?(该表有77列,有问题的列是表中的最后一列)

Oracle返回特定NUMBER(4)列的小数

是的,我们遇到了同样的问题- ODP。. NET将输出NUMBER声明替换为OracleDecimal,即使值没有十进制分数。

在我看来,如果类型被声明为只是NUMBER或像oracle查询那样解释,即使值没有分数,它也会变成小数。这与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()实现为列名的循环。使用此库的用户必须非常小心,并特别注意功能测试。