none
INSERCION DE REGISTROS EN SQL CONSUME MUCHO TIEMPO

    Question

  • Saludos amigos. Tengo una aplicación que originalmente la hice para trabajar con bases de datos en SQLCe, y recientemente hice los cambios para migrar a SQL Express, usando SQL Server 2008 R2. La aplicación la hice con Visual Studio 2010.

    El asunto es que diariamente se ejecuta un proceso en el que se toman aproximadamente unos 400 registros desde un datagridview y se insertan en una base de datos que pesa alrededor de 75 megas, y que actualmente tiene unos 385.000 registros. Hasta hace unos días, todavía trabajaba con SQLCe y este proceso demoraba no más de unos 10 segundos, pero ahora usando SQL Express me esta demorando como 5 minutos, lo cual es demasiado tiempo, mas aún cuando yo esperaba que al ser un sistema de base de datos basado en servicio fuera mucho mas rapido. Honestamente, no tengo idea de donde se puede ubicar el origen del problema. Viendo el Administrador de Tareas de windows, observo parametros normales, no hay incrementos en el consumo de memoria RAM ni tampoco alta actividad de la CPU. A continuación les pongo el codigo, y les agradecería a quienes pudieran ayudarme con esto:

    While fila <= cantfilas Me.ProgressBar2.Value = fila + 1 Me.Label7.Text = (fila + 1).ToString + "/" + (cantfilas + 1).ToString My.Application.DoEvents() codigo = Me.DataGridView1.Item(0, fila).Value cantidad = CDec(Me.DataGridView1.Item(1, fila).Value) descrip = Me.DataGridView1.Item(2, fila).Value operacion = Me.DataGridView1.Item(3, fila).Value 'el costo hay que obtenerlo del inventario principal para tenerlo actualizado Dim consul As SISCOPYME.DatosDataSet.inventarioDataTable consul = Me.InventarioTableAdapter1.extraer(codigo) costo = consul.Item(0).COSTOPROM pvp = CDec(Me.DataGridView1.Item(5, fila).Value) cliente = Me.DataGridView1.Item(6, fila).Value fecha = CDate(Me.DataGridView1.Item(7, fila).Value) factura = Me.DataGridView1.Item(8, fila).Value Me.MovimientosTableAdapter1.Insert(codigo, cantidad, descrip, operacion, costo, pvp, cliente, fecha, factura) fila = fila + 1 End While

    A continuación les muestro el codigo almacenado en los table adapters:

    Me.InventarioTableAdapter1.extraer(codigo):

    SELECT     CODIGO, CANTIDAD, DESCRIPCION, MARCA, COSTO, COSTOPROM, EXENTO, PVP, PVM, CATEGORIA, SUBCATEGORIA, PROVEEDOR, MINIMO
    FROM         inventario
    WHERE     (CODIGO = @code)

    Me.MovimientosTableAdapter1.Insert(codigo, cantidad, descrip, operacion, costo, pvp, cliente, fecha, factura)

    INSERT INTO movimientos (CODIGO, CANTIDAD, DESCRIPCION, OPERACION, COSTO, PVP, [CLIENTE/PROVEEDOR], FECHA, FACTURA) VALUES (@codigo,@cantidad,@descripcion,@operacion,@costo,@pvp,@provcliente,@fecha,@factura)


    Luis Bermúdez Desarrollador de Aplicaciones Independiente Costa Rica

    Wednesday, October 31, 2012 4:37 AM

Answers

