• • •

Oracle

Introducción

Cualquier programador que se haya enfrentado a una base de datos Oracle se habrá dado cuenta de que la información disponible por parte de Oracle es limitada, está muy dispersa y es bastante confusa. También se habrá dado cuenta de que necesita de una cuenta "de pago" para poder acceder a su base de conocimientos, por lo que tendrá que acudir a otro tipo de fuentes de información alternativas.
Desde un punto de vista muy ignorante, la explicación que encuentro se basa en el modelo de negocio de Oracle: ocultar información sobre sus sistemas para así potenciar su negocio de consultoría. Con palabras sencillas: Oracle es una base de datos para clientes de alto poder adquisitivo.

Sin entrar en otro tipo de consideraciones, el segundo problema que se encuentra un programador es la de decidir qué tipo de conexión quiere establecer con la base de datos y qué driver utilizar. En Client / Server Interoperability Support Matrix for Different Oracle Versions se puede encontrar información sobre la compatibilidad entre diferentes versiones. Una vez decidido, se encontrará con el gran problema de la instalación del driver, que será un proceso arduo cuando desee tener instalados varios drivers totalmente operativos en el mismo cliente.

Oracle pone a dsiposición de los usuarios un "engendro" llamado "Oracle Universal Installer (OUI)". Este humilde desarrollador considera que "esta cosa" lo único que hace es llenar de ponzoña la máquina donde se ejecuta:
Oracle Database 21c (21.3). Apartado: Oracle Database 21c Client (21.3) for Microsoft Windows x64 (64-bit)
Differences between Oracle Client and Client Home
How to Install Oracle Client Home 21c on Windows
Para la última versión de Oracle (23ai) no se ha localizado este "engendro". Parece ser que hasta Oracle se ha dado cuenta de su ineficiencia.

Sin embargo, Oracle también suministra instaladores, "si se puede llamar así", que instalan sólamente lo que realmente se necesita. Sin embargo, la documentación ofrecida para estos "instaladores" es realmente pobre.

Resaltar que no se ha realizado ninguna prueba con Oracle Cloud, ya que la escasez de recursos económicos disponibles en MalaCharger impiden la realización de dichos trabajos.

