12 de abril de 2018

SQLXMLBulkLoad funciona

Artículo original: SQLXMLBulkLoad Rocks!
http://doughennig.blogspot.com/2006/07/sqlxmlbulkload-rocks.html
Autor: Doug Hennig
Traducido por: Ana María Bisbé York


Por razones que serán obvias en septiembre, he estado trabajando últimamente en actualizar los datos a SQL Server 2005. Existen varios mecanismos que se pueden utilizar para hacerlo; pero todos tienen sus pequeños inconvenientes bajo ciertas condiciones. Por ejemplo, es lento al utilizar una serie de instrucciones INSERT, mientras que insertar un volumen grande no trabaja con campos memo. Al buscar otra cosa en los "SQL Server Books Online", pasé por un tema sobre carga masiva a XML. Luego de jugar un poco, parecía fácil de hacer, hice algunas pruebas en un archivo relativamente grande (365,741 registros) que contiene archivos memo (lo que significa que no podía usar INSERT masivo). Utilizando otros mecanismos, me tomó más de dos horas cargar los datos a una tabla SQL Server. Utilizando la carga masiva XML, me tomó 11 minutos. Eso es un 90 % de mejora en la velocidad. ¡ Me encanta hacer las cosas más rápido !

Utilizar la carga masiva XML es sencillo: instancie SQLXMLBulkLoad.SQLXMLBulkload.4.0, configure su propiedad ConnectionString a una cadena de conexión OLE DB, establezca adecuadamente algunas propiedades (por ejemplo, KeepNulls = .T. para insertar nulos en lugar de los valores predeterminados para los valores de las columnas), y llame a Execute, pasando el nombre del archivo de esquema y el nombre para el archivo XML. Execute lanza un error si hay algo mal con alguno de los archivos, y los errores al importar se guardan en un archivo cuyo nombre se almacena en la propiedad ErrorLogFile.

Hay algo un poco complejo al trabajar con datos VFP: mientras la función CURSORTOXML() puede crear el archivo de esquema, necesita ser afinado cuando se trabaja con carga masiva XML. Además, los campos DateTime tienen que ser señalizados con el atributo sql:datatype="dateTime" (esto último fue documentado, lo demás me costó solucionarlo tras algunas pruebas y errores).

He aquí el programa genérico que hace la carga desde un cursor VFP abierto a una tabla SQL abierta.

*==============================================================================
* Función: BulkXMLLoad
* Objetivo: Realizar una carga masiva de XML a SQL Server
* Autor: Doug Hennig
* Última revisión: 07/06/2006
* Parámetros: tcAlias - alias del cursor a exportar
* tcTable - nombre de la tabla hacia la que se importa
* tcDatabase - base de datos a la que pertenece la tabla
* tcServer - nombre del servidor SQL 
* tcUserName - nombre de usuario para la conexión (opcional:
* si no se especifica, se utiliza, la Seguridad Integrada de Windows 
* tcPassword - palabra clave para la conexión (opcional:
* si no se especifica, se utiliza, la Seguridad Integrada de Windows 
* Devuelve: cadena vacía si la carga masiva ha sido exitosa 
* o texto con un mensaje de error si ha fallado
* Entorno de entrada: debe estar abierto el alias especificada en tcAlias 
* deben existir la base de datos y tabla especificadas
* el servidor especificado debe estar accesible
* debe existir espacio de disco suficiente  para los archivos XML 
* Entorno de salida: si es devuelta una cadena vacía, los datos fueron 
* importados a la tabla especificada 
*==============================================================================
lparameters tcAlias, ;
  tcTable, ;
  tcDatabase, ;
  tcServer, ;
  tcUserName, ;
  tcPassword
  local lnSelect, ;
  lcSchema, ;
  lcData, ;
  lcReturn, ;
  loException as Exception, ;
  lcXSD, ;
  loBulkLoad
* Crea los archivos de dato y de esquema para el XML.
lnSelect = select()
select (tcAlias)
lcSchema = forceext(tcTable, 'xsd')
lcData = forceext(tcTable, 'xml')
try
  cursortoxml(alias(), lcData, 1, 512 + 8, 0, lcSchema)
  lcReturn = ''
catch to loException
  lcReturn = loException.Message
endtry

* Convierte el formato XSD en un formato aceptable por SQL Server.
* Añade el espacio de nombre SQL,
* convierte las etiquetas inicio y fin ,
* utiliza el atributo sql:datatype pra campos DateTime fields,
* y especifica la tabla importada con el atributo sql:relation.
if empty(lcReturn)
  lcXSD = filetostr(lcSchema)
  lcXSD = strtran(lcXSD, ':xml-msdata">', ;
    ':xml-msdata" xmlns:sql="urn:schemas-microsoft-com:mapping-schema">')
  lcXSD = strtran(lcXSD, 'IsDataSet="true">', ;
    'IsDataSet="true" sql:is-constant="1">')
  lcXSD = strtran(lcXSD, '<xsd:choice maxOccurs="unbounded">', ;
    '<xsd:sequence>')
  lcXSD = strtran(lcXSD, '</xsd:choice>', ;
    '</xsd:sequence>')
  lcXSD = strtran(lcXSD, 'type="xsd:dateTime"', ;
    'type="xsd:dateTime" sql:datatype="dateTime"')
  lcXSD = strtran(lcXSD, 'minOccurs="0"', ;
    'sql:relation="' + lower(tcTable) + '" minOccurs="0"')
  strtofile(lcXSD, lcSchema)

  * Instancia el objeto SQLXMLBulkLoad, configura su propiedad ConnectionString
  * y otras propiedades, y llama a Execute para realizar la importación masiva.
  try
    loBulkLoad = createobject('SQLXMLBulkLoad.SQLXMLBulkload.4.0')
    lcConnString = 'Provider=SQLOLEDB.1;Initial Catalog=' + tcDatabase + ;
      ';Data Source=' + tcServer + ';Persist Security Info=False;'
    if empty(tcUserName)
      lcConnString = lcConnString + 'Integrated Security=SSPI'
    else
      lcConnString = lcConnString + 'User ID=' + tcUserName + ;
        ';Password=' + tcPassword
    endif empty(tcUserName)
    loBulkLoad.ConnectionString = lcConnString
    *** Puede configurar la propiedad ErrorLogFile con el nombre 
    *** del archivo para escribir errores de importación
    loBulkLoad.KeepNulls = .T.
    loBulkLoad.Execute(lcSchema, lcData)
    lcReturn = ''
  catch to loException
    lcReturn = loException.Message
  endtry
  * Código de limpieza y cierre.
  erase (lcSchema)
  erase (lcData)
endif empty(lcReturn)
select (lnSelect)
return lcReturn

No hay comentarios. :

Publicar un comentario

Los comentarios son moderados, por lo que pueden demorar varias horas para su publicación.