我正在使用ODBC 2从SQL Server移植到Oracle的C/C++应用程序.对于超过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.
为什么ODBC没有完全使用自己的SQL_NUMERIC_STRUCT?我不知道.看起来它有效,但我认为这只是太多的工作.
我想我会使用SQL_C_CHAR.
最佳答案 我个人的偏好是使绑定变量字符串(VARCHAR2),并让Oracle从字符转换到它自己的内部存储格式.很容易(在C中)以可接受的格式获得表示为空终止字符串的数据值.
所以,而不是像这样编写SQL:
SET MY_NUMBER_COL = :b1
, MY_DATE_COL = :b2
我这样编写SQL:
SET MY_NUMBER_COL = TO_NUMBER( :b1 )
, MY_DATE_COL = TO_DATE( :b2 , 'YYYY-MM-DD HH24:MI:SS')
并提供字符串作为绑定变量.
这种方法有几个优点.
一个是解决与绑定其他数据类型遇到的问题和错误.
另一个优点是绑定值更容易在Oracle事件10046跟踪上进行解密.
另外,EXPLAIN PLAN(我相信)期望所有绑定变量都是VARCHAR2,这意味着所解释的语句与正在执行的实际语句略有不同(由于实际绑定参数的数据类型时隐式数据转换语句不是VARCHAR2.)
而且(不太重要)当我在TOAD中测试语句时,只需能够在输入框中键入字符串就更容易了,而不必在下拉列表框中更改数据类型.
我还让buitin TO_NUMBER和TO_DATE函数验证数据. (至少在早期版本的Oracle中,我遇到了直接绑定DATE值的问题,并且绕过了(至少某些)有效性检查,并允许无效的日期值存储在数据库中.
根据过去的经验,这只是个人偏好.我使用与Perl DBD相同的方法.
我想知道Tom Kyte(asktom.oracle.com)对这个话题有什么看法?