lunes, 4 de diciembre de 2017

Utilidades SQL - Convertir datos delimitados a filas

Saludos,

Supongamos que tenemos una tabla con un campo que almacena una lista de datos separados por algun delimitador, como podemos convertir esa cadena en filas?

Aunque no es lo recomendado almacenar datos concatenados en un campo, sino utilizar tablas detalle, a veces nos topamos con este escenario.

Ejemplo de una cadena que tiene concatenado 3 codigos de almacen. A continuación 3 opciones para usar:

 XMLTABLE
 
SQL>SELECT TRIM (COLUMN_VALUE) CODIGO FROM XMLTABLE (REPLACE ('"' || 'ALM01,ALM02,ALM03' || '"', ',', '","'));
CODIGO
--------------------------------------------------------------------------------
ALM01
ALM02
ALM03



REGEXP_SUBSTR/CONNECT BY
 
SQL>SELECT REGEXP_SUBSTR('ALM01,ALM02,ALM03','[^,]+',1,LEVEL) CODIGO
FROM DUAL
CONNECT BY LEVEL <= LENGTH(REGEXP_REPLACE('ALM01,ALM02,ALM03','[^,]+')) + 1;

CODIGO
--------------------------------------------------------------------------------
ALM01
ALM02
ALM03
 
 
SYS.DBMS_DEBUG_VC2COLL
 
