我刚刚被 SO 问题Binding int64 (SQL_BIGINT) as query parameter cause error during execution in Oracle 10g ODBC 中描述的问题所困扰。
我正在将使用 ODBC 2 的 C/C++ 应用程序从 SQL Server 移植到 Oracle。对于超过 NUMBER(9) 的数字字段,它使用 __int64 数据类型,该数据类型作为 SQL_C_SBIGINT 绑定到查询。显然,Oracle ODBC 不支持这种绑定。我现在必须将应用程序范围转换为另一种方法。由于我没有太多时间---这是一个意想不到的问题---我宁愿使用经过验证的解决方案,而不是反复试验。
在 Oracle 中应该使用什么数据类型绑定为例如 NUMBER(15)?是否有记录在案的推荐解决方案?你在用什么?有什么建议?
我对不需要任何额外转换的解决方案特别感兴趣。我可以轻松地以 __int64 或 char* 的形式提供和使用数字(没有千位分隔符或小数点的正常非指数形式)。任何其他格式都需要我进行额外的转换。
到目前为止我尝试过的:
SQL_C_CHAR
看起来它对我有用。我担心数字格式的可变性。但在我的用例中,这似乎无关紧要。显然只有分数点字符会随着系统语言设置而变化。
而且我不明白为什么我应该在 SQL INSERT 或 UPDATE 命令中使用显式转换(例如 TO_NUMERIC)。当我将参数与 SQL_C_CHAR 作为 C 类型和 SQL_NUMERIC(具有适当的精度和比例)作为 SQL 类型绑定时,一切正常。我无法重现任何数据损坏效果。
SQL_NUMERIC_STRUCT
我注意到 SQL_NUMERIC_STRUCT 添加了 ODBC 3.0 并决定尝试一下。我很失望。
在我的情况下就足够了,因为应用程序并没有真正使用小数。但作为一般解决方案......简单地说,我不明白。我的意思是,我终于明白它应该如何使用了。我不明白的是:为什么有人会引入这种新结构,然后让它以这种方式工作。
SQL_NUMERIC_STRUCT 具有表示任何 NUMERIC(或 NUMBER 或 DECIMAL)值及其精度和小数位数所需的所有字段。只有它们不被使用。
读取时,ODBC 设置数字的精度(基于列的精度;除了 Oracle 返回更大的精度,例如 NUMBER(15) 为 20)。但是,如果您的列有小数部分(比例 > 0),则默认情况下会被截断。要以适当的比例读取数字,您需要在获取数据之前使用 SQLSetDescField 调用设置精度和缩放自己。
幸运的是,在编写时,Oracle 尊重 SQL_NUMERIC_STRUCT 中包含的比例。但是 ODBC 规范并没有强制要求它,MS SQL Server 忽略了这个值。所以,再次回到 SQLSetDescField。
有关详细信息,请参阅HOWTO:使用 SQL_NUMERIC_STRUCT 检索数字数据和INF:如何将 SQL_C_NUMERIC 数据类型与数字数据一起使用。
为什么 ODBC 没有完全使用它自己的 SQL_NUMERIC_STRUCT?我不知道。看起来它有效,但我认为这工作太多了。
我想我会使用 SQL_C_CHAR。
我个人的偏好是使绑定变量为字符串(VARCHAR2),并让Oracle将字符转换为它自己的内部存储格式。很容易(在 C 中)以可接受的格式获取表示为空终止字符串的数据值。
因此,不要像这样编写 SQL:
SET MY_NUMBER_COL = :b1
, MY_DATE_COL = :b2
Run Code Online (Sandbox Code Playgroud)
我这样写SQL:
SET MY_NUMBER_COL = TO_NUMBER( :b1 )
, MY_DATE_COL = TO_DATE( :b2 , 'YYYY-MM-DD HH24:MI:SS')
Run Code Online (Sandbox Code Playgroud)
并提供字符串作为绑定变量。
这种方法有几个优点。
一种是解决绑定其他数据类型时遇到的问题和错误。
另一个优点是绑定值在 Oracle 事件 10046 跟踪上更容易破译。
另外,EXPLAIN PLAN(我相信)期望所有绑定变量都是 VARCHAR2,因此这意味着正在解释的语句与正在执行的实际语句略有不同(由于当实际绑定参数的数据类型时进行隐式数据转换)语句不是 VARCHAR2。)
而且(不太重要)当我在 TOAD 中测试语句时,只需在输入框中输入字符串就更容易了,而不必在下拉列表框中更改数据类型。
我还让内置的 TO_NUMBER 和 TO_DATE 函数验证数据。(至少在 Oracle 的早期版本中,我遇到了直接绑定 DATE 值的问题,它绕过了(至少部分)有效性检查,并允许无效的日期值存储在数据库中。
这只是个人偏好,基于过去的经验。我对 Perl DBD 使用相同的方法。
我想知道 Tom Kyte (asktom.oracle.com) 对这个话题有什么看法?
| 归档时间: |
|
| 查看次数: |
2143 次 |
| 最近记录: |