Drivers Oracle

  • Managed Driver

    Excel To Database lleva integrado el Managed Driver Oracle® Data Provider for .NET. Ello quiere decir que en muchas ocasiones no es necesario la instalación de drivers. Sin embargo, este driver tiene algunas limitaciones, por lo que es posible que se necesite la instalación de drivers adicionales para poder utilizar otro tipo de conexiones (OLE DB u ODBC) sin las limitaciones de este driver.

    En System Requirements Oracle indica que el driver es apropiado para acceder a bases de datos Oracle 19c o posterior. Para acceder a versiones anteriores será necesario utilizar una conexión OLE DB u ODBC e instalar el driver que permita la conexión a la versión de Oracle deseada.

    En Connection String Attributes se describen los diferentes parámetros que admite la cadena de conexión. Excel To Database establece los siguientes atributos en la cadena de conexión:
        Persist Security Info=false;Application Continuity=true;Self Tuning=true

    Excel To Database puede utilizar dos métodos alternativos para la insercción masiva de datos. Uno de ellos se basa en el uso de la caracterítica Bulk Copy. En Bulk Copy se indican los tipos de datos soportados y las diferentes restricciones del driver y dicha característica.
    Conviene destacar, como ya se indicó en "Oracle: Truncar Cadenas", que esta opción permite la insercción de CUALQUIER COSA en la base de datos. De hecho, en la documentación se especifica que las restricciones CHECK, FOREIGN KEY, DEFAULTS e INSERT TRIGGERS son automáticamente deshabilitados. También indica que los índices son reconstruidos tras la insercción masiva, momento en el cual se verifican las restricciones UNIQUE, y si existe algún problema con este tipo de restricciones, el índice quedará en un estado no usable. Ver: Integrity Constraints Affecting Oracle Bulk Copy
    Existen innumerables problemas asociados con esta característica. Como ejemplo, en los siguientes comentarios se describen algunos de ellos:
    What specifically does OracleBulkCopy do, and how can I optimize its performance?
    Oracle bulk copy duplicate rows.
    En la misma documentación se especifica que si se especifica la cláusula "EVALUATE CHECK_CONSTRAINTS" se habilitan las restricciones CHECK, pero que si algún registro no cumple dichas restricciones será eliminado (no se indica nada de si el usuario recibirá un mensaje indicando tal hecho). Sin embargo, no existe posibilidad de especificar dicha cláusula en .NET.
    Es por todo esto que se recomienda NO UTILIZAR este gran Bug Managed Driver (Bulk Copy).

    El otro método se basa en el uso de la característica Array Binding. En Array Binding se indican los tipos de datos soportados y las diferentes restricciones del driver y la característica Array Binding.
    Esta característica no es tan problemática como la anterior, aunque parece ser que tiene sus problemas con los LOB:
    Array Binding with Oracle(11g) Db and c#
    Performance problems with array binding

  • Drivers OLE DB

    La documentación de Oracle acerca del driver OLE DB se encuentra en: Oracle® Provider for OLE DB, y la documentación sobre instalaciones de cliente se encuentran en: Installing Oracle Instant Client.

    Como ya se ha mencionado, para acceder a versiones antiguas de bases de datos Oracle, o para conseguir características que el Managed Driver no posee, puede ser preciso la instalación de drivers OLE DB.
    Para las pruebas se han descargado los "ODAC Xcopy Packages" ODAC-21.7-Xcopy-64-bit.zip y ODAC-23.5-Xcopy-64.zip. Es decir, las pruebas se han realizados con las versiones de "ODAC Xcopy Packages" 21 y 23.
    Los paquetes contienen el driver Oracle Provider for OLE DB y el componente "Oracle Instant Client" necesario para su funcionamiento.
    Tenga en cuenta que posiblemente tenga que instalar Descargas de las versiones más recientes admitidas de Microsoft Visual C++ Redistributable Microsoft Visual C++ Redistributable latest supported downloads o superior y es posible que deba instalar las versiones de 64 y 32 bits.

    Dentro del paquete se encuentra el documento "readme.htm". Contiene las instrucciones para su instalación: las cuales no son muy detalladas.
    El "instalador" simplemente consiste en un fichero ".bat" que instala los componentes de una forma totalmente rudimentaria. Debe ser el usuario quien deba realizar las comprobaciones necesarias para evitar conflictos, las cuales no están detalladas en ninguna parte de la documentación de Oracle.
    Ni que decir tiene que el instalador NO FUNCIONA aunque se ejecute con permisos de administrador. Ello es debido a que el configurador (configure.bat), aunque modifique la variable de entorno PATH, la utilidad regsvr32 no es capaz de encontrar lo módulos necesarios para registrar la librería:

    REM Add <Oracle Home> to the PATH environment variable
    set PATH=%HOME_DIR_NO_TRAILING_SLASH%;%HOME_DIR_NO_TRAILING_SLASH%\bin;%PATH%
    
    REM Register OraOLEDB DLL in the registry
    echo Register OraOLEDB DLL >> install.log
    regsvr32 /s "%HOME_DIR_NO_TRAILING_SLASH%\bin\OraOLEDB.dll"
    

    La solución es añadir manualmente los dos directorios a la variable de entorno PATH del sistema y registrar la librería "OraOLEDB.dll" manualmente.
    Ante esta situación, simplemente desearle suerte al usuario, insistir en que no desespere y que lo intente una y otra vez hasta que logre que funcione.
    Si ya tiene una instalación de otro driver o cualquier cosa de Oracle, desde aquí no podemos ayudarle. Simplemente recomendamos que haga una copia de seguridad de su máquina, por que lo único seguro es que la va a necesitar. Sin embargo, puede obtener alguna idea de la siguientes respuestas:
    Installing Oracle 32-bit Client on Windows Server Already Running 64-bit Oracle Database Server
    NLS_LANG issue for two Oracle Homes on the same host, not refreshing the System and User Environment
    Is there a command to refresh environment variables from the command prompt in Windows?
    Setting NLS_LANG Value for Oracle

    Como con Oracle nunca se sabe por donde pueden surgir los problemas en las conexiones, se puede utilizar la siguiente aplicación para comprobar versiones de los drivers y librerías utilizadas: Verify Oracle Client Installation aunque no es muy eficiente, ya que obtiene la versión del archivo pero no la versión del producto. Por ejemplo, al instalar "Oracle Database Express Edition (XE)", la verisón de la librería "OraOLEDB.dll" instalada con dicho paquete es la 21.1.0.0, la misma que la instalada con "ODAC-21.7-Xcopy-64-bit.zip". Sin embargo, en la versión del producto difieren: para la librería instalada con "Oracle Database Express Edition (XE)" es 21.1.0.0.20210772, mientras que para "ODAC-21.7-Xcopy-64-bit.zip" es 21.1.0.0.20211208.

    NOTA: Tras instalar un "Oracle Database Express Edition (XE)" en Windows 10 e intentar actualizar el driver OLE DB de Oracle, el listener dejó de funcionar. ¿Qué extraño, no? Pues no: Oracle, además de consumir recursos del sistema, también consume la paciencia de los administradores, programadores y demás incautos que piensan que siguiendo las instrucciones de Oracle todo va a funcionar.

    Señalar que esta página no pretende ser un tratado sobre drivers ni cadenas de conexión, así que se recomienda consultar la página OraOLEDB-Specific Connection String Attributes.

    Excel To Database establece los siguientes atributos en la cadena de conexión:
        Persist Security Info=False;OLEDB.NET=True;NDatatype=True;SPPrmsLOB=False;UseSessionFormat=True;Application Name=Excel To Database

    El driver "Microsoft OLE DB Provider for Oracle" (MSDAORA) sólo funciona para las arquitecturas de 32 bits. Microsoft lo considera obsoleto, por lo que no es tenido en cuenta para los análisis realizados con Excel To Database.

    Destacar que las clases de .NET para manejar este tipo de conexiones, System.Data.OleDb, no se encuentran actualizadas, y puede que no manejan correctamente los tipos de datos devueltos por los controladores.

  • Drivers ODBC Oracle

    La documentación del driver Oracle ODBC Driver se encuentra en: Introduction to the Oracle Database ODBC Driver. Tener en cuenta que el cuadro de diálogo de configuración del driver Oracle Database ODBC Driver Configuration Dialog Box no aparece cuando se desea crear un DSN de archivo. En cambio, si aparece cuando se crea un DSN de usuario o de sistema.
    La documentación sobre instalaciones de cliente se encuentran en: Installing Oracle Instant Client ODBC (Windows).

    Para instalar el Oracle ODBC Driver, el primer inconveniente es encontrar la página de descarga de dicho driver. En Oracle Instant Client Downloads for Microsoft Windows (x64) 64-bit se encuentran los enlaces a las descargas de diferentes versiones del controlador.
    Dentro del archivo comprimido se encuentran las instrucciones para su instalación. Básicamente consiste en descomprimir el contenido del fichero en el directorio donde se encuentra el "Oracle Instant Client" y ejecutar el instalador con permisos de administrador.

    Para acceder a versiones antiguas de bases de datos Oracle, o para conseguir características que el Managed Driver no posee, puede ser necesario la instalación de versiones anteriores del Oracle ODBC Driver. Para ello, vea la matriz de compatibilidad indicada al inicio de este capítulo.

    Como conclusión, para instalar un par de "tristes" drivers hay que realizar un curso de ingeniería software completo. Después de cuatro o cinco décadas, Oracle es incapaz de realizar instaladores sencillos y totalmente automatizados que no llenen la máquina de ponzoña y de cosas totalmente ajenas y extrañas a las pocas librerías que realmente se necesitan.

    Por supuesto, al igual que con OLE DB, existen drivers para arquitecturas de 32 bits ("Microsoft ODBC for Oracle"), que Microsoft considera obsoleto y por tanto no son objeto de estudio en este análisis.

    Señalar que esta página no pretende ser un tratado sobre drivers ni cadenas de conexión, así que se recomienda consultar las páginas Format of the Connection String y Oracle Database ODBC Driver Configuration Dialog Box (que no aparece en los DSN de archivo pero si en los de usuario o sistema) donde se describen los diferentes atributos permitidos en la cadena de conexión.

    Excel To Database establece los siguientes atributos en la cadena de conexión:

    app=Excel To Database;apa=T;rst=T;bnf=F;lob=T;btd=F;num=NLS;dba=W;exc=F;fwc=T;mdi=F;bam=IfAllSuccessful
    
    APA Applications Attributes: T = Thread Safety Enabled.
    RST Result Sets:             T = Result Sets Enabled.
    BNF Bind NUMBER as FLOAT:    F = Bind NUMBER as NUMBER.
    LOB LOB Writes:              T = LOBs Enabled.
    BTD Bind TIMESTAMP as DATE:  F = Bind SQL_TIMESTAMP as Oracle TIMESTAMP
    NUM Numeric Settings:        NLS = Use Oracle NLS numeric settings(to determine the decimal and group separator).
    DBA Database Attribute:      W = write access.
    EXC EXEC Syntax:             F = EXEC Syntax Disabled.
    FWC Force SQL_WCHAR Support: T = Force SQL_WCHAR Enabled.
    MDI Set Metadata ID Default: F = SQL_ATTR_METADATA_ID defaults to SQL_FALSE.
    BAM Batch Autocommit Mode:   IfAllSuccessful = Commit only if all statements are successful(old behavior).
    

    Por último, resaltar que las clases de .NET para manejar este tipo de conexiones, System.Data.Odbc, no se encuentran actualizadas, y puede que no manejan correctamente los tipos de datos devueltos por los controladores.

