none
Disminuir tiempo de carga en una sentencia UPDATE RRS feed

  • Pregunta

  • Hola a todos:

    Tengo un problema al querer cargar y mostrar una serie de 392 registros mediante una sentencia Update.

    Querría que por favor me indicáseis si la misma se podría realizar de otra manera para que el tiempo de carga (12 segundos) sea más rápido.

    Os muestro una parte de la misma ya que todas son iguales y solamente cambian unos códigos. La base de datos es Access 2007 y os pregunto también si este mismo método con SQLCompact (que aún no lo utilizo), podría ser mucho más rápido.

    Toda la instrucción se actualiza directamente en una tabla y ocupa cada registro 12 campos.

    Try Using Cnn As New OleDbConnection(strConexion) Dim cmd As OleDbCommand = Cnn.CreateCommand() cmd.CommandType = CommandType.StoredProcedure Cnn.Open() 'Inicializamos la variable a 0 ReDim ValoresBalance(m_ValoresBalance.Length - 1) cmd.CommandText = "procFormulasBalance2digMP" With cmd.Parameters .Clear() .AddWithValue("@empresa", m_strCod_Empresa) .AddWithValue("@planconta", "PLAN 2007") .AddWithValue("@codigo1", "P2") .AddWithValue("@codigo2", "P3") End With Using rst As OleDbDataReader = cmd.ExecuteReader() While rst.Read For i As Integer = 0 To valor ValoresBalance(i) = ValoresBalance(i) + rst.GetDouble(i) / m_dblValorDivisor Next i End While End Using 'Actualización valores balance de situación If valor = 4 Then cmd.CommandText = "procActualizBalSituac_5ejerc" Else cmd.CommandText = "procActualizBalSituac_12ejerc" End If With cmd.Parameters .Clear() For i As Integer = 0 To valor .AddWithValue(String.Format("@ejer{0}", i), ValoresBalance(i)) Next .AddWithValue("@orden", "405") .AddWithValue("@empresa", m_strCod_Empresa) .AddWithValue("@TipoPlan", strTipoPlan) End With cmd.ExecuteNonQuery() 'Inicializamos la variable a 0 ReDim ValoresBalance(ValoresBalance.Length - 1) cmd.CommandText = "procFormulasBalance1dig" With cmd.Parameters .Clear() .AddWithValue("@empresa", m_strCod_Empresa) .AddWithValue("@planconta", "PLAN 2007") .AddWithValue("@codigo1", "2105") End With Using rst As OleDbDataReader = cmd.ExecuteReader() While rst.Read For i As Integer = 0 To valor ValoresBalance(i) = ValoresBalance(i) + rst.GetDouble(i) / m_dblValorDivisor Next i End While End Using 'Actualización valores balance de situación If valor = 4 Then cmd.CommandText = "procActualizBalSituac_5ejerc" Else cmd.CommandText = "procActualizBalSituac_12ejerc" End If With cmd.Parameters .Clear() For i As Integer = 0 To valor .AddWithValue(String.Format("@ejer{0}", i), ValoresBalance(i)) Next .AddWithValue("@orden", "405") .AddWithValue("@empresa", m_strCod_Empresa) .AddWithValue("@TipoPlan", strTipoPlan) End With cmd.ExecuteNonQuery()

    ...//... End Using Catch ex As Exception 'Se ha producido un error MessageBox.Show(ex.Message) End Try

    Os agradezco mucho vuestra atención.

    Un saludo a todos.

    Gemma

    viernes, 30 de noviembre de 2012 17:26

