如何从 Oracle 中的数字返回受约束的整数?

问题描述 投票:0回答:3

用 Oracle 来说,这将是一个

NUMBER(9, 0)
或任何映射到 32 位(甚至 16 位或 64 位)整数的内容。

CREATE TABLE T(C1 NUMBER, C2 NUMBER(9, 0) ); -- Unconstrained and constrained number
INSERT INTO T(C1, C2) VALUES (10, 10);

SELECT C1, C2 FROM T; -- C1 is NUMBER, C2 is NUMBER(9,0)

Oracle 假定了精度和小数位数,然后似乎没有办法在不修改表结构本身的情况下设置它们:

SELECT
    CAST(C1 AS INTEGER) FAILURE1, -- Unconstrained number
    TRUNC(C1)           FAILURE2, -- Unconstrained number
    ROUND(C1, 0)        FAILURE3, -- Unconstrained number
    CAST(ROUND(C1, 0) AS NUMBER(9, 0)) FAILURE4, -- Unconstrained number
    TO_NUMBER(TO_CHAR(PERCENT_DONE, '99') FAILURE5, -- ~Overcomplicated and still fails
FROM T

这使得编写与 Oracle 一起使用的应用程序变得更加困难,并且还使它们变得更慢且内存效率更低。像 Stack Overflow 自己的 Dapper 这样的 ORM 及其底层驱动程序不知道不受约束的数字可能包含什么,因此它们将这些列映射到类型,例如

System.Decimal
,而不仅仅是简单的整数类型。

Oracle 确实有

TO_BINARY_FLOAT()
TO_BINARY_DOUBLE()
分别映射到 float 和 double,但是如何用整数来完成呢?

oracle orm dapper
3个回答
2
投票

Oracle
NUMBER
数据类型的存储

在 Oracle 中,

NUMBER
是一种精确的数字数据类型,指数用 1 个字节表示,数字的每两位数字用 1 个字节表示。

如果对于您的示例数据,您使用:

SELECT DUMP(C1), DUMP(CAST(C1 AS NUMBER(9,0))), DUMP(C2) FROM T;

然后您将看到查询返回的二进制值:

转储(C1) 转储(CAST(C1ASNUMBER(9,0))) 转储(C2)
典型=2 长度=2:193,11 典型=2 长度=2:193,11 典型=2 长度=2:193,11

如您所见,数据库不会传递有关基础列数据类型的数据和值,它仅传递值(并且使用

CAST
不会更改值)。

即使基础列是

NUMBER
NUMBER(9,0)
,数据库也仅传递 2 字节信息,在这种情况下,比使用 32 位(4 字节)或 64 位更有效(8 字节)整数。

小提琴


Oracle 的数字数据类型

Oracle 具有 数据类型:

  • NUMBER(p, s)
    - 存储精确值,范围从 -84 到 +127,精度为 1 到 38 位十进制数字。
    • NUMERIC
      DECIMAL
      - 是
      NUMBER
    • 的别名
    • FLOAT(p)
      -
      NUMBER
      的子类型,精度为 1 到 126 个二进制数字(即
      NUMBER(1)
      NUMBER(38)
      )。
      FLOAT
      DOUBLE PRECISION
      FLOAT(126)
      的别名,
      REAL
      FLOAT(63)
      的别名。
    • INTEGER
      INT
      SMALLINT
      - 是
      NUMBER(38)
    • 的别名
  • BINARY_DOUBLE
    - 64 位浮点数。
  • BINARY_FLOAT
    - 32 位浮点数。

Oracle 确实有 TO_BINARY_FLOAT() 和 TO_BINARY_DOUBLE() 分别映射到 float 和 double,但是如何用整数来完成此操作?

Oracle SQL 中没有

BINARY_INTEGER
数据类型(它存在于 PL/SQL 中)。 Oracle 中不存在您所要求的内容;只有
NUMBER
BINARY_FLOAT
BINARY_DOUBLE


数据库驱动程序

但是,您所要求的与Oracle数据库的内部无关;相反,它与与数据库通信的数据库驱动程序如何将值返回给第三方编程语言有关。

例如,.NET 数据提供程序开发人员指南文档中有一个关于“将 Oracle 数据类型映射到 EDM 类型”的部分:

表 4-1 Oracle 数据类型和 EDM 类型的映射

Oracle 数据类型二进制_双精度二进制_浮点数二进制_整数浮动国际数字(1,0)数字(2,0)数字(6,0)数字(7,0)数字(11,0)数字(12,0)数量(所有其他情况)小林特
EDM 类型(Primitive-TypeKind)
单人
Int32
十进制
Int32
数字(3,0)
数字(4,0)
数字(5,0)

Int16
数字(8,0)
数字(9,0)
数字(10,0)

Int32
数字(13,0)
数字(14,0)
数字(15,0)
数字(16,0)
数字(17,0)
数字(18,0)
数字(19,0)

Int64
十进制
Int16
这表明,对于那个司机来说,你想要的已经完成了。


1
投票

在甲骨文中:

CREATE TABLE my_schema.T(c1 NUMBER(9,0) NOT NULL, c2 NUMBER NOT NULL)

以下完整的 C# 控制台应用程序可以粘贴到新项目的 Program.cs 上进行演示:

using System.Data; using Oracle.ManagedDataAccess.Client; // Normally you want to use Dapper or EF and not the raw driver calls. // The presence of the SQL and cstr in this file are for demonstration purposes only. var connection = new OracleConnection(); connection.ConnectionString = "SOME CONNECTION STRING"; using var cmd = new OracleCommand(); cmd.CommandText = """ SELECT c1, c2, CAST(C1 AS INTEGER) i1, CAST(C2 AS INTEGER) i2, TRUNC(C1) t1, TRUNC(C2) t2, ROUND(C1, 0) r1, ROUND(C2, 0) r2, CAST(ROUND(C1, 0) AS NUMBER(9, 0)) rc1, CAST(ROUND(C2, 0) AS NUMBER(9, 0)) rc2, CAST(C1 AS NUMBER(18, 0)) rc1, CAST(C2 AS NUMBER(18, 0)) rc2 FROM my_schema.T """; cmd.Connection = connection; connection.Open(); using IDataReader reader = cmd.ExecuteReader(); while(reader.Read()) { for(var columnCount = 0; columnCount < reader.FieldCount; columnCount++) { Type type = reader.GetFieldType(columnCount); var val = reader.GetValue(columnCount); var fieldName = reader.GetName(columnCount); Console.WriteLine($"{fieldName}:{val}:{type}"); } }

这产生:

C1:100:System.Int32 C2:100:System.Decimal <-- Want C2 as a NUMBER(9,0) or similar I1:100:System.Decimal I2:100:System.Decimal T1:100:System.Decimal T2:100:System.Decimal R1:100:System.Decimal R2:100:System.Decimal RC1:100:System.Int32 <-- ✅ RC2:100:System.Int32 <-- ✅ RC1:100:System.Int64 <-- ✅ RC2:100:System.Int64 <-- ✅

最后四行 Int32 和 Int64 是我所需要的。
小数 = 不好,除非我们确实需要 128 位结构类型来存储值。

CAST()更改传出数据类型

。
我原来的帖子显示这返回了一个不受约束的数字,但显然,我错了。我最初的测试有一个 ROUND 包裹着 CAST,我没想到会删除有关精度和比例的所有信息。


0
投票

CREATE TABLE T (C1 NUMBER, C2 NUMBER(10, 0) CONSTRAINT T_C2_VALUE_CHECK CHECK(C2 BETWEEN -2147483648 AND 2147483647))

不知道/您的 ORM 是否可以利用此信息。

这里是db-fiddle

© www.soinside.com 2019 - 2024. All rights reserved.