Opciones Conexiones

Como se indica en Globalization Support, el Managed Driver es independiente de NLS_LANG y todas las variables de entorno que tanto le gustan a Oracle. La configuración de este driver se realiza desde el propio código.
Sin embargo, las conexiones de tipo OLE DB y ODBC son dependientes de la instalación realizada para el componente "Oracle Instant Client" y su configuración. En Windows, por defecto, la configuración se define en el Registro:
    HKEY_LOCAL_MACHINE\SOFTWARE\Oracle\KEY_[OracleHome]\
La clave NLS_LANG, tras la instalación de "Oracle Instant Client" queda establecida en AMERICAN_AMERICA.WE8MSWIN1252.
Sin embargo, dado el sitema de pruebas (Windows 10 en español) se establece en el Registro la clave NLS_LANG a SPANISH_SPAINWE8MSWIN1252 para que quede correctamente configurado.

Con el fin de aumentar las prestaciones de Excel To Database, cada vez que se abre una conexión a Oracle se establecen las siguientes opciones a la sesión intencionadamente:

ALTER SESSION SET NLS_LANGUAGE = "*1";
ALTER SESSION SET NLS_TERRITORY = "*2";
ALTER SESSION SET NLS_NUMERIC_CHARACTERS = ".,";
ALTER SESSION SET NLS_NCHAR_CONV_EXCP = true;
ALTER SESSION SET NLS_DATE_FORMAT = "YYYY-MM-DD HH24:MI:SS";
ALTER SESSION SET NLS_TIME_FORMAT = "HH24:MI:SS.FF7";
ALTER SESSION SET NLS_TIME_TZ_FORMAT = "HH24:MI:SS.FF7 TZH:TZM";
ALTER SESSION SET NLS_TIMESTAMP_FORMAT = "MM/DD/YYYY HH24:MI:SS.FF7";
ALTER SESSION SET NLS_TIMESTAMP_TZ_FORMAT = "MM/DD/YYYY HH24:MI:SS.FF7 TZH:TZM";
ALTER SESSION SET TIME_ZONE='*3'

*1 -> SELECT value FROM NLS_DATABASE_PARAMETERS WHERE parameter ='NLS_LANGUAGE'
*2 -> SELECT value FROM NLS_DATABASE_PARAMETERS WHERE parameter ='NLS_TERRITORY'
Es decir, la sesión se va establecer con el NLS_LANGUAGE y NLS_TERRITORY establecidas en la base de datos.

