我有一个遗留数据库,它是用数据库类型INTEGER for many (1.000+) Oracle列创建的。MS SQL存在具有相同结构的数据库。据我所知,最初的定义是使用一个工具创建的,该工具为MS SQL和Oracle生成从逻辑模型到特定模型的脚本。
使用C++和MFC,列可以很好地映射到两个数据库管理系统的整数类型。
我正在将这个应用程序移植到.NET和C#。使用相同的C#代码库来访问MS和Oracle。我们使用相同的DataSets和逻辑,我们需要相同的类型(两者都使用int32)。
Oracle的ODP.NET驱动程序将它们映射到Decimal。这是合乎逻辑的,因为Oracle自动将整数列创建为NUMBER(37)。MS SQL中的列映射到int32。
我能以某种方式控制如何映射ODP.NET驱动程序中的类型吗?我想说一些类似"map NUMBER(37) to int32“的话。列中的值永远不会超过int32的限制。我们之所以知道这一点,是因为它正在MS SQL版本中使用。
或者,我是否可以将所有列从NUMBER(37)修改为NUMBER(8)或SIMPLE_INTEGER,以便它们为我们映射到正确的类型?这些列中的许多列被用作主键(考虑自动增量)。
发布于 2015-09-22 20:56:00
关于类型映射,希望这就是你所需要的
http://docs.oracle.com/cd/E51173_01/win.122/e17732/entityDataTypeMapping.htm#ODPNT8300
关于类型转换,如果表为空,可以使用如下脚本(只需将YOUR_TABLE_NAME替换为大写的表名即可):
DECLARE
v_table_name CONSTANT VARCHAR2(30) := '[YOUR_TABLE_NAME]';
BEGIN
FOR col IN (SELECT * FROM user_tab_columns WHERE table_name = v_table_name AND data_type = 'NUMBER' AND data_length = 37)
LOOP
EXECUTE IMMEDIATE 'ALTER TABLE '||v_table_name||' MODIFY '||col.column_name||' NUMBER(8)';
END LOOP;
END;如果其中一些列不为空,则不能降低它们的精度
如果您没有太多数据,可以将其移动到临时表中
create table temp_table as select * from [YOUR_TABLE_NAME]然后截断原始表
truncate [YOUR_TABLE_NAME]然后运行上面的脚本
然后将数据移回
insert /*+ append */ into [YOUR_TABLE_NAME] select * from temp_table
commit如果数据量很大,最好移动一次。在这种情况下,使用正确的数据类型和所有索引、约束等创建新表,然后移动数据,然后重命名这两个表,以使新表具有正确的名称,这样会更快。
发布于 2015-09-23 02:42:53
不幸的是,.NET和Oracle之间的数值类型映射是在OracleDataReader类中硬编码的。
通常,我更喜欢在数据库中设置适当的数据类型,因此如果可能,我会更改列数据类型,因为它们更好地表示实际值,并且它们的constraints.
然后,您还有一些应用程序实现选项:
但是,这不适用于ExecuteScalar,因为此方法永远不会公开底层数据读取器。
var connection = new OracleConnection("DATA SOURCE=HQ_PDB_TCP;PASSWORD=oracle;USER ID=HUSQVIK");
connection.Open();
var command = connection.CreateCommand();
command.CommandText = "SELECT 1 FROM DUAL";
var reader = command.ExecuteDatabaseReader();
reader.Read();
Console.WriteLine(reader[0].GetType().FullName);
Console.WriteLine(reader.GetFieldType(0).FullName);
public static class DataReaderExtensions
{
private static readonly FieldInfo NumericAccessorField = typeof(OracleDataReader).GetField("m_dotNetNumericAccessor", BindingFlags.NonPublic | BindingFlags.Instance);
private static readonly object Int32DotNetNumericAccessor = Enum.Parse(typeof(OracleDataReader).Assembly.GetType("Oracle.DataAccess.Client.DotNetNumericAccessor"), "GetInt32");
private static readonly FieldInfo MetadataField = typeof(OracleDataReader).GetField("m_metaData", BindingFlags.NonPublic | BindingFlags.Instance);
private static readonly FieldInfo FieldTypesField = typeof(OracleDataReader).Assembly.GetType("Oracle.DataAccess.Client.MetaData").GetField("m_fieldTypes", BindingFlags.NonPublic | BindingFlags.Instance);
public static OracleDataReader ExecuteDatabaseReader(this OracleCommand command)
{
var reader = command.ExecuteReader();
var columnNumericAccessors = (IList)NumericAccessorField.GetValue(reader);
columnNumericAccessors[0] = Int32DotNetNumericAccessor;
var metadata = MetadataField.GetValue(reader);
var fieldTypes = (Type[])FieldTypesField.GetValue(metadata);
fieldTypes[0] = typeof(Int32);
return reader;
}
}我实现了命令执行的扩展方法,返回读取器,我可以在其中设置所需的列数字类型。不设置数字访问器(它只是内部枚举Oracle.DataAccess.Client.DotNetNumericAccessor),您将获得System.Decimal,设置了访问器,您将获得Int32。使用它,你可以得到所有的Int16,Int32,Int64,Float或Double。VARCHAR是一个列索引,它将只应用于数字类型,如果columnNumericAccessors是DATE或VARCHAR列,则忽略数字访问器。如果您的实现没有公开特定于提供程序的类型,请在IDbCommand或DbCommand上创建扩展方法,然后将DbDataReader安全地强制转换为OracleDataReader。
编辑:为GetFieldType方法添加了hack。但可能会发生静态映射哈希表可能会被更新,因此这可能会产生不必要的影响。您需要正确地测试它。fieldTypes数组保存为数据读取器的所有列返回的类型。
https://stackoverflow.com/questions/32604384
复制相似问题