none
Cursores SQL Server RRS feed

  • Pregunta

  • Saludos:
    Según lo comentado por todos los foros, el uso de los cursores en SQL Server no es muy recomendable, es mas, no se debe usar para ningún caso, comprendiendo eso si no es bueno usar cursores, que debo usar para el siguiente ejemplo:
    - Tengo aproximadamente 4 millones de registros recuperados desde un archivo de texto el que se procesa normalmente por periodos mensuales.
    - De lo recuperado se necesita seleccionar por cada registro para actualizar o insertar en otra tabla, bajo un orden y secuencia.

    El siguiente es una trama del archivo, el cual contiene más campos de lo mostrado, el que se recupera a una tabla1 temporal para luego según la secuencia registrada en un campo (que es un correlativo) y/o el orden del archivo de texto se ejecuta la actualización de campos (UPD) o la Inserción de un registro (INS) o la eliminación lógica del registro (DEL) de otra tabla2, el que debe seguir la secuencia con el fin de ejecutar correctamente la actualización.

    97U2107|CENTRAL SA             |13270783|0011|26|51225|240101|...|INS|...
    93A0819|SOC CAROLINA S A       |10014417|0011|26|13200|150104|...|UPD|...
    92M7351|RIFESA S A             |10015758|0011|26|51906|150141|...|INS|...
    98B2887|ASESORIA DE EMPRESAS SA|10017700|0011|26|93098|150131|...|INS|...
    9131337|PROAVICO               |10018234|0011|26|36996|150137|...|INS|...
    9006176|CARMINTEX              |10036429|0011|26|17117|150113|...|DEL|...
    96M7325|DISTRIBUIDORA SUPERFINA|10150701|0011|26|51313|150101|...|INS|...
    95V9876|CENTRO CORPORATIVO     |11054889|0011|26|74145|150131|...|UPD|...
    98P1403|CENTRO MAIZOTI S A     |12408323|0011|26|15316|070101|...|UPD|...
    

    La trama contiene mas de 4 millones de registros los que se ejecutan bajo periodos mensuales, la consulta es como se debe realizar la actualización sin tener que usar Cursores.

    Saludos

    sábado, 15 de septiembre de 2018 6:00