*3 -> Se establece a la zona horaria del cliente en el momento de la conexión.
Es decir, que depende del horario de invierno o de verano.


Así, cuando se configura una cadena de conexión a Oracle, Excel To Database
muestra en el Panel de Resultados la información que se obtiene de ejecutar:

SELECT * FROM NLS_SESSION_PARAMETERS;

NLS_LANGUAGE: AMERICAN
NLS_TERRITORY: AMERICA
NLS_CURRENCY: $
NLS_ISO_CURRENCY: AMERICA
NLS_NUMERIC_CHARACTERS: .,
NLS_CALENDAR: GREGORIAN
NLS_DATE_FORMAT: YYYY-MM-DD HH24:MI:SS
NLS_DATE_LANGUAGE: AMERICAN
NLS_SORT: BINARY
NLS_TIME_FORMAT: HH24:MI:SS.FF7
NLS_TIMESTAMP_FORMAT: MM/DD/YYYY HH24:MI:SS.FF7
NLS_TIME_TZ_FORMAT: HH24:MI:SS.FF7 TZH:TZM
NLS_TIMESTAMP_TZ_FORMAT: MM/DD/YYYY HH24:MI:SS.FF7 TZH:TZM
NLS_DUAL_CURRENCY: $
NLS_COMP: BINARY
NLS_LENGTH_SEMANTICS: BYTE
NLS_NCHAR_CONV_EXCP: TRUE

Se recomienda encarecidamente la lectura de la siguiente página de Oracle: NLS_LANG FAQ.

Libros, Bases de Datos

Las pruebas se van a realizar sobre Windows 10 64 bits, Oracle Database 21c Express Edition (VERSION FULL: 21.3.0.0.0) y Excel To Database de 64 bits compilada para .NET Framework 4.8. Como es de suponer, no se analizarán todos los drivers ni todas las opciones posibles, sino que se estudiarán los casos más representativos.

Con el fin de mostrar el comportamiento de las diferentes opciones de la aplicación, se han preparado una serie de tablas para MS Access, SQL Server y Oracle y varios Libros Excel. Los scripts de creación de tablas, los libros, las bases de datos MS Access y el fichero de Configuraciones para todos los capítulos se pueden descargar de: "Descarga de Ejemplos".
Los scripts de creación de tablas también se muestran en la documentación, así como las Hojas Excel utilizadas en cada momento (como imágenes).

  • Bases de Datos y Conexiones Oracle

    La base de datos construida para las pruebas tiene la configuración mostrada a continuación. Para ella, se han preparado 4 Configuraciones en Excel To Database, de las cuales, la conexión ODBC utiliza el archivo DSN mostrado a continuación:

    SELECT * FROM NLS_DATABASE_PARAMETERS;
    
    "NLS_RDBMS_VERSION"           "21.0.0.0.0"
    "NLS_NCHAR_CONV_EXCP"         "FALSE"
    "NLS_LENGTH_SEMANTICS"        "BYTE"
    "NLS_COMP"                    "BINARY"
    "NLS_DUAL_CURRENCY"           "$"
    "NLS_TIMESTAMP_TZ_FORMAT"     "DD-MON-RR HH.MI.SSXFF AM TZR"
    "NLS_TIME_TZ_FORMAT"          "HH.MI.SSXFF AM TZR"
    "NLS_TIMESTAMP_FORMAT"        "DD-MON-RR HH.MI.SSXFF AM"
    "NLS_TIME_FORMAT"             "HH.MI.SSXFF AM"
    "NLS_SORT"                    "BINARY"
    "NLS_DATE_LANGUAGE"           "AMERICAN"
    "NLS_DATE_FORMAT"             "DD-MON-RR"
    "NLS_CALENDAR"                "GREGORIAN"
    "NLS_NUMERIC_CHARACTERS"      ".,"
    "NLS_NCHAR_CHARACTERSET"      "AL16UTF16"
    "NLS_CHARACTERSET"            "AL32UTF8"
    "NLS_ISO_CURRENCY"            "AMERICA"
    "NLS_CURRENCY"                "$"
    "NLS_TERRITORY"               "AMERICA"
    "NLS_LANGUAGE"                "AMERICAN"
    
    
          Se preparan 4 Configuraciones con las siguientes conexiones a la Bases de Datos:
    
    • Conexión con Managed Driver (BulkCopy): USER ID=UsuarioOracle;PASSWORD=Contrasenia;DATA SOURCE=localhost
    • Conexión con Managed Driver (Array Binding): USER ID=UsuarioOracle;PASSWORD=Contrasenia;DATA SOURCE=localhost
    • Conexión con OLE DB: Provider=OraOLEDB.Oracle.1;Data Source=localhost;Persist Security Info=False;Password=Contrasenia;User ID=UsuarioOracle;oledb.net=True;ndatatype=False;spprmslob=False;usesessionformat=True;application name=\"Excel To Database\"
    • Conexión con ODBC: Driver={Oracle in 21_7};server=LOCALHOST;uid=UsuarioOracle;pwd=Contrasenia;dbq=LOCALHOST;dba=W;apa=T;exc=F;xsm=Default;fen=T;qto=T;frc=10;fdl=10;lob=T;rst=T;btd=F;bnf=F;bam=IfAllSuccessful;num=NLS;dpm=F;mts=T;mdi=F;csr=F;fwc=T;fbs=60000;tlo=O;mld=0;oda=F;ste=F;tsz=8192;ast=FLOAT;lps=8192;app=Excel To Database
    
          La conexión ODBC utiliza el siguiente archivo DSN:
    
    Oracle_ODBC_X64.dsn
    [ODBC]
    DRIVER=Oracle in 21_7
    UID=UsuarioOracle
    LPS=8192
    AST=FLOAT
    TSZ=8192
    STE=F
    ODA=F
    MLD=0
    TLO=O
    FBS=60000
    FWC=F
    CSR=F
    MDI=Me
    MTS=T
    DPM=F
    NUM=NLS
    BAM=IfAllSuccessful
    BNF=F
    BTD=F
    RST=T
    LOB=T
    FDL=10
    FRC=10
    QTO=T
    FEN=T
    XSM=Default
    EXC=F
    APA=T
    DBA=W
    DBQ=LOCALHOST
    SERVER=LOCALHOST
    
  • Libro Excel

    Para las pruebas realizadas se ha utilizado un Libro Excel cuyas Hojas se irán mostrando en imágenes a lo largo del capítulo.

    Destacar que para las pruebas no se ha incluido ninguna celda con error, por lo que la opción Sin Valores Error no va a ser utilizada.

    Los datos han sido preparados de forma que permitan comprobar el funcionamiento de la carga de datos. No son una batería de pruebas exhaustivas, por lo que se puede deducir que la carga de datos no tiene garantía de que se produzca como el usuario pudiese esperar ni como se describe en esta página.

    Puede descargar los ficheros de prueba desde aquí: Descarga de Ejemplos