All replies

  • Hola.

    Es lo que tienen los cursores, y en general la realización de acciones de registro en registro en lugar de en conjuntos, que es lento.

    Prepara tu proceso como un insert..select y verás que el rendimiento mejora drásticamente. Luego dependerá también de lo que tarde esa sentencia, del volumen de registros, etc.

    Para preparar esa inserción en bloque, tienes que obtener una sentencia "select" que devuelva todos los registros que se han de insertar y con los valores ya calculados para cada uno de los campos (codigo, cantidad, descripcion, costo, fecha, etc. Será muy similar a tu actual "select", pero sin filtrar por el campo "codigo", o filtrándola, pero no de registro en registros. Y luego insertas. Realizas una operación, no una por cada registro.

    Inténtalo, si no lo logras, nos dices.


    Alberto López Grande
    SQL Server MVP
    Visita mi blog en http://qwalgrande.com
    Sígueme en twitter en http://twitter.com/qwalgrande

    Wednesday, October 31, 2012 6:30 AM
    Moderator
  • Hola Alberto. Muchas gracias por responder. Te confieso que soy novato en el manejo de SQL Server, y lo poco que se lo he venido aprendiendo solo y de forma desordenada. No entiendo bien a que te refieres con la inserción en bloque, y lo de mezclar select con insert en una sola operación; me sería de gran ayuda que me ilustraras un poco mas al respecto.

    Por otro lado, llama mi atención, que el ciclo while me demora aproximadamente un segundo por cada registro. Lo digo por que hasta hace poco tenia la misma aplicación trabajando con SQLCe, y hace tiempo la tuve con Access, y me hacia todo el proceso de unas 300-400 inserciones en unos 10 ó 15 segundos. En mi caso, la aplicación es para uso personal, no se trata de una gran empresa, ni se manejan millones de registros. Incluso el SQL server está instalado para servir a mi aplicación exclusivamente, no hay otras aplicaciones instaladas que usen el SQL Server. Desconozco como funciona exactamente de forma interna el SQL Server, pero me cuesta comprender por que siendo uno de los motores de bases de datos mas poderosos, me esté dando un rendimiento inferior al que me daba SQLCe ó Access. ¿Tu crees que sea normal que SQL Server se consuma un segundo para procesar un select y un insert? La tabla inventario donde se ejecuta el select tiene apenas 3 mil registros, la de movimientos si es una tabla un poco mas grande y tiene cerca de 385 mil registros, pero hasta donde he leido sobre las capacidades de SQL Server, esos son volumenes de datos pequeños que deberían poder administrarse con facilidad. ¿No pudiera haber algun parametro de configuración del SQL server que esté afectando su rendimiento? Por allí leí algo sobre los bloqueos, y los procesos en espera, no se si pudiera estar interfiriendo algo de eso.


    Luis Bermúdez Desarrollador de Aplicaciones Independiente Costa Rica

    Wednesday, October 31, 2012 9:22 PM
  • hola, amigo

    Veo que estas utilizando componentes de negocios, yo revisaria lo siguiente:

    Primero el tiempo que tarda esta consulta, puede que necesites un indices:

     'el costo hay que obtenerlo del inventario principal para tenerlo actualizado
                    Dim consul As SISCOPYME.DatosDataSet.inventarioDataTable
                    consul = Me.InventarioTableAdapter1.extraer(codigo)
                    costo = consul.Item(0).COSTOPROM

    El insert lo puedes realizar por un batch y no individual como menciona Alberto, inserta todo en un DataTable y luego utiliza tu dataadapter con el método update.

    Aunque también te aconsejo que pruebes Entity Framework, muchas cosas solucionarias :).

    Mucha suerte amigo


    Ahias Portillo

    Thursday, November 01, 2012 3:57 AM
  • Ya logré resolver. Todo el problema era poner el autoclose de la base de datos en false. Lo descubrí gracias a una respuesta de Alberto en el siguiente link: http://social.msdn.microsoft.com/Forums/es-ES/sqlserveres/thread/02845958-c9e9-4db7-88a3-022ccb7c3ccf

     El Proceso que me tardaba como 5 minutos, ahora lo hace como en 2 segundos, lo cual es bastante aceptable para lo que necesito, y supera mis espectativas. Muchas gracias a Alberto y a Ahias por sus buenas intenciones en ayudarme...


    Luis Bermúdez Desarrollador de Aplicaciones Independiente Costa Rica

    • Marked as answer by Luisgange Thursday, November 01, 2012 4:33 AM
    Thursday, November 01, 2012 4:33 AM
  • Estimado, podria darme un ejemplo de un proceso insert..select

    gracias


    _______________________
    Edward J. Ocando. | Desarrollador | SoyDesarrollador.net
    Telefono.+58 (416) 164.34.88 | Edward.Ocando@SoyDesarrollador.net

    Saturday, November 03, 2012 1:37 AM
  • Hola,

    El concepto es sencillo, generalmente cuando realizamos un insert individual tipicamente realizamos lo siguiente:

    1. Definimos la tabla a la que realizaremos el insert.
    2. Definimos los campos que vamos insertar, esto te aconsejo que lo hagas siempre, por que las tablas pueden cambiar con el tiempo, es decir adicionar nuevos campos, y si no definimos los campos el motor interpreta que vamos a insertar valores en todos lo campos.
    3. Los valores a insertar.

    En un insert con select el concepto es similar, pero en vez de insertar un unico valor insertaremos una lista de valores que son el resultado del query, solo debes tomar en cuenta que el query y los campos devueltos sea compactibles con la definicion de tu tabla y que la lista que retorna tu query sea equivalente a la definida en tu insert, tambien toma en cuenta que los valores indentity no deben definirse ya que son generado al momento de insert, tambien debes evaluar la cantidad de registro que insertas, por que el commint es por todos los registros que devuelve tu select y si hablamos de millones de registros debes evaluar si vale la pena hacer iteraciones de insert pequenos.

    Mira el ejemplo para guiarte.

    INSERT INTO MiTablaInsert (Campo01, Campo02,CampoX)
    SELECT
    	Campo01=CampoTabla01,
    	Campo02=CampoTabla02,
    	CampoX=CampoTabla03
    FROM MiTablaConsulta;

    Saludos cordiales


    Ahias Portillo

    Saturday, November 03, 2012 1:47 AM
  • Yo una v ex tuve un problema similar, lo resolví mediante procedimientos almacenados con SP_EXECUTESQL esecuta una cadena NVARCHAR como consulta y solo envías los parámetros, la teoría marca que con SP... SQL solo compila una sola vez la la cadena de ejecución y después solo va cambiando los parámetros, lo que difiere en un INSERT convencional.

    Saludos

    Friday, April 25, 2014 12:02 AM