none
Transacciones RRS feed

  • Pregunta

  • Hola.

    Tengo un problema con una transacción, tengo un DataGridView el cual recorro con un FOR para guardar información a la base de datos. Dentro del FOR mando a llamar a este procedimiento:

    [dbo].[guardarOrden] (
    @pOrden VARCHAR(50),@pIdTalla INT, @pCantidad INT,
    @pNumCorte INT, @pIdColor INT, @pIdTemporada INT,
    @pNumOrden VARCHAR(50), @IdCliente INT, @pIdTela INT,
    @pIdEstilo INT, @pIdEjemplar INT, @FechaI DATE, 
    @FechaF DATE, @Tamano INT, @val VARCHAR(100) OUTPUT)
    AS
    DECLARE @intFlag AS INT = 0;
    BEGIN
    	BEGIN
    	
        BEGIN TRY
    	
    		DECLARE @IdOp AS INT;
    
    		DECLARE @IdProduccion INT;
    
            BEGIN TRAN TRAN1
            
            IF NOT EXISTS(SELECT no_orden FROM orden_produccion WHERE no_orden = @pNumOrden)
    		BEGIN
    			INSERT INTO orden_produccion (no_orden) VALUES (@pNumOrden);				
    			SELECT @IdOp = id_produccion_pk FROM orden_produccion WHERE no_orden = @pNumOrden;
    		END
    		
    		SELECT @IdOp = id_produccion_pk FROM orden_produccion WHERE no_orden = @pNumOrden;
    
    		INSERT INTO dbo.pedidosxtalla(id_talla_fk, id_op_fk, cantidad) 
    		VALUES (@pIdTalla, @IdOp, @pCantidad);
    	
    		SET @intFlag = @intFlag + 1;
    				
    		IF @intFlag = @Tamano
    			BEGIN
    			
    				SELECT @IdProduccion = id_produccion_pk FROM orden_produccion
    				WHERE no_orden = @pNumOrden;
    				
    				INSERT INTO orden
    				(no_corte, id_color_fk, id_temporada_fk, 
    				 id_produccion_fk, id_cliente_fk, id_cod_telas_fk, 
    				 id_estilo_fk, id_ejemplar_fk, fecha_inicial, fecha_final)
    				VALUES
    				(@pNumCorte, @pIdColor, @pIdTemporada, 
    				 @IdProduccion, @IdCliente, @pIdTela, 
    				 @pIdEstilo, @pIdEjemplar, @FechaI, @FechaF);
    			 
    				SET @val = 1;
    				
    				COMMIT TRAN
    			END
    		ELSE
    			BEGIN
    				SET @val = @intFlag;
    				ROLLBACK TRAN			
    			END
        END TRY
        BEGIN CATCH
    		SET @val = @intFlag;
    		--ERROR_LINE() + ' ' + ERROR_MESSAGE();
    		ROLLBACK TRAN
        END CATCH
    END
    END

    Al parecer funciona correctamente, pero no me guarda todos los datos. Es decir, si en el DataGridView hay dos filas, el procedimiento me almacena la primera fila, más no la última.

    He pasado horas tratando de solucionarlo, pero no doy con solución alguna.

    ¿Alguien sabe por qué no me guarda los datos como debe de ser?


    Antonio Mata

    jueves, 30 de junio de 2016 21:50