Pruebas Oracle: Números de "Punto Flotante"

Para mostrar el comportamiento de los drivers con este tipo de datos de Oracle se utiliza la tabla cuyo script de creación es el mostrado a continuación y la Hoja Excel mostrada en la figura siguiente:

CREATE TABLE "Tabla Numeros" (
  Id NUMBER(6) GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
  Provider                  VARCHAR2(10 CHAR),
  Driver                    VARCHAR2(50 CHAR),

  "Float 5"                 FLOAT(5),
  "Float"                   FLOAT,
  "Binary Float"            BINARY_FLOAT,
  "Binary Double"           BINARY_DOUBLE
);

Téngase en cuenta que la definición de FLOAT de Oracle es un tanto extraña. El campo definido en el ejemplo como "Float 5" no puede superar los 5 dígitos "binarios". Si se supera dicha precisión, el dato será truncado. Oracle no recomienda su uso, por lo que las pruebas se limitarán a comprobar su compatibilidad con los diferentes drivers.
Sin embargo, el campo "Float", mas o menos, se comporta como el campo "Binary Double".

Oracle: Hoja Excel para Números de "Punto Flotante"

Oracle: Hoja Excel para Números de 'Punto Flotante'

Hay que destacar que el formato de celdas en la Hoja Excel es "Texto", "Número" o "Científica". Es "Texto" para aquellos casos en que se supera la precisión de Excel. En estos casos se utiliza como "Símbolo decimal" el punto ".", ya que es el separador que utiliza Excel To Database para convertir los textos a números.

En primer lugar, destacar que el Bug Managed Driver, con la opción BulkCopy tiene problemas con el tipo de datos Float (no ocurre con la opción Array Binding ni con el resto de los drivers). Así, al intentar insertar los datos en dicho campo se produce el siguiente error:

Se ha producido una excepción durante la carga de datos en la Tabla 'Tabla Numeros'.
    Query que indica las columnas utilizadas de la Tabla:
    SELECT "Float 5", "DRIVER", "PROVIDER" FROM "Tabla Numeros"
{
  "$id": "1",
  "$type": "Oracle.ManagedDataAccess.Types.OracleTruncateException, Oracle.ManagedDataAccess",
  "Message": "ORA-16550: resultado truncado
https://docs.oracle.com/error-help/db/ora-16550/",
  "Number": 16550,
  "Source": "Oracle Data Provider for .NET, Managed Driver",
  "Data": {
    "$type": "System.Collections.ListDictionaryInternal, mscorlib"
  },
  "InnerException": null,
  "HelpLink": null,
  "HResult": -2146233087
}

Los mensajes que Excel To Database muestra durante la lectura de los datos son los siguientes:

Celda: A3, Valor: 7.89
    El valor contenido en la celda ha sido convertido al tipo Double de .NET: 7.8899999999999997

Celda: B3, Valor: 7.89
    El valor contenido en la celda ha sido convertido al tipo Double de .NET: 7.8899999999999997

Celda: A4, Valor: 12.79
    El valor contenido en la celda ha sido convertido al tipo Double de .NET: 12.789999999999999

Celda: B4, Valor: 12.79
    El valor contenido en la celda ha sido convertido al tipo Double de .NET: 12.789999999999999

Celda: D6, Valor: 0.1234567890123456
    El valor contenido en la celda ha sido convertido al tipo Double de .NET: 0.12345678901234559

Celda: D10, Valor: 9.99999999999999E+307
    El valor contenido en la celda ha sido convertido al tipo Double de .NET: 9.9999999999999901E+307

Celda: C15, Valor: 1.2345678
    El valor contenido en la celda ha sido convertido al tipo Single de .NET: 1.23456776

Celda: B16, Valor: 3.4028230E+38
    El valor contenido en la celda ha sido convertido al tipo Double de .NET: 3.4028229999999999E+38