Respuestas

  • "gemma_campillo" preguntó:

    > Querría que por favor me indicáseis si la misma se podría
    > realizar de otra manera para que el tiempo de carga
    > (12 segundos) sea más rápido.

    ¿Y qué tiempo consideramos como "más rápido"? ¿Quizás 9 segundos? ¿1 segundo? ;-)

    > Tengo un problema al querer cargar y mostrar una serie de
    > 392 registros mediante una sentencia Update.
    >
    > Os muestro una parte de la misma ya que todas son iguales
    > y solamente cambian unos códigos

    Gemma, nos muestras "una parte de la misma", lo que significa que el procedimiento es mucho más largo, por tanto, el procedimiento tardará en ejecutarse lo que tenga que tardar; en tu equipo será 12 segundos y en otros tardará mucho más o mucho menos, dependiendo del hardware que tenga instalado el equipo donde se ejecute el código.

    > Using rst As OleDbDataReader = cmd.ExecuteReader()
    >  While rst.Read
    >   For i As Integer = 0 To valor
    >     ValoresBalance(i) = ValoresBalance(i) + rst.GetDouble(i) / m_dblValorDivisor
    >   Next i
    >  End While
    > End Using

    Ten en cuenta que a parte del proceso de ejecutar los procedimientos almacenados propiamente dichos, también recorres bucles para obtener los valores de los diversos objetos DataReader abiertos, y debes de saber que por cada registro que lees se produce un acceso a la base de datos (al disco duro de tu PC). Si son 392 registros y recorres dos bucles, estamos hablando de 784 veces que hay que hacer una consulta a la base de datos, y como dices que nos muestras "una parte de la misma", lo mismo recorres más bucles en ella, con lo cual se multiplicarán las veces que hay que acceder a la base de datos. Si el procesador es rápido, lo mismo tardará menos tiempo en ejecutarse, no así si el procesador no es tan rápido. ;-)

    En lugar de trabajar en "modo conectado" (abriendo objetos DataReader), podrías hacerlo en "modo no conectado" (recuperando un objeto DataTable), con lo cual los datos se cargaran en memoria y se eliminarán de la misma cuando ya no los necesites. Pero ¡claro! Ahora hablamos de "memoria", y si el PC no tiene instalada una suficiente cantidad de RAM, el sistema operativo echará mano del archivo de paginación, con lo cual estamos accediendo al disco duro, que normalmente es mucho más lento que si accedemos directamente a la RAM, con lo que puede suceder que te encuentres en la misma situación: tardando 12 segundos en completar el código de tu procedimiento. ;-)

    Si tu preocupación es que se "congela" tu aplicación mientras se ejecuta el procedimiento, habría que sopesar ejecutar dicho procedimiento en otro subproceso diferente al subproceso principal de tu aplicación, de ésta manera, el usuario podría estar haciendo otros trabajos mientras se ejecuta en segundo plano el código de tu procedimiento.

    Pero esto último puede que acarre otro problema con el tipo de aplicación que estás desarrollando, que si no estoy equivocado, creo que es de contabilidad. Si el proceso de actualización lo ejecutas en otro subproceso, puede que el usuario mientras tanto esté consultando o actualizando datos que posteriormente no sean correctos. ¿Me explico?

    Yo creo que no te deberías de preocupar demasiado por reducir los 12 segundos, que tampoco es una barbaridad, máxime si es un proceso que necesariamente hay que hacerlo, y lo mismo, a lo mejor hay que hacerlo solamente una vez al año.

    Para tu tranquilidad, te comento que hay aplicaciones que tardan un día entero, o más, en un proceso de mantenimiento o actualización, y durante ese tiempo, los usuarios se tienen que dedicar a otros menesteres, porque no pueden ni tan siquiera consultar datos. ;-)

    > os pregunto también si este mismo método con SQLCompact
    > (que aún no lo utilizo), podría ser mucho más rápido.

    Te va a tardar prácticamente igual, porque en todos los casos, estamos hablando del acceso a un archivo que se encuentra grabado en el disco duro del PC.

    Lo que sí te digo es que, si tienes pensado migrar tu aplicación a SQL Server, SQL Compact o a cualquier otro motor de datos, vas a tener que reescribir de nuevo todo el código de acceso a datos existente en tu aplicación, porque al estar trabajando actualmente con una base de datos de Access, el acceso a los datos lo realizas a través del proveedor de datos OleDb de .NET, y éste proveedor no es el más recomendable para SQL Server o para SQL Compact. Asi que ¡más trabajo! ;-)

    Si desde un primer momento hubieras tenido en mente migrar tu aplicación a otro motor de datos, en lugar de utilizar objetos del tipo OleDbConnection, OleDbCommand, OleDbDataAdapter u OleDbDataReader, tenías que haber utilizado objetos que no dependan de un proveedor de datos .net concreto, como son los objetos DbConnection, DbCommand, DbDataAdapter y DbDataReader. Posteriormente, con sólo cambiar la cadena de conexión en el archivo de configuración de tu aplicación, puedes trabajar con una base de datos de Access, SQL Server, SQL Compact, o de cualquier otro motor de datos. En definita, que tenías que haber escrito código independiente de cualquier tipo de proveedor de datos .NET.

    Introducción al modelo independiente del proveedor

    Trabajo con generadores

    Un saludo y ¡Feliz Año Nuevo!


    Enrique Martínez
      [MS MVP - VB]

    Nota informativa: La información contenida en este mensaje, así como el código fuente incluido en el mismo, se proporciona «COMO ESTÁ», sin garantías de ninguna clase, y no otorga derecho alguno. Usted asume cualquier riesgo al poner en práctica, utilizar o ejecutar lo recomendado o sugerido en el presente mensaje.

    Si esta respuesta le ha resultado útil, recuerde marcarla como satisfactoria.

    Si usas Visual Basic .NET y deseas ser productivo y feliz, activa la instrucción Option Strict.

    • Marcado como respuesta gemma_campillo miércoles, 9 de enero de 2013 7:43
    sábado, 5 de enero de 2013 10:36
    Moderador