Todas las respuestas

  • [...] el uso de los cursores en SQL Server [...] no se debe usar para ningún caso,

    Tengo una objeción a eso: He visto sitios donde el DBA dice que "los cursores no se deben hacer en ningún caso", y entonces lo que los programadores hacen es realizar en lado cliente las mismas operaciones que se hubieran hecho en lado servidor con un cursor, y con eso el DBA ya se queda tan contento porque no están usando cursores en "su" servidor. Sin embargo, el cursor en lado cliente es TODAVÍA PEOR que usar el cursor en el lado servidor. No solo tiene que poner los mismos bloqueos y ocasiona la misma carga sobre el servidor, sino que encima lo hace durante más tiempo debido al trasvase de datos entre cliente y servidor. Así que, si la solución al problema va a ser hacer las operaciones del cursor en lado cliente, entonces ES PREFERIBLE USAR EL CURSOR.

    Dicho esto, no creo que la solución a tu problema requiera un cursor. No he entendido muy bien la pregunta, pero si se trata de hacer inserciones en orden, se podría hacer un "select" sobre la tabla original que añada un Row_Number y en la clausula OVER poner el criterio de ordenación que hay que seguir, y el resultado usarlo en la inserción para generar el valor que debe ser consecutivo.

    sábado, 15 de septiembre de 2018 8:17
  • Hola Jose Santuyo:


    Tengo aproximadamente 4 millones de registros recuperados desde un archivo de texto...


    Para el ejemplo, he cogido tus datos y he utilizado dos variantes, pero en este punto, es posible, que de la que recuperas, puedas estar directamente insertando en una tabla temporal que tenga un campo identity (mas fácil) o una secuence y ya los tienes ordenados secuencialmente, por la lectura.


    De lo recuperado se necesita seleccionar por cada registro para actualizar o insertar en otra tabla, bajo un orden y secuencia


    Si el orden o secuencia se rige, por cualquier otro criterio, entonces, puedes utilizar por ejemplo el escenario 1 de los dos que te planteo, (Tal y como te ha comentado Alberto).

    Para el orígen de los datos, yo he creado una variable de tabla donde directamente he insertado lo que tu has escrito.

    use foros 
    go
    create table Final (cod varchar(10), datos int)
    go
    DECLARE @entrada TABLE
    (cod    VARCHAR(10),
     nombre VARCHAR(100),
     col1   INT,
     col2   INT,
     col3   INT,
     col4   INT,
     col5   INT,
     col6   VARCHAR(10),
     col7   VARCHAR(5),
     col8   VARCHAR(5)
    );
    insert into @entrada
    (cod, nombre , col1 , col2 , col3 , col4 , col5 , col6 , col7, col8 )
    values
    ('97U2107','CENTRAL SA             ',13270783,0011,26,51225,240101,'...','INS','...'),
    ('93A0819','SOC CAROLINA S A       ',10014417,0011,26,13200,150104,'...','UPD','...'),
    ('92M7351','RIFESA S A             ',10015758,0011,26,51906,150141,'...','INS','...'),
    ('98B2887','ASESORIA DE EMPRESAS SA',10017700,0011,26,93098,150131,'...','INS','...'),
    ('9131337','PROAVICO               ',10018234,0011,26,36996,150137,'...','INS','...'),
    ('9006176','CARMINTEX              ',10036429,0011,26,17117,150113,'...','DEL','...'),
    ('96M7325','DISTRIBUIDORA SUPERFINA',10150701,0011,26,51313,150101,'...','INS','...'),
    ('95V9876','CENTRO CORPORATIVO     ',11054889,0011,26,74145,150131,'...','UPD','...'),
    ('98P1403','CENTRO MAIZOTI S A     ',12408323,0011,26,15316,070101,'...','UPD','...')
    /*Correlativo con un cte */
    ;with cte as 
    (	
    	select ROW_NUMBER() over(order by cod) as fila, cod, nombre, col1, col2, col3, col4, col5, col6, col7, col8
    	 from @entrada
    )
    --select * from cte;
    /* correlativo utilizando un campo identity en la misma tabla temporal donde se deben de leer los registros*/
      MERGE Final AS Target
         USING
    (
        SELECT c.cod, c.col3
        FROM CTE AS c
    ) AS source(cod, col3)
         ON(Target.cod = source.cod)
    		when MATCHED AND target.datos < (source.col3*2) 
    		/* SI EL ORIGEN Y DESTINO COINCIDEN PERO EN UNA CAUSISTICA X QUIERO BORRAR...*/
    		then
    			delete
             WHEN MATCHED
    		 /* SI COINCIDEN Y QUIERO POR TANTO UPDATEAR LA COLUMNA DATOS */
             THEN UPDATE SET
                             datos = source.col3
              
             WHEN NOT MATCHED
             THEN
    		 /* NO EXISTE EN DESTINO POR TANTO INSERTO */
               INSERT(cod,
                      datos)
               VALUES
    (source.cod,
     source.col3
    );
    
    DECLARE @entradaCorrelativo TABLE
    (fila   INT IDENTITY(1, 1),
     cod    VARCHAR(10),
     nombre VARCHAR(100),
     col1   INT,
     col2   INT,
     col3   INT,
     col4   INT,
     col5   INT,
     col6   VARCHAR(10),
     col7   VARCHAR(5),
     col8   VARCHAR(5)
    );
    INSERT INTO @entradaCorrelativo
           SELECT *
           FROM @entrada;
    
    /*MISMO MERGE PERO CON LA TABLA CON IDENTIDAD ENTRADACORRELATIVA */
    
     MERGE Final AS Target
         USING
    (
        SELECT c.cod, c.col3
        FROM @entradaCorrelativo AS c
    ) AS source(cod, col3)
         ON(Target.cod = source.cod)
    		when MATCHED AND target.datos < (source.col3*2) 
    		/* SI EL ORIGEN Y DESTINO COINCIDEN PERO EN UNA CAUSISTICA X QUIERO BORRAR...*/
    		then
    			delete
             WHEN MATCHED
    		 /* SI COINCIDEN Y QUIERO POR TANTO UPDATEAR LA COLUMNA DATOS */
             THEN UPDATE SET
                             datos = source.col3
              
             WHEN NOT MATCHED
             THEN
    		 /* NO EXISTE EN DESTINO POR TANTO INSERTO */
               INSERT(cod,
                      datos)
               VALUES
    (source.cod,
     source.col3
    );
    
    

    Tal cual lo has escrito, yo veo un MERGE, desde un CTE con Row_Number

    Espero te ayude a mejorar la eficiencia de ese proceso.

    Un saludo



    domingo, 16 de septiembre de 2018 5:17
  • Gracias Amigos,

    La idea de consultar la presente es, no usar un row_number puesto que puede funcionar sin problemas si tengo un select limitado, éste ejemplo si lo conceptué inicialmente, sin embargo consume mucha memoria y procesamiento, bueno al igual que los cursores. 

    La idea de Javi esta genial pero no sirve para la millonada de registros que se tiene.

    Bueno, no me queda que usar SSIS.

    Saludos.

    miércoles, 19 de septiembre de 2018 6:12
  • Bueno, no me queda que usar SSIS.

    ¡AJA! A eso me refería yo precisamente en un mensaje anterior cuando comentaba acerca de lo de "no se debe nunca usar un cursor". El desarrollador elige una solución tal como por ejemplo "utilicemos SSIS" y se queda tan tranquilo porque con eso ya hemos evitado el uso del cursor. ERROR. Lo que va a hacer SSIS internamente es usar un cursor de lado cliente. Esto es todavía peor que usar un cursor en el lado servidor. Sí, probablemente SSIS hará la operación correctamente. Pero esta solución no es mejor que usar un cursor en SQL, desde el punto de vista de la carga de trabajo total del servidor y número de bloqueos en la tabla.
    miércoles, 19 de septiembre de 2018 11:27