Celda: C16, Valor: 3.4028230E+38
    El valor contenido en la celda ha sido convertido al tipo Single de .NET: 3.40282306E+38

Celda: C17, Valor: 0.0000000E+00
    El valor contenido en la celda ha sido convertido al tipo Single de .NET: 0

Celda: D17, Valor: 1.797693134862325E+308, Tabla: Tabla Numeros, Columna: Binary Double
    El contenido de la Celda no ha podido convertirse al tipo de datos Double
    Se intentará cargar la celda como NULL (o el valor por defecto). Opción "Sin Errores de Conversión" marcada.

Celda: B18, Valor: 3.4028236E+38
    El valor contenido en la celda ha sido convertido al tipo Double de .NET: 3.4028235999999999E+38

Celda: C18, Valor: 3.4028236E+38, Tabla: Tabla Numeros, Columna: Binary Float
    El contenido de la Celda no ha podido convertirse al tipo de datos Single
    Se intentará cargar la celda como NULL (o el valor por defecto). Opción "Sin Errores de Conversión" marcada.

Celda: C19, Valor: 1.4012980E-45
    El valor contenido en la celda ha sido convertido al tipo Single de .NET: 1.40129846E-45

Celda: D19, Valor: 4.9406565E-307
    El valor contenido en la celda ha sido convertido al tipo Double de .NET: 4.9406565000000001E-307

Celda: C20, Valor: 1.401298E-45
    El valor contenido en la celda ha sido convertido al tipo Single de .NET: 1.40129846E-45

Celda: D20, Valor: 4.94065645841246544E-324
    El valor contenido en la celda ha sido convertido al tipo Double de .NET: 4.9406564584124654E-324

Celda: C21, Valor: 1.0E-46
    El valor contenido en la celda ha sido convertido al tipo Single de .NET: 0

Celda: D21, Valor: 1.0E-324
    El valor contenido en la celda ha sido convertido al tipo Double de .NET: 0

A continuación se detalla el comportamiento de Excel To Database y de los diferentes drivers para explicar los datos almacenados en la base de datos Oracle:

  • Comportamiento de Excel To Database:
    Debido a la imprecisión de los datos numéricos de tipo flotante, muchos de los valores leídos no coinciden con el valor convertido al tipo de datos correspondiente de .NET. Son las celdas "A3:B4", "D6", "D10", "C15:C16", "B16", "B18" y "C19:D21".
    Y las celdas "D17" y "C18" exceden la capacidad de .NET, y por ello no pueden ser leídas. Con la opción "Sin Errores de Conversión" marcada son sustituidas por el valor por defecto indicado en el "Panel de Mapeos".
    Un caso especial es el de la celda "D19". El Formato de Celda es Científica con 7 decimales, pero supera las capacidades de Excel. Así, el dato es almacenado como texto en la Hoja Excel. Pero dado que el idioma del sistema operativo es el español, Excel almacena el número como texto utilizando como el separador decimal la coma (","). Excel To Database lo interpreta incorrectamente, ya que internamente utiliza como separador decimal el punto ("."). Es por ello que el exponente no coincide con el mostrado en Excel.
    Sin embargo, en la celda D20 contiene el mismo dato pero con formato Texto, y con separador decimal el punto ("."). Excel To Database lo interpreta correctamente, y dada la precisión de 15 dígitos para el tipo de datos Double de .NET, el dato es redondeado a 15 dígitos.

  • Comportamiento de Oracle:
    Como casos destacados, las celdas "C19:C20" exceden la precisión del campo, por lo que son redondeadas por la base de datos, almacenándose en el campo 1.4E-45. Lo mismo sucede con la celda "D20", que almacena 4.9E-324. El caso de la celda "D19" ya se ha comentado anteriormente.

  • Comportamiento de Managed Driver:
    En primer lugar, destacar que el campo "Float 5" no se puede cargar con la opción BulkCopy, como ya se ha mencionado antes. Sin embargo, se puede cargar perfectamente con la opción Array Binding, ya que Oracle trunca el dato adecuadamente.
    Destacar que por alguna extraña razón, las celdas con imprecisiones son almacenadas correctamente: "B3:B4", "D6", "D10", "C15:C16", "B16", "B18" y "C19:C20". Los casos para "D19" y "D20" ya se han comentado. Con Array Binding también se almacenan correctamente las celdas "A3:A4".

  • Comportamiento de Oracle ODBC Driver:
    Con este driver, no se produce la extraña razón comentada anteriormente, por lo que las celdas "B3:B4", "B16" y "B18" con cargadas con el dato de .NET. En otras palabras, no se redondean con el driver, como si parece que se hace con el Managed Driver y con OraOLEDB.Oracle
    Con las celdas "D6", "D10" y "C15:C16" si se produce ese redondeo misterioso.
    Los casos de las celdas "C19:D20" ya se han comentado con anterioridad.

  • Comportamiento de Oracle Provider for OLE DB (OraOLEDB.Oracle):
    El comportamiento de este driver es el mismo que el descrito para el Managed driver con la opción Arrary Binding excepto para los datos de tipo BINARY_DOUBLE. En este caso el driver tiene el siguiente bug cuando los datos son número grandes. Así, para las celdas "D7:D10" hace que se produzca un error de desbordamiento. Este error de desbordamiento se produce durante la fase de carga de datos en la tabla y hace que se detenga dicha fase.
    Nota: Los errores que se producen durante esta fase hace no se continúe con el resto de las cargas.

    Fila: 6
    Comando: INSERT INTO "Tabla Numeros" ("Binary Double", "DRIVER", "PROVIDER") VALUES (?, ?, ?)
    Comando Select: SELECT "Binary Double", "DRIVER", "PROVIDER" FROM "Tabla Numeros"
    Excepción:
    {
      "$id": "1",
      "$type": "System.Data.OleDb.OleDbException, System.Data",
      "ErrorCode": -2147217900,
      "Errors": {
        "$type": "System.Data.OleDb.OleDbErrorCollection, System.Data",
        "$values": [
          {
            "$id": "2",
            "$type": "System.Data.OleDb.OleDbError, System.Data",
            "Message": "ORA-01426: numeric overflow",
            "NativeError": 0,
            "Source": "OraOLEDB",
            "SQLState": ""
          }
        ]
      },
      "Message": "ORA-01426: numeric overflow",
      "Data": {
        "$type": "System.Collections.ListDictionaryInternal, mscorlib"
      },
      "InnerException": null,
      "HelpLink": null,
      "Source": "OraOLEDB",
      "HResult": -2147217900
    }
    
        Columna: Binary Double    Valor: 1.79769313486232E+308
        Columna: DRIVER    Valor: OraOLEDB.Oracle
        Columna: PROVIDER    Valor: OLE DB
    Se ha producido una excepción al intentar cargar la Tabla 'Tabla Numeros'. Error en la Fila: 6
        Query que indica las columnas utilizadas de la Tabla:
        SELECT "Binary Double", "DRIVER", "PROVIDER" FROM "Tabla Numeros"
    {
      "$id": "1",
      "$type": "System.Data.OleDb.OleDbException, System.Data",
      "ErrorCode": -2147217900,
      "Errors": {
        "$type": "System.Data.OleDb.OleDbErrorCollection, System.Data",
        "$values": [
          {
            "$id": "2",
            "$type": "System.Data.OleDb.OleDbError, System.Data",
            "Message": "ORA-01426: numeric overflow",
            "NativeError": 0,
            "Source": "OraOLEDB",
            "SQLState": ""
          }
        ]
      },
      "Message": "ORA-01426: numeric overflow",
      "Data": {
        "$type": "System.Collections.ListDictionaryInternal, mscorlib"
      },
      "InnerException": null,
      "HelpLink": null,
      "Source": "OraOLEDB",
      "HResult": -2147217900
    }
    