Todas las respuestas

  • Hola ! , el hecho de que este en un gestor o en otro no implica que sea mas eficiente; pienso que 392 registros es una cantidad considerable y al querer hacer un UPDATE , es necesario que se tome su tiempo, ademas también intervienen factores como la velocidad de procesamiento de datos de tu PC, RAM, entre otros. podrias utilizar Linq to SQL; ya que este mapea las tablas de una base de datos SQL, es más eficaz y eficiente al realizar los CRUD en una base de datos. :)

    Suerte ! 

    viernes, 4 de enero de 2013 16:17
  • "gemma_campillo" preguntó:

    > Querría que por favor me indicáseis si la misma se podría
    > realizar de otra manera para que el tiempo de carga
    > (12 segundos) sea más rápido.

    ¿Y qué tiempo consideramos como "más rápido"? ¿Quizás 9 segundos? ¿1 segundo? ;-)

    > Tengo un problema al querer cargar y mostrar una serie de
    > 392 registros mediante una sentencia Update.
    >
    > Os muestro una parte de la misma ya que todas son iguales
    > y solamente cambian unos códigos

    Gemma, nos muestras "una parte de la misma", lo que significa que el procedimiento es mucho más largo, por tanto, el procedimiento tardará en ejecutarse lo que tenga que tardar; en tu equipo será 12 segundos y en otros tardará mucho más o mucho menos, dependiendo del hardware que tenga instalado el equipo donde se ejecute el código.

    > Using rst As OleDbDataReader = cmd.ExecuteReader()
    >  While rst.Read
    >   For i As Integer = 0 To valor
    >     ValoresBalance(i) = ValoresBalance(i) + rst.GetDouble(i) / m_dblValorDivisor
    >   Next i
    >  End While
    > End Using

    Ten en cuenta que a parte del proceso de ejecutar los procedimientos almacenados propiamente dichos, también recorres bucles para obtener los valores de los diversos objetos DataReader abiertos, y debes de saber que por cada registro que lees se produce un acceso a la base de datos (al disco duro de tu PC). Si son 392 registros y recorres dos bucles, estamos hablando de 784 veces que hay que hacer una consulta a la base de datos, y como dices que nos muestras "una parte de la misma", lo mismo recorres más bucles en ella, con lo cual se multiplicarán las veces que hay que acceder a la base de datos. Si el procesador es rápido, lo mismo tardará menos tiempo en ejecutarse, no así si el procesador no es tan rápido. ;-)

    En lugar de trabajar en "modo conectado" (abriendo objetos DataReader), podrías hacerlo en "modo no conectado" (recuperando un objeto DataTable), con lo cual los datos se cargaran en memoria y se eliminarán de la misma cuando ya no los necesites. Pero ¡claro! Ahora hablamos de "memoria", y si el PC no tiene instalada una suficiente cantidad de RAM, el sistema operativo echará mano del archivo de paginación, con lo cual estamos accediendo al disco duro, que normalmente es mucho más lento que si accedemos directamente a la RAM, con lo que puede suceder que te encuentres en la misma situación: tardando 12 segundos en completar el código de tu procedimiento. ;-)

    Si tu preocupación es que se "congela" tu aplicación mientras se ejecuta el procedimiento, habría que sopesar ejecutar dicho procedimiento en otro subproceso diferente al subproceso principal de tu aplicación, de ésta manera, el usuario podría estar haciendo otros trabajos mientras se ejecuta en segundo plano el código de tu procedimiento.

    Pero esto último puede que acarre otro problema con el tipo de aplicación que estás desarrollando, que si no estoy equivocado, creo que es de contabilidad. Si el proceso de actualización lo ejecutas en otro subproceso, puede que el usuario mientras tanto esté consultando o actualizando datos que posteriormente no sean correctos. ¿Me explico?

    Yo creo que no te deberías de preocupar demasiado por reducir los 12 segundos, que tampoco es una barbaridad, máxime si es un proceso que necesariamente hay que hacerlo, y lo mismo, a lo mejor hay que hacerlo solamente una vez al año.

    Para tu tranquilidad, te comento que hay aplicaciones que tardan un día entero, o más, en un proceso de mantenimiento o actualización, y durante ese tiempo, los usuarios se tienen que dedicar a otros menesteres, porque no pueden ni tan siquiera consultar datos. ;-)

    > os pregunto también si este mismo método con SQLCompact
    > (que aún no lo utilizo), podría ser mucho más rápido.

    Te va a tardar prácticamente igual, porque en todos los casos, estamos hablando del acceso a un archivo que se encuentra grabado en el disco duro del PC.

    Lo que sí te digo es que, si tienes pensado migrar tu aplicación a SQL Server, SQL Compact o a cualquier otro motor de datos, vas a tener que reescribir de nuevo todo el código de acceso a datos existente en tu aplicación, porque al estar trabajando actualmente con una base de datos de Access, el acceso a los datos lo realizas a través del proveedor de datos OleDb de .NET, y éste proveedor no es el más recomendable para SQL Server o para SQL Compact. Asi que ¡más trabajo! ;-)

    Si desde un primer momento hubieras tenido en mente migrar tu aplicación a otro motor de datos, en lugar de utilizar objetos del tipo OleDbConnection, OleDbCommand, OleDbDataAdapter u OleDbDataReader, tenías que haber utilizado objetos que no dependan de un proveedor de datos .net concreto, como son los objetos DbConnection, DbCommand, DbDataAdapter y DbDataReader. Posteriormente, con sólo cambiar la cadena de conexión en el archivo de configuración de tu aplicación, puedes trabajar con una base de datos de Access, SQL Server, SQL Compact, o de cualquier otro motor de datos. En definita, que tenías que haber escrito código independiente de cualquier tipo de proveedor de datos .NET.

    Introducción al modelo independiente del proveedor

    Trabajo con generadores

    Un saludo y ¡Feliz Año Nuevo!


    Enrique Martínez
      [MS MVP - VB]

    Nota informativa: La información contenida en este mensaje, así como el código fuente incluido en el mismo, se proporciona «COMO ESTÁ», sin garantías de ninguna clase, y no otorga derecho alguno. Usted asume cualquier riesgo al poner en práctica, utilizar o ejecutar lo recomendado o sugerido en el presente mensaje.

    Si esta respuesta le ha resultado útil, recuerde marcarla como satisfactoria.

    Si usas Visual Basic .NET y deseas ser productivo y feliz, activa la instrucción Option Strict.

    • Marcado como respuesta gemma_campillo miércoles, 9 de enero de 2013 7:43
    sábado, 5 de enero de 2013 10:36
    Moderador
  • Hola Enrique:

    Gracias por la respuesta.

    Desde que hice la pregunta en el foro en el mes de Noviembre, he intentado disminuir dichos tiempos y lo he logrado. Ante todo el programa es de análisis financiero no es de contabilidad, por lo que utiliza un gran número de fórmulas basadas en selects. Bueno, dicho esto, comprobé que tardaba mucho cuando tenía que actualizar datos en las tablas mediante update, tardaba tanto porque si tenía que hacer 50 updates por ejemplo, los tenía cada uno en un porocedimiento con su respectiva apertura y cierre de la base de datos cada vez, lo que hice y me resultó de gran utilidad es poner esos 50 updates en un solo procedimiento, con lo que la apertura de la base de datos y cierre se realiza solo una vez ya que siempre tiene que hacer lo mismo. Esa fué la solución y efectivamente la aplicación corre sin demoras importantes, hablamos de 3, 4 o 5 segundos en abrir un form cargado de fórmulas cuando antes tarda de 12 a 20 segundos, por lo tanto estamos en lo normal. No utilizo los dataset en estos casos y grabo directamente el resultado del update en la tabla correspondiente a travñes del resultado que obtengo de las selects, en otros casos son el valor de variables lo que grabo en la tabla.

    Lo que si me causa dudas es porqué en VB6 con ADO no tarda nada y en cambio en vbnet 2010 y con access2007 demora mucho más que en vb6, entiendo que debe ser por la forma en que trabaja ado.net, pero bueno, ya está funcionando correctamente y sin problemas. No falla en nada.

    Bueno maestro, feliz año ya que no te lo pude desear anteriormente y muchas gracias por tus consejos.

    Un fuerte abrazo.

    Gemma.

    miércoles, 9 de enero de 2013 7:43