SQL>SELECT COLUMN_VALUE
FROM TABLE(SYS.DBMS_DEBUG_VC2COLL('ALM01', 'ALM02', '
ALM03'));

CODIGO
--------------------------------------------------------------------------------
ALM01
ALM02
ALM03



Nota: SYS.DBMS_DEBUG_VC2COLL No recibe cadena concatenada, solo se pasa como parametros cada texto que deseamo convertir a filas.

Ahora veamos un ejemplo mas, supogamos que tenemos una tabla con los nombres de vendedores y en el se encuentra los dias libres, ahora queremos geenrar un calendario semana de dias habiles vs laborables.
 --------------------------------------------------------------------------------
CREATE TABLE VENDEDOR
(
COD_VENDEDOR VARCHAR2(5),
NOMBRE VARCHAR2(200),
DIAS_LIBRES VARCHAR2(200),
PRIMARY KEY (COD_VENDEDOR)
);

INSERT INTO VENDEDOR(COD_VENDEDOR,NOMBRE,DIAS_LIBRES)
VALUES('001','VICTOR','LUNES,MARTES');
INSERT INTO VENDEDOR(COD_VENDEDOR,NOMBRE,DIAS_LIBRES)
VALUES('002','MANUEL','MIERCOLES,JUEVES');
INSERT INTO VENDEDOR(COD_VENDEDOR,NOMBRE,DIAS_LIBRES)
VALUES('003','RAFAEL','DOMINGO,LUNES');


En este ejemplo usar XMLTABLE para transforma la cadena separada por "," a filas
--------------------------------------------------------------------------------
SELECT V.*, TRIM (COLUMN_VALUE) DIAS
FROM VENDEDOR v,
XMLTABLE (REPLACE ('"' ||V.DIAS_LIBRES|| '"', ',', '","'));

Listo, como ven del campo DIAS_LIBRES se descompone a filas representado en el campo dias.

Ahora  nos queda presntarlo como tabla en donde las filas son los vendedores y las columnas los dias, la interseccion de estos representaran en 1=LIBRE y 0=Laborable. Para esto nos apoyaremos del comando PIVOT.
--------------------------------------------------------------------------------
SELECT * FROM
    (  SELECT V.*, TRIM (COLUMN_VALUE) DIAS FROM VENDEDOR V,XMLTABLE (REPLACE ('"' ||V.DIAS_LIBRES|| '"', ',', '","')))
PIVOT (COUNT(COD_VENDEDOR) FOR DIAS IN ('LUNES', 'MARTES', 'MIERCOLES','JUEVES','VIERNES','SABADO','DOMINGO'))
 

Listo, ya tenemos nuestro calendario de dias libres!!


miércoles, 6 de septiembre de 2017

Oracle12C DBLINK hacia SQLSERVER

Oracle nos permite acceder de forma remota a base de datos heterogeneas (sean o no Oracle) que usan consultas SQL (como por ejemplo MS Access, SQL Server, MySQL, PostgreSQL etc) , esto es una gran ayuda cuando tenemos varios ambientes de base de datos que pueden ser de diferentes marcas, pero que necesitamos acceder para establecer interfaces de comunicación.



Para realizar la conectividad, vamos a realizarlo por medio de Heterogeneous Service. Vamos a mostrar un ejemplo de conexión desde Oracle a SQLSERVER y para ello tenemos que segui los pasos siguientes


  1. Crear el ODBC en el host de la base de datos Oracle
  2. Configurar el HS (Servicio heterogeneo)
  3. Configurar listener
  4. Configurar descriptor de conexion
  5. Crear el database link
Crear el ODBC en el host de la base de datos Oracle
Dependiendo de que si el motor de base de datos es para 32 o 64 bits configuraremos con el ODBC correspondiente. En nuestro ejemplo tenemos una BD Oracle 12c de 64bis por ello seleccionamos Origenes de datos ODBC (64 Bits).


En la pestaña DSN de sistema agregamos el origen de datos de SqlServer.


Especificamos nombre del DSN y el nombre SQLSERVER al DSN  del servidor donde esta la BD SqlServer


Especidicamos las credenciales de conexión


Indicamos la BD por defecto


Damos finalizar y probar la conexión



Configurar el HS (Servicio heterogeneo)
En el host de la base de datos vamos a ubicarnos en la ruta D:\oracle\product\12.1.0\dbhome_1\hs\admin, en esta ruta encontraremos el archivo initdg4odbc.ora, procederemos duplicarlo y el nuevo archivos le daremos el nombre de initSQLSERVER.ora, como ven el prefijo es init y a continuación el nombre del del DSN.


Dentro de archivo  D:\oracle\product\12.1.0\dbhome_1\hs\admin\initSQLSERVER.ora editamos la lineas
 
 initSQLSERVER.ora
#
# HS init parameters
#
HS_FDS_CONNECT_INFO = SQLSERVER
HS_FDS_TRACE_LEVEL = off


Configurar listener
Configuramos el archivos listener.ora que sen encuentra en el host de la base de datos D:\oracle\product\12.1.0\dbhome_1\NETWORK\ADMIN, se agregará al SID_LIST_LISTENER

 listener.ora
SID_LIST_LISTENER =
  (SID_LIST =
    (SID_DESC =
      (SID_NAME = CLRExtProc)
      (ORACLE_HOME = D:\oracle\product\12.1.0\dbhome_1)
      (PROGRAM = extproc)
      (ENVS = "EXTPROC_DLLS=ONLY:D:\oracle\product\12.1.0\dbhome_1\bin\oraclr12.dll")
    )
    (SID_DESC =
      (SID_NAME = SQLSERVER)
      (ORACLE_HOME = d:\oracle\product\12.1.0\dbhome_1)
      (PROGRAM = dg4odbc)
    )
  )

Configurar descriptor de conexion
Agregegamos en el descriptor D:\oracle\product\12.1.0\dbhome_1\NETWORK\ADMIN\tnsnames.ora la conexion a la base de datos
 tnsnames.ora
HS_SQLSERVER =
  (DESCRIPTION =
    (ADDRESS = (PROTOCOL = TCP)(HOST = LOCALHOST)(PORT = 1521))
    (CONNECT_DATA =
      (SERVER = DEDICATED)
      (SERVICE_NAME = SQLSERVER)
    )
    (HS = OK)
  )

Realizamos un reload en el servicio del listener o reiniciamos el servicio Listener desde el administrador de servicio

Recargar la configuración desde el LSNRCTL


Reinicio desde el administrador de servicio de windows

Verificamos que el servicio este asociado el lisntener



Crear el database link
Una vez ya configurado el los archivos procedemos a crear el databse link en la base de datos

Creación del database link
create database link "SQLSERVER" --Nombre del DBLINK
connect to "sa" --Usuario de la base de datos de SQLSERVER
identified by "sa" --Clave de
using 'HS_SQLSERVER'; --El nombre que se configuró en el descriptor

Listo, una vez ya configurado y creado el database link podemos realizar consulta desde la base de datos Oracle hacia la base de datos SqlServer, realizamos la prueba de consulta


Errores


En la version que probe 12.1.0.2 se me presentó el siguiente mensaje de error al realizar la consulta:

Error: ORA-28500
ORA-28500: la conexión de ORACLE a un sistema no Oracle ha devuelto este mensaje:
[Microsoft][ODBC SQL Server Driver][SQL Server]Incorrect syntax near 'USUARIO'. {42000,NativeErr = 102}[Microsoft][ODBC SQL Server Driver][SQL Server]Statement(s) could not be prepared. {42000,NativeErr = 8180}
ORA-02063: 2 lines precediendo a SQLSERVER
Causa
Oracle Database Gateway for ODBC 11g puts double quotes around objects in query.
Accion
1. Set EnableQuotedIdentifiers=1 in SQL Server data source configured in the odbc.ini configuration file.
2. Stop/start gateway listener
3. Run linked query again

En resumen ir al archivo D:\oracle\product\12.1.0\dbhome_1\hs\admin\initSQLSERVER.ora  y agregar la linea Set EnableQuotedIdentifiers=1, reiniciar el servicio y probar de nuevo la consulta