Se quiere hacer notar que actualmente Excel To Database utiliza las capacidades de .NET para la lectura de los datos numéricos. Pero en sucesivas versiones esta funcionalidad será modificada de forma que dichas capacidades se adapten a lo que Oracle o SQL Server pueden ofrecer.

Pruebas Oracle: Fechas

Como complemento a las pruebas aquí realizadas, se recomienda la lectura del apartado dedicado a las fechas en MS Access: Pruebas MS Access: Fechas y SQL Server: Pruebas SQL Server: Fechas.

Excel To Database realiza el siguiente mapeo de tipos de datos de Oracle a tipos de datos de .NET:

  • Proveedor de Datos .NET (Managed Driver): Date -> OracleDate, TimeStamp -> OracleTimeStamp, TimeStamp WITH TIME ZONE -> OracleTimeStampTZ, TimeStamp WITH LOCAL TIME ZONE -> OracleTimeStampLTZ

  • Proveedor de Datos OLE DB (System.Data.OleDb): Date, TimeStamp, TimeStamp WITH TIME ZONE, TimeStamp WITH LOCAL TIME ZONE -> String

  • Proveedor de Datos ODBC (System.Data.Odbc): Date, TimeStamp, TimeStamp WITH TIME ZONE y TimeStamp WITH LOCAL TIME ZONE -> String

Dado que el Managed Driver tiene problemas para la insercción de datos cuando éstos no cumplen las restricciones del campo de la base de datos, la única solución encontrada es la de utilizar las estructuras de Oracle. Sin embargo, éstas están tan sumamente mal programadas que es necesario preprocesar los datos previamente con .NET. Este preprocesado podrá dar lugar a que la precisión alcanzada con los tipos de datos de .NET sea inferior a las capacidades de los campos de Oracle.

Esto no ocurre con los proveedores de datos OLE BD ni ODBC. Para ellos, los datos se pueden insertar como cadenas de caracteres. Así, serán los proveedores de datos y la propia base de datos quienes realicen los redondeos y recortes que los programadores de los mismos consideraron como oportunos.

Para las pruebas se ha utilizado la siguiente tabla y la siguiente hoja:

CREATE TABLE "Tabla Fechas" (
  Id NUMBER(6) GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
  Provider                          VARCHAR2(10 CHAR),
  Driver                            VARCHAR2(50 CHAR),

  "Date"                            DATE,
  "Timestamp2"                      TIMESTAMP(2),
  "Timestamp2 with time zone"       TIMESTAMP(2) WITH TIME ZONE,
  "Timestamp2 with local time zone" TIMESTAMP(2) WITH LOCAL TIME ZONE
);

Oracle: Hoja Excel para Fechas

Oracle: Hoja Excel para Fechas.

Dado que el manejo de fechas e intervalos en Oracle es complejo, se pone la siguiente página como ayuda mediante ejemplos para comprender su funcionamiento: How to use DATEs, TIMESTAMPs, and INTERVALs in Oracle Database - SQL Office Hours script