Respuestas

  • A. Mata,

    El procedimiento lo podrías mejorar de la siguiente manera:

    CREATE PROCEDURE [dbo].[guardarOrden] (
    	@pOrden VARCHAR(50),
    	@pIdTalla INT, 
    	@pCantidad INT,
    	@pNumCorte INT, 
    	@pIdColor INT, 
    	@pIdTemporada INT,
    	@pNumOrden VARCHAR(50), 
    	@IdCliente INT, 
    	@pIdTela INT,
    	@pIdEstilo INT, 
    	@pIdEjemplar INT, 
    	@FechaI DATE, 
    	@FechaF DATE, 
    	@val INT OUTPUT)
    AS
    BEGIN	
    	BEGIN TRANSACTION TRAN1
        BEGIN TRY
    	
    		DECLARE @IdOp AS int;
    		DECLARE @IdProduccion int;
    
    		INSERT INTO orden_produccion (no_orden) SELECT @pNumOrden WHERE NOT EXISTS(SELECT no_orden FROM orden_produccion WHERE no_orden = @pNumOrden);        
            		
    		SELECT @IdOp = id_produccion_pk FROM orden_produccion WHERE no_orden = @pNumOrden;
    
    		INSERT INTO dbo.pedidosxtalla(id_talla_fk, id_op_fk, cantidad) VALUES (@pIdTalla, @IdOp, @pCantidad);
    		
    		SELECT @IdProduccion = id_produccion_pk FROM orden_produccion WHERE no_orden = @pNumOrden;
    			
    		INSERT INTO orden
    			(no_corte, id_color_fk, id_temporada_fk, id_produccion_fk, id_cliente_fk, id_cod_telas_fk, id_estilo_fk, id_ejemplar_fk, fecha_inicial, fecha_final)
    		SELECT
    			@pNumCorte, @pIdColor, @pIdTemporada, @IdProduccion, @IdCliente, @pIdTela, @pIdEstilo, @pIdEjemplar, @FechaI, @FechaF
    		WHERE
    			NOT EXISTS(SELECT no_corte FROM orden WHERE no_corte = @pNumCorte);
    		
    		SET @val = '1';	
    
    		COMMIT TRANSACTION
        END TRY
        BEGIN CATCH
    		SET @val = '0';
    
    		ROLLBACK TRANSACTION
        END CATCH
    END

    Nota que la variable @IdOp y @IdProduccion contienen el mismo valor ¿es correcto o estás equivocando de asignación?

    Por otro lado, las transacciones también puedes gestionarlas desde ADO .Net, es decir, podrías tener 2 o mas procedimientos dentro de una transacción, te dejo unos enlaces para que revises:

    Transacciones locales

    SqlConnection.BeginTransaction

    • Marcado como respuesta A. Mata lunes, 4 de julio de 2016 19:10
    viernes, 1 de julio de 2016 4:56