En primer lugar, destacar que el Bug Oracle Provider for OLE DB (OraOLEDB.Oracle). No es capaz de almacenar las fracciones de segundo.

Bien Oracle, o bien los drivers, redondean los datos con MidpointRounding.AwayFromZero. Ver MidpointRounding Enumeración.

También destacar el Bug Oracle Provider for OLE DB (OraOLEDB.Oracle) y el Bug Oracle ODBC Driver para el tipo de datos "TIMESTAMP WITH TIME ZONE".
Con el Managed Driver si se almacena correctamente la zona horaria de las celdas "C4:C7": A las 3:00:00 del día 27 de Octubre de 2024 se cambió la hora en España. La nueva hora será las 2:00:00 y la diferencia horaria con GMT pasa de ser +02:00 a +01:00. Este echo queda prefectamente reflejado en los datos almacenados en la base de datos.
Sin embargo, con ODBC y OLE DB sólo es capaz de almacenar la zona horaria del cliente en el momento de la ejecución de las Configuraciones, no la que se puede deducir por el dato.

El tipo de datos "TIMESTAMP WITH LOCAL TIME ZONE" tiene un comportamiento extrañísimo. Este humilde ingeniero ha sido incapaz de descifrar de dónde salen unos 50 minutos y 20 segundos de diferencia con determinados datos con cualquiera de los drivers.

Por último, indicar que aunque Oracle permite la insercción de datos de sólo hora a estos campos añadiéndoles el primer día del mes en que se ejecuta la insercción, Excel To Database no lo permite. Así, las celdas "A10:D11" y "A13:D13" se consideran como datos que no pueden insertarse en los campos correspondientes.

Pruebas Oracle: Intervalos

Excel To Database realiza el siguiente mapeo de tipos de datos de Oracle a tipos de datos de .NET:

  • Proveedor de Datos .NET (Managed Driver): Interval YEAR TO MONTH -> OracleIntervalYM e Interval DAY TO SECOND -> OracleIntervalDS

  • Proveedor de Datos OLE DB (System.Data.OleDb): Interval YEAR TO MONTH e Interval DAY TO SECOND -> String

  • Proveedor de Datos ODBC (System.Data.Odbc): El Bug Oracle ODBC Driver hace que no se reconozcan los tipos de datos "Interval YEAR TO MONTH" e "Interval DAY TO SECOND". Cuando se intenta obtener la información del tipo de datos para estos campos aparece el siguiente error (el cual no tiene ningúm mensaje asociado):

    Se ha producido una excepción al intentar cargar la Tabla 'Tabla Intervalos'. Error en la Fila: 0
        Query que indica las columnas utilizadas de la Tabla:
        SELECT "Interval day (3) to second (2)", "Interval year (2) to month", "PROVIDER", "DRIVER" FROM "Tabla Intervalos"
    {
      "$id": "1",
      "$type": "System.Data.Odbc.OdbcException, System.Data",
      "Errors": {
        "$type": "System.Data.Odbc.OdbcErrorCollection, System.Data",
        "$values": []
      },
      "Source": "",
      "ErrorCode": -2146232009,
      "Message": "",
      "Data": {
        "$type": "System.Collections.ListDictionaryInternal, mscorlib"
      },
      "InnerException": null,
      "HelpLink": null,
      "HResult": -2146232009
    }
    

Para las pruebas se ha utilizado la siguiente tabla y la siguiente hoja:

CREATE TABLE "Tabla Intervalos" (
  Id NUMBER(6) GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
  Provider                          VARCHAR2(10 CHAR),
  Driver                            VARCHAR2(50 CHAR),

  "Interval year (2) to month"      INTERVAL YEAR (2) TO MONTH,
  "Interval day (3) to second (2)"  INTERVAL DAY (3) TO SECOND (2)
);

Oracle: Hoja Excel para Intervalos

Oracle: Hoja Excel para Intervalos.

Bien Oracle, o bien los drivers, redondean los datos con MidpointRounding.ToEven. Ver MidpointRounding Enumeración.

Excel interpreta incorrectamente el Formato de celda [h]:mm:ss. Así, la celda "B6" presenta 564:59:59 que debería ser equivalente a 23 días con 12:59:59. Sin embargo, Excel almacena la fecha 23/01/1900 12:59:59. Dados los problemas de Excel con las fechas (1900 no fue bisiesto, la fecha de referencia 31/12/1899 y demás) hace que en la base de datos se almacena el intervalo +24 12:59:59.000000.
Lo mismo sucede con el contenido de la celda "B23". Es el mismo dato de la celda "B6" pero con otro formato de celda.
Sin embargo, el dato de la celda "B18" con Formato de Celda "Texto" es almacenado correctamente en la base de datos: +23 12:59:59.600000.

Existe un Bug Oracle Provider for OLE DB (OraOLEDB.Oracle): Las celdas "B7:B8", "B16:B17" son redondeadas incorrectamente. Así, las fracciones de segundo ".005" y ".006" son almacenadas como ".1", cuando debería almacenarse como ".01".

Como conclusión después de realizar todas las pruebas mostradas en los diferentes capítulos para Oracle, desde aquí se recomienda usar el Managed Driver (Array Binding), siendo esta opción, además, la más rápida.

Preguntas Frecuentes

¿Cómo ver el código de error ORA en Excel To Database?

Consultando la propiedad StatusCode en el log JSON exportado tras el fallo.