Todas las respuestas

  • Hola A. Mata

    muéstranos por favor cómo llamas a tu procedimiento desde el for para ver cómo ayudar..

     

    Javier

    jueves, 30 de junio de 2016 21:57
  • Hola Javier, gracias por tu atención.

    Al procedimiento lo mando a llamar de esta forma:

     int tamano = gridUnits.RowCount;
    
                    for (int i = 0; i <= gridUnits.Rows.Count - 1; i++)
                    {
                        if (gridUnits.Rows[i].Cells["colDz"].Value != null
                        || gridUnits.Rows[i].Cells["colUnits"].Value != null)
                        {
                            IdTalla = Convert.ToInt32(gridUnits.Rows[i].Cells["colId"].Value.ToString());
    
                            int dz = 0, unit = 0;
    
                            dz =
                            Convert.ToInt32(gridUnits.Rows[i].Cells["colDz"].Value);
    
                            unit =
                            Convert.ToInt32(gridUnits.Rows[i].Cells["colUnits"].Value);
    
                            Total = (dz * 12) + unit;
    
                            gridUnits.Rows[i].Cells["colTotal"].Value = Total.ToString();
    
                            gridUnits.Rows[i].Cells["colTotal"].Value = Total.ToString();
    
                            r = Query.guardarOrden(Po, IdTalla, Total,
                                             NumRequisition, idColor, idSeason,
                                             Po, idCliente, idFabric, idEstilo,
                                             idSample, FechaIni, FechaFin, tamano);
                                //Query.insertarPedido_Talla(Po, IdTalla, Total);
    
                        }
                    }

    Y lo he codificado de esta forma:

    public int guardarOrden(String NumOrden, int IdTalla, int Cantidad,
                                    int NumCorte, int idColor, int idTemporada,
                                    String NumOrden2, int idCliente, int idTela,
                                    int idEstilo, int idEjemplar, DateTime FechaI, DateTime FechaF, int Tamano)
            {
                int val = 0;
    
                try
                {
                    using (Conexion.getConexion())
                    using (SqlCommand cmd = new SqlCommand("dbo.guardarOrden", Conexion.getConexion()))
                    {
                        cmd.CommandType = CommandType.StoredProcedure;
    
                        // set up the parameters
                        cmd.Parameters.Add("@pOrden", SqlDbType.VarChar, 50);
                        cmd.Parameters.Add("@pIdTalla", SqlDbType.Int);
                        cmd.Parameters.Add("@pCantidad", SqlDbType.Int);
                        cmd.Parameters.Add("@pNumCorte", SqlDbType.Int);
                        cmd.Parameters.Add("@pIdColor", SqlDbType.Int);
                        cmd.Parameters.Add("@pIdTemporada", SqlDbType.Int);
                        cmd.Parameters.Add("@pNumOrden", SqlDbType.VarChar, 50);
                        cmd.Parameters.Add("@IdCliente", SqlDbType.Int);
                        cmd.Parameters.Add("@pIdTela", SqlDbType.Int);
                        cmd.Parameters.Add("@pIdEstilo", SqlDbType.Int);
                        cmd.Parameters.Add("@pIdEjemplar", SqlDbType.Int);
                        cmd.Parameters.Add("@FechaI", SqlDbType.Date);
                        cmd.Parameters.Add("@FechaF", SqlDbType.Date);
                        cmd.Parameters.Add("@Tamano", SqlDbType.Int);
    
                        cmd.Parameters.Add("@val", SqlDbType.Int).Direction = ParameterDirection.Output;
    
                        // set parameter values
                        cmd.Parameters["@pOrden"].Value = NumOrden;
    
                        cmd.Parameters["@pIdTalla"].Value = IdTalla;
    
                        cmd.Parameters["@pCantidad"].Value = Cantidad;
    
                        cmd.Parameters["@pNumCorte"].Value = NumCorte;
    
                        cmd.Parameters["@pIdColor"].Value = idColor;
    
                        cmd.Parameters["@pIdTemporada"].Value = idTemporada;
    
                        cmd.Parameters["@pNumOrden"].Value = NumOrden;
    
                        cmd.Parameters["@IdCliente"].Value = idCliente;
    
                        cmd.Parameters["@pIdTela"].Value = idTela;
    
                        cmd.Parameters["@pIdEstilo"].Value = idEstilo;
    
                        cmd.Parameters["@pIdEjemplar"].Value = idEjemplar;
    
                        cmd.Parameters["@FechaI"].Value = FechaI;
    
                        cmd.Parameters["@FechaF"].Value = FechaF;
    
                        cmd.Parameters["@Tamano"].Value = Tamano;
    
                        cmd.ExecuteNonQuery();
    
                        // read output value from @NewId
                        val = Convert.ToInt32(cmd.Parameters["@val"].Value);
    
                        cmd.Parameters.Clear();
                    }
                }
                catch (Exception ex)
                {
                    throw new Exception(ex.ToString());
                }
    
                return val;
            }


    Antonio Mata

    jueves, 30 de junio de 2016 22:02
  • A. Mata,

    Como primer punto te sugiero refactorizar tu procedimiento, hay redundancia.

    Luego de revisar -el desorden- puedo imaginar que no insertas todas las filas a razón de dos situaciones:

    1. Tienes la validación (IF @intFlag = @Tamano) y por el seguimiento que hice la variable @intFlag siempre será 1, entonces la inserción se realiza sólo cuando (1 = 1) es decir, cuando @Tamano sea 1, consideralo.
    2. Si el procedimiento es correcto -a pesar del desorden y de "cosas" demás- sería bueno que postees el código que utilizas para persistir los datos, es posible que ahí estés cometiendo algún error.

    Esperamos tus comentarios.

    jueves, 30 de junio de 2016 22:06
  • Hola Williams, gracias por las observaciones.

    Si tienes una sugerencia/aporte estaré de igual forma muy agradecido.

    Estas son las tres tablas que necesito manejar por medio de transacciones, la tabla pedidosxtalla es donde almacenaré varias tallas, las cuales las tengo en un DataGridView, al final que insertó todas las tallas las guardo en una orden "Tabla orden", ya que en esta última no puede repetirse.

    El código que utilizo lo he puesto arriba.


    Antonio Mata

    jueves, 30 de junio de 2016 22:15
  • A. Mata,

    Acabo de darme cuenta recién que ya posteaste código.

    Bueno, creo que el primer punto que te coloque en mi comentario anterior es el causante de tus horas de sosobra.

    Fíjate lo que haces:

    int tamano = gridUnits.RowCount;

    La propiedad RowCount retorna el número de filas que tiene el objeto (gridUnits), ¿qué pasa el objeto gridUnits tiene mas de una fila?

    Fíjate lo que tienes en el procedimiento almacenado:

    IF @intFlag = @Tamano
           BEGIN
                   <...>

    La variable @intFlag siempre es 1 y la inserción se hará sólo cuando @Tamano sea 1, es decir, sólo cuando el objeto gridUnits tenga una sola fila.

    No estoy seguro porque realizas la validación (IF @intFlag = @Tamano) pero quitandola debería funcionar.


    jueves, 30 de junio de 2016 22:15
  • Hola Williams.

    >La propiedad RowCount retorna el número de filas que tiene el objeto (gridUnits), ¿qué pasa el objeto gridUnits tiene mas de una fila?

    Sí, de hecho pueden ser cuantas filas el usuario quiera, de este modo:

    Esos datos se almacenarán en la tabla pedidosxtalla.

    > No estoy seguro porque realizas la validación (IF @intFlag = @Tamano) pero quitandola debería funcionar.

    Trataré de explicarme, tengo un formulario donde aparte de las tallas obtengo la información necesaria que se almacenará en la tabla orden, pero en esta tabla no pueden repetirse los valores ya que tiene campos UNIQUE, y por cada iteración del FOR intentará guardar el mismo registro y daría error, por eso hago la validación que hasta que intFlag alcance a almacenar todas las tallas que eligió el usuario almacene toda la demás información en la tabla orden, para que no ocurra el problema antes mencionado.

    Más arriba dejé el diagrama de la base de datos, por si quieres echarle un vistazo.

    Cualquier sugerencia es bienvenida.



    Antonio Mata

    jueves, 30 de junio de 2016 22:33
  • A. Mata,

    Primero que no es la forma de operar con un conjunto de datos que quieres persitirlos como una unidad -transaccion-, deberías tener un parámetro XML o un parámetro de tipo tabla, pero por el momento creo sería complicarte mas las cosas.

    Si quieres hacerlo simple debes de tener dos procedimientos, uno que persista las tablas [orden] y [orden_produccion] y otra que persista las "n" filas en la tabla [Pedidosxtalla].

    jueves, 30 de junio de 2016 23:04
  • Hola de nuevo, Williams.

    En un primer momento pensé en crear dos procedimientos almacenados, lo probé, pero sucedía que insertaba las tallas y si había errores en la transacción los datos aún se guardaban, cuando no debería.

    Después de tanto, lo solucioné, pero no me termina de convencer aunque es funcional.

    En esto de programar en SQL soy muy novato, pero suele simplificarse código en algunas ocasiones cuando se trabaja con Procedimientos o Funciones.

    Lo hice de la siguiente forma:

    PROCEDURE [dbo].[guardarOrden] (
    @pOrden VARCHAR(50),@pIdTalla INT, @pCantidad INT,
    @pNumCorte INT, @pIdColor INT, @pIdTemporada INT,
    @pNumOrden VARCHAR(50), @IdCliente INT, @pIdTela INT,
    @pIdEstilo INT, @pIdEjemplar INT, @FechaI DATE, 
    @FechaF DATE, @val INT OUTPUT)
    AS
    BEGIN	
        BEGIN TRY
    	
    		DECLARE @IdOp AS INT;
    
    		DECLARE @IdProduccion INT;
    
            BEGIN TRAN TRAN1
            
            IF NOT EXISTS(SELECT no_orden FROM orden_produccion WHERE no_orden = @pNumOrden)
    		BEGIN
    			INSERT INTO orden_produccion (no_orden) VALUES (@pNumOrden);				
    		END
    		
    		SELECT @IdOp = id_produccion_pk FROM orden_produccion WHERE no_orden = @pNumOrden;
    
    		INSERT INTO dbo.pedidosxtalla(id_talla_fk, id_op_fk, cantidad) 
    		VALUES (@pIdTalla, @IdOp, @pCantidad);
    		
    		SELECT @IdProduccion = id_produccion_pk FROM orden_produccion
    		WHERE no_orden = @pNumOrden;
    			
    		IF NOT EXISTS(SELECT no_corte FROM orden WHERE no_corte = @pNumCorte)
    		BEGIN
    			INSERT INTO orden
    			(no_corte, id_color_fk, id_temporada_fk, 
    			 id_produccion_fk, id_cliente_fk, id_cod_telas_fk, 
    			 id_estilo_fk, id_ejemplar_fk, fecha_inicial, fecha_final)
    			VALUES
    			(@pNumCorte, @pIdColor, @pIdTemporada, 
    			 @IdProduccion, @IdCliente, @pIdTela, 
    			 @pIdEstilo, @pIdEjemplar, @FechaI, @FechaF);
    			 
    		END
    		
    		SET @val = '1';	
    		COMMIT TRAN
    
        END TRY
        BEGIN CATCH
    		SET @val = '0';
    		ROLLBACK TRAN
        END CATCH
    END



    Antonio Mata

    viernes, 1 de julio de 2016 2:01
  • A. Mata,

    El procedimiento lo podrías mejorar de la siguiente manera:

    CREATE PROCEDURE [dbo].[guardarOrden] (
    	@pOrden VARCHAR(50),
    	@pIdTalla INT, 
    	@pCantidad INT,
    	@pNumCorte INT, 
    	@pIdColor INT, 
    	@pIdTemporada INT,
    	@pNumOrden VARCHAR(50), 
    	@IdCliente INT, 
    	@pIdTela INT,
    	@pIdEstilo INT, 
    	@pIdEjemplar INT, 
    	@FechaI DATE, 
    	@FechaF DATE, 
    	@val INT OUTPUT)
    AS
    BEGIN	
    	BEGIN TRANSACTION TRAN1
        BEGIN TRY
    	
    		DECLARE @IdOp AS int;
    		DECLARE @IdProduccion int;
    
    		INSERT INTO orden_produccion (no_orden) SELECT @pNumOrden WHERE NOT EXISTS(SELECT no_orden FROM orden_produccion WHERE no_orden = @pNumOrden);        
            		
    		SELECT @IdOp = id_produccion_pk FROM orden_produccion WHERE no_orden = @pNumOrden;
    
    		INSERT INTO dbo.pedidosxtalla(id_talla_fk, id_op_fk, cantidad) VALUES (@pIdTalla, @IdOp, @pCantidad);
    		
    		SELECT @IdProduccion = id_produccion_pk FROM orden_produccion WHERE no_orden = @pNumOrden;
    			
    		INSERT INTO orden
    			(no_corte, id_color_fk, id_temporada_fk, id_produccion_fk, id_cliente_fk, id_cod_telas_fk, id_estilo_fk, id_ejemplar_fk, fecha_inicial, fecha_final)
    		SELECT
    			@pNumCorte, @pIdColor, @pIdTemporada, @IdProduccion, @IdCliente, @pIdTela, @pIdEstilo, @pIdEjemplar, @FechaI, @FechaF
    		WHERE
    			NOT EXISTS(SELECT no_corte FROM orden WHERE no_corte = @pNumCorte);
    		
    		SET @val = '1';	
    
    		COMMIT TRANSACTION
        END TRY
        BEGIN CATCH
    		SET @val = '0';
    
    		ROLLBACK TRANSACTION
        END CATCH
    END

    Nota que la variable @IdOp y @IdProduccion contienen el mismo valor ¿es correcto o estás equivocando de asignación?

    Por otro lado, las transacciones también puedes gestionarlas desde ADO .Net, es decir, podrías tener 2 o mas procedimientos dentro de una transacción, te dejo unos enlaces para que revises:

    Transacciones locales

    SqlConnection.BeginTransaction

    • Marcado como respuesta A. Mata lunes, 4 de julio de 2016 19:10
    viernes, 1 de julio de 2016 4:56