none
Error de restricción de datos RRS feed

  • Pregunta

  • Amigos buenas noches , les pregunto algo , hay alguna manera de insertar datos repetidos donde hay una llave primaria ,resulta que estoy tratando de insertar un código de factura y por ende este trae todos los ítem facturados pero solo me guarda el primer registro y con los siguientes salta error por qué se repite el número de factura , está tabla ya hace ese procedimiento desde otro aplicativo pero a mí no me daje , y creo q no es conveniente eliminar ese llave para que haya duplicidad de datos , que me pueden aconsejar , muchas gracias de antemano

    Daniel

    lunes, 3 de agosto de 2020 3:02

Respuestas

  • Hola DannyCV012:

    Mira tu escenario super simplificado:

    create table SAITEMOPI (numeroD nvarchar(10), nroLInea int)
    go
    DECLARE @NumeroD nvarchar(10) = N'123455';
    DECLARE  @MAX INT = (SELECT ISNULL(max(NroLinea),0)+1 from SAITEMOPI WHERE numeroD = @numeroD);
    INSERT INTO SAITEMOPI 
    VALUES (@NumeroD, @MAX);
    GO
    DECLARE @NumeroD nvarchar(10) = N'123456';
    DECLARE  @MAX INT = (SELECT ISNULL(max(NroLinea),0)+1 from SAITEMOPI WHERE numeroD = @numeroD);
    INSERT INTO SAITEMOPI 
    VALUES (@NumeroD, @MAX);
    GO
    DECLARE @NumeroD nvarchar(10) = N'123457';
    DECLARE  @MAX INT = (SELECT ISNULL(max(NroLinea),0)+1 from SAITEMOPI WHERE numeroD = @numeroD);
    INSERT INTO SAITEMOPI 
    VALUES (@NumeroD, @MAX);
    GO
    DECLARE @NumeroD nvarchar(10) = N'123457';
    DECLARE  @MAX INT = (SELECT ISNULL(max(NroLinea),0)+1 from SAITEMOPI WHERE numeroD = @numeroD);
    INSERT INTO SAITEMOPI 
    VALUES (@NumeroD, @MAX);
    GO
    DECLARE @NumeroD nvarchar(10) = N'123457';
    DECLARE  @MAX INT = (SELECT ISNULL(max(NroLinea),0)+1 from SAITEMOPI WHERE numeroD = @numeroD);
    INSERT INTO SAITEMOPI 
    VALUES (@NumeroD, @MAX);
    GO
    DECLARE @NumeroD nvarchar(10) = N'123457';
    DECLARE  @MAX INT = (SELECT ISNULL(max(NroLinea),0)+1 from SAITEMOPI WHERE numeroD = @numeroD);
    INSERT INTO SAITEMOPI 
    VALUES (@NumeroD, @MAX);
    GO
    DECLARE @NumeroD nvarchar(10) = N'123458';
    DECLARE  @MAX INT = (SELECT ISNULL(max(NroLinea),0)+1 from SAITEMOPI WHERE numeroD = @numeroD);
    INSERT INTO SAITEMOPI 
    VALUES (@NumeroD, @MAX);
    GO

    Como puedes observar he creado una tabla donde tengo un numeroD y una línea calculada con el max. E inserto por el númeroD que me llega como parámetro el número de línea que le corresponda.

    Eso produce el resultado esperado.

    Es muy importante como te explique en la anterior entrada que tengas un nivel de aislamiento que te permita garantizar que no se duplicarán las filas:

    Create PROC INSERTSAITEMOPI (
    	@numeroD	nvarchar(10),
    	@coditem	nvarchar(30),
    	@CodUbic	varchar(10),
    	@codubic2	varchar(10),
    	@Cantidad	decimal(18,4),
    	@NroUnico	int,
    	@NroLote	nvarchar(100)
    )
    as 
    begin
    SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
    BEGIN TRY
    BEGIN TRAN
    	declare		@Descrip1 nvarchar(150),
    				@Descrip3 nvarchar(100),
    				@costo  decimal (18,4),
    				@NroLinea int
    	
    	
    	Select 
    		@Descrip1	= Descrip ,
    		@Descrip3	= Descrip3, 
    		@costo		= CostPro 
    	From SAPROD where CodProd = @coditem
    	
    	DECLARE  @MAX INT = (SELECT ISNULL(max(NroLinea),0)+1 from SAITEMOPI WHERE NumeroD = @numeroD );
    	
    	
    	insert into SAITEMOPI(CodSucu,TipoOpI,NumeroD,NroLinea,NroLineaC,CodItem,CodUbic,CodUbic2,Descrip1,Descrip2,
    		Descrip3,Descrip4,Descrip5,Descrip6,Descrip7,Descrip8,Descrip9,Descrip10,Refere,Signo,Tara,CantidadD,Cantidad,
    		CantidadO,ExistAntU,ExistAnt,ExistAntU2,ExistAnt2,CantidadU,CantidadA,CantidadUA,Costo,Descto,CodMeca,TotalItem,NroUnicoL,
    		NroLote,Precio,PrecioU,FechaE,FechaL,FechaV,EsServ,EsUnid,EsFreeP,EsPesa,EsExento,UsaServ,DEsLote,DEsSeri) 
    	values('00000','N', @numeroD,@MAX ,0,@coditem,@CodUbic,@codubic2,@Descrip1,'',@Descrip3,'','','','','','','','',0,0.0000,
    		0.0000,@Cantidad,0.0000,0.0000,0.0000,0.0000,0.0000,0.0000,0.0000,0.0000,@costo,0.0000,NULL,0.0000,@NroUnico,
    		@NroLote,0.0000,0.0000,GETDATE(),GETDATE(),NULL,0,0,0,0,0,0,1,0);
    
    	COMMIT TRAN;
    END TRY
    BEGIN CATCH
    	IF @@TRANCOUNT > 0
    		ROLLBACK TRAN;
    	THROW;
    END CATCH
    
    end;

    Puntos importantes a tener en cuenta de la solución planteada.

    El nivel de aislamiento

    El max para el numero D

    La inserción del max

    sábado, 8 de agosto de 2020 5:22

Todas las respuestas

  • hola

    >>hay alguna manera de insertar datos repetidos donde hay una llave primaria

    no, no lo hay

    es justamente la definicion de key la que quieres evadir

    >>pero solo me guarda el primer registro y con los siguientes salta error por qué se repite el número de factura

    estas seguro que el problema es la key o es una validacion de la aplicacion o de la base de datos?

    en la db se pueden definir contraints que apliquen un UNIQUE sobre determinados campos mas alla de que sea key o no

    >>creo q no es conveniente eliminar ese llave para que haya duplicidad de datos

    Si deberias realizar un DELETE para que elimine fisicamente el registro y luego realices el nuevo INSERT

    o tambien podrias evaluar realizar un UPDATE modificando los datos de la factura existente con los nuevos datos

    saludos


    Leandro Tuttini

    Blog
    MVP Profile
    Buenos Aires
    Argentina

    lunes, 3 de agosto de 2020 5:03
  • Hola Dannycv012:

    No es posible insertar datos repetidos en una tabla donde exista una clave primaria. Normalmente las facturas tienen un maestro y un detalle. En el maestro se anotan el número de factura, la fecha, cliente etc.... y en el detalle se anotan cada linea de item que se factura, al igual que puedes ver en una factura real. El detalle se relaciona con la factura por este número como clave foránea en la tabla de detalle.

    Create table Lab_factura (numFactura int primary key, cliente varchar(100))
    Create table Lab_Detalle( numfactura int, linea int, idArticulo int, primary key (numfactura, linea));
    go
    Insert into Lab_factura (numFactura, cliente)
    values
    (1,'juan'),
    (2,'ana');
    go

    Puedo tener la tabla de facturas con datos.

    Pero si intento insertar otra vez un registro existente en la/s columna/s identificadas como clave primaria.

    insert into Lab_factura(numFactura,cliente)
    values
    (1,'pepe');

    Mens. 2627, Nivel 14, Estado 1, Línea 9
    Infracción de la restricción PRIMARY KEY 'PK__Lab_fact__C989668B82DF1C53'. No se puede insertar una clave duplicada en el objeto 'dbo.Lab_factura'. El valor de la clave duplicada es (1).
    Se terminó la instrucción.

    En mi tabla de detalle puedo tener múltiples lineas que hacen referencia a una factura existente. Relacionando el detalle, con la factura, mediante una foreign key.

    alter table Lab_detalle 
    add constraint fk_LabFactura foreign key (numfactura) references Lab_factura (numFactura);
    go
    

    Entonces:

    Insert into Lab_Detalle (numFactura, linea, idarticulo)
    values
    (1,1,1),
    (1,2,100);

    Por tanto la factura 1 está compuesta de su maestro y su detalle.

    Existen varias maneras de implementar un maestro/detalle, esto solo era un pequeño ejemplo

    lunes, 3 de agosto de 2020 5:21
  • Hola Dannycv012, 

      

    ¿Alguna novedad sobre la consulta realizada? ¿Han sido útiles las  respuestas proporcionadas?  

    Espero su respuesta.  

    Cualquier duda referente a productos Microsoft, puedes consultarnos. Es un gusto informarte. 

    Gracias por usar los foros de MSDN.  

    Eric Ruiz

    ____________________________  

     

    Por favor recuerde "Marcar como respuesta" las respuestas que hayan resuelto su problema, es una forma común de reconocer a aquellos que han ayudado, y hace que sea más fácil para los otros visitantes encontrar la solución más tarde.  

    Microsoft ofrece este servicio de forma gratuita, con la finalidad de ayudar a los usuarios y la ampliación de la base de datos de conocimientos relacionados con los productos y tecnologías de Microsoft.   

    Este contenido es proporcionado "tal cual" y no implica ninguna responsabilidad de parte de Microsoft. 

    lunes, 3 de agosto de 2020 17:00
    Moderador
  • Javi fernandez , ya he logrado insertar los datos en la tabla con la numeracion  en nrolinea , para ello utilice  CREATE SEQUENCE , pero resulta que esto se debe enumerar del 1 hasta los item que se ingresen en cada Remision , como puedo hacer para que siempre que inserte una remision y se haga el conteo de los item y estos se enumeren del 1 hasta el ultimo  item que se remisiono , por ejemplo  la remision 01 tiene 20 items  y debe contar del 1 hasta  el 20 y si ingreso una nueva remision y esta solo tiene dos item debe iniciar el conteo otra vez y seria de 1 a 2 y asi simpre que el numero de remision sea diferenten. comparto mi codigo 

    CREATE SEQUENCE Nrolinea_seq
      AS BIGINT
      START WITH 1
      INCREMENT BY 1
      NO CYCLE

    Create PROC INSERTSAITEMOPI
    @numeroD nvarchar(10),
    @coditem nvarchar(30),
    @CodUbic varchar(10),
    @codubic2 varchar(10),
    @Cantidad decimal(18,4),
    @NroUnico int,
    @NroLote nvarchar(100)

    as 
    begin
    declare @Descrip1 nvarchar(150),
             @Descrip3 nvarchar(100),
    @costo  decimal (18,4),
    @NroLinea int


    select @Descrip1=Descrip ,
    @Descrip3=Descrip3, 
    @costo = CostPro 
    from SAPROD where CodProd = @coditem




    insert into SAITEMOPI(CodSucu,TipoOpI,NumeroD,NroLinea,NroLineaC,CodItem,CodUbic,CodUbic2,Descrip1,Descrip2,
    Descrip3,Descrip4,Descrip5,Descrip6,Descrip7,Descrip8,Descrip9,Descrip10,Refere,Signo,Tara,CantidadD,Cantidad,
    CantidadO,ExistAntU,ExistAnt,ExistAntU2,ExistAnt2,CantidadU,CantidadA,CantidadUA,Costo,Descto,CodMeca,TotalItem,NroUnicoL,
    NroLote,Precio,PrecioU,FechaE,FechaL,FechaV,EsServ,EsUnid,EsFreeP,EsPesa,EsExento,UsaServ,DEsLote,DEsSeri) 
    values('00000','N', @numeroD,NEXT VALUE FOR Nrolinea_seq ,0,@coditem,@CodUbic,@codubic2,@Descrip1,'',@Descrip3,'','','','','','','','',0,0.0000,
    0.0000,@Cantidad,0.0000,0.0000,0.0000,0.0000,0.0000,0.0000,0.0000,0.0000,@costo,0.0000,NULL,0.0000,@NroUnico,
    @NroLote,0.0000,0.0000,GETDATE(),GETDATE(),NULL,0,0,0,0,0,0,1,0)

     








    end;


    Daniel


    • Editado Dannycv012 lunes, 3 de agosto de 2020 21:40
    lunes, 3 de agosto de 2020 21:39
  • Hola Dannycv012:

    No es válido el uso de sequence para ese fin. O al menos no es su utilidad.

    Te dejo un enlace sobre como se pueden generar autonuméricos, que te será de utilidad. Además de una posible solución, donde verás explicadas las lineas que te añado.

    Id incremental

    https://javifer2.wordpress.com/2019/11/23/id-incremental-como/

    Create PROC INSERTSAITEMOPI
    @numeroD nvarchar(10),
    @coditem nvarchar(30),
    @CodUbic varchar(10),
    @codubic2 varchar(10),
    @Cantidad decimal(18,4),
    @NroUnico int,
    @NroLote nvarchar(100)
    
    as 
    begin
    SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
    BEGIN TRY
    BEGIN TRAN
    	declare @Descrip1 nvarchar(150),
    	         @Descrip3 nvarchar(100),
    	@costo  decimal (18,4),
    	@NroLinea int
    	
    	
    	select 
    	@Descrip1=Descrip ,
    	@Descrip3=Descrip3, 
    	@costo = CostPro 
    	from SAPROD where CodProd = @coditem
    	
    	DECLARE  @MAX INT = (SELECT ISNULL(max(NroLinea),0)+1 from SAITEMOPI WHERE NumeroD = @numeroD );
    	
    	
    	insert into SAITEMOPI(CodSucu,TipoOpI,NumeroD,NroLinea,NroLineaC,CodItem,CodUbic,CodUbic2,Descrip1,Descrip2,
    	Descrip3,Descrip4,Descrip5,Descrip6,Descrip7,Descrip8,Descrip9,Descrip10,Refere,Signo,Tara,CantidadD,Cantidad,
    	CantidadO,ExistAntU,ExistAnt,ExistAntU2,ExistAnt2,CantidadU,CantidadA,CantidadUA,Costo,Descto,CodMeca,TotalItem,NroUnicoL,
    	NroLote,Precio,PrecioU,FechaE,FechaL,FechaV,EsServ,EsUnid,EsFreeP,EsPesa,EsExento,UsaServ,DEsLote,DEsSeri) 
    	values('00000','N', @numeroD,@MAX ,0,@coditem,@CodUbic,@codubic2,@Descrip1,'',@Descrip3,'','','','','','','','',0,0.0000,
    	0.0000,@Cantidad,0.0000,0.0000,0.0000,0.0000,0.0000,0.0000,0.0000,0.0000,@costo,0.0000,NULL,0.0000,@NroUnico,
    	@NroLote,0.0000,0.0000,GETDATE(),GETDATE(),NULL,0,0,0,0,0,0,1,0);
    
    	COMMIT TRAN;
    END TRY
    BEGIN CATCH
    	IF @@TRANCOUNT > 0
    		ROLLBACK TRAN;
    	THROW;
    END CATCH
    
    end;

    Por otro lado viendo la sentencia de inserción, hay cosas que no parecen tener lógica.

    Si ya tienes un item para insertar en la linea, puedo entender que haya propiedades que  le muestres al usuario y que estas se puedan modificar en la inserción, que ya no son del item. Por ejemplo la descripción. Antes de insertar se la entregas al usuario, y al guardar, esta la guardas, porque el mismo pudo hacer alguna modificación y entonces pasa a ser en vez de la descripción del item, el concepto de guardado de la linea. Pero no parece normal, tener que guardar en una linea, esa cantidad de información que te estas llevando vacía.

    Para que Descript1 a Descript10, entre otras columnas. Si eso me supongo que lo tienes en la tabla de artículos, y lo puedes obtener relacionando ambas tablas.



    martes, 4 de agosto de 2020 5:31
  • Javier es que segun el requerimiento debe ser asi , lo que he logrado  ver es que esta base de datos no esta totalmente normalizada y para mi hubiera sido mucho mas  facil insertar los item de los producto en otra tabla y con el codigo de la remision relaciono las tablas  pero ellos insisten en que deben ir ahi y  que cada vez que se inserte una remision diferente el numero linea  marcara desde 1 hasta  la cantidad de item que tenga cada remision.

    el numero de remision se guarda en la columna numeroD comparto una imgen de como debe ir la secuencia cada vez que cambie el numeroD

    hay alguno forma de reiniciar la secuencia de numeros en nrolinea cada vez que se ingrese un numeroD nuevo eso lo que me indica es cuantos item tiene cada numeroD


    Daniel


    • Editado Dannycv012 martes, 4 de agosto de 2020 14:39
    martes, 4 de agosto de 2020 13:23
  • Hola Dannycv012:

    No obstante, algo que esta mal, estará mal siempre.

    Pero el código que te he facilitado asume lo que tenías, y te obtiene el número de linea para la transacción, en la que estás.

    Fíjate que tienes que tener el nivel de aislamiento, no vaya a ser que te inserten lineas repetidas (si hay alta concurrencia), y puede generarte problemas.

    Ojo voy a modificar la respuesta anterior, porque te pegue parte del código, donde se calcula el max pero no te lo inserto.

    martes, 4 de agosto de 2020 14:06
  • javier es que eso que me piden funciona actualmente en otro aplicativo , pero yo no he logra encontar la forma de realizar ese paso y tampoco veo en los procedimientos almacenados de aplicativo ya mencionado como hacen esa secuencia.

    sera que  debo tener algo encuenta con esta restricción 

    Infracción de la restricción PRIMARY KEY 'SAITEMOPI_IX0'. No se puede insertar una clave duplicada en el objeto 'dbo.SAITEMOPI'. El valor de la clave duplicada es (00000, N, 049312, 1, 0).
    Se terminó la instrucción. 

    lo  raro es que el otro aplicativo si lo hace


    Daniel


    • Editado Dannycv012 martes, 4 de agosto de 2020 15:01
    martes, 4 de agosto de 2020 14:57
  • Hola Dannycv012, 

      

    Gracias por tu respuesta de nuevo.

      

    Si resuelves el problema que estás experimentando en tu entorno, te agradecería que compartieras la solución con la comunidad de MSDN. 

      

    Espero tu respuesta. 

      

    Cualquier duda referente a productos Microsoft, puedes consultarnos. Es un gusto informarte. 

    Gracias por usar los foros de MSDN. 

      

    Eric Ruiz

    ____________________________ 

      

    Por favor recuerde "Marcar como respuesta" las respuestas que hayan resuelto su problema, es una forma común de reconocer a aquellos que han ayudado, y hace que sea más fácil para los otros visitantes encontrar la solución más tarde.  

    Microsoft ofrece este servicio de forma gratuita, con la finalidad de ayudar a los usuarios y la ampliación de la base de datos de conocimientos relacionados con los productos y tecnologías de Microsoft.   

    Este contenido es proporcionado "tal cual" y no implica ninguna responsabilidad de parte de Microsoft. 

    viernes, 7 de agosto de 2020 17:54
    Moderador
  • Hola DannyCV012:

    Mira tu escenario super simplificado:

    create table SAITEMOPI (numeroD nvarchar(10), nroLInea int)
    go
    DECLARE @NumeroD nvarchar(10) = N'123455';
    DECLARE  @MAX INT = (SELECT ISNULL(max(NroLinea),0)+1 from SAITEMOPI WHERE numeroD = @numeroD);
    INSERT INTO SAITEMOPI 
    VALUES (@NumeroD, @MAX);
    GO
    DECLARE @NumeroD nvarchar(10) = N'123456';
    DECLARE  @MAX INT = (SELECT ISNULL(max(NroLinea),0)+1 from SAITEMOPI WHERE numeroD = @numeroD);
    INSERT INTO SAITEMOPI 
    VALUES (@NumeroD, @MAX);
    GO
    DECLARE @NumeroD nvarchar(10) = N'123457';
    DECLARE  @MAX INT = (SELECT ISNULL(max(NroLinea),0)+1 from SAITEMOPI WHERE numeroD = @numeroD);
    INSERT INTO SAITEMOPI 
    VALUES (@NumeroD, @MAX);
    GO
    DECLARE @NumeroD nvarchar(10) = N'123457';
    DECLARE  @MAX INT = (SELECT ISNULL(max(NroLinea),0)+1 from SAITEMOPI WHERE numeroD = @numeroD);
    INSERT INTO SAITEMOPI 
    VALUES (@NumeroD, @MAX);
    GO
    DECLARE @NumeroD nvarchar(10) = N'123457';
    DECLARE  @MAX INT = (SELECT ISNULL(max(NroLinea),0)+1 from SAITEMOPI WHERE numeroD = @numeroD);
    INSERT INTO SAITEMOPI 
    VALUES (@NumeroD, @MAX);
    GO
    DECLARE @NumeroD nvarchar(10) = N'123457';
    DECLARE  @MAX INT = (SELECT ISNULL(max(NroLinea),0)+1 from SAITEMOPI WHERE numeroD = @numeroD);
    INSERT INTO SAITEMOPI 
    VALUES (@NumeroD, @MAX);
    GO
    DECLARE @NumeroD nvarchar(10) = N'123458';
    DECLARE  @MAX INT = (SELECT ISNULL(max(NroLinea),0)+1 from SAITEMOPI WHERE numeroD = @numeroD);
    INSERT INTO SAITEMOPI 
    VALUES (@NumeroD, @MAX);
    GO

    Como puedes observar he creado una tabla donde tengo un numeroD y una línea calculada con el max. E inserto por el númeroD que me llega como parámetro el número de línea que le corresponda.

    Eso produce el resultado esperado.

    Es muy importante como te explique en la anterior entrada que tengas un nivel de aislamiento que te permita garantizar que no se duplicarán las filas:

    Create PROC INSERTSAITEMOPI (
    	@numeroD	nvarchar(10),
    	@coditem	nvarchar(30),
    	@CodUbic	varchar(10),
    	@codubic2	varchar(10),
    	@Cantidad	decimal(18,4),
    	@NroUnico	int,
    	@NroLote	nvarchar(100)
    )
    as 
    begin
    SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
    BEGIN TRY
    BEGIN TRAN
    	declare		@Descrip1 nvarchar(150),
    				@Descrip3 nvarchar(100),
    				@costo  decimal (18,4),
    				@NroLinea int
    	
    	
    	Select 
    		@Descrip1	= Descrip ,
    		@Descrip3	= Descrip3, 
    		@costo		= CostPro 
    	From SAPROD where CodProd = @coditem
    	
    	DECLARE  @MAX INT = (SELECT ISNULL(max(NroLinea),0)+1 from SAITEMOPI WHERE NumeroD = @numeroD );
    	
    	
    	insert into SAITEMOPI(CodSucu,TipoOpI,NumeroD,NroLinea,NroLineaC,CodItem,CodUbic,CodUbic2,Descrip1,Descrip2,
    		Descrip3,Descrip4,Descrip5,Descrip6,Descrip7,Descrip8,Descrip9,Descrip10,Refere,Signo,Tara,CantidadD,Cantidad,
    		CantidadO,ExistAntU,ExistAnt,ExistAntU2,ExistAnt2,CantidadU,CantidadA,CantidadUA,Costo,Descto,CodMeca,TotalItem,NroUnicoL,
    		NroLote,Precio,PrecioU,FechaE,FechaL,FechaV,EsServ,EsUnid,EsFreeP,EsPesa,EsExento,UsaServ,DEsLote,DEsSeri) 
    	values('00000','N', @numeroD,@MAX ,0,@coditem,@CodUbic,@codubic2,@Descrip1,'',@Descrip3,'','','','','','','','',0,0.0000,
    		0.0000,@Cantidad,0.0000,0.0000,0.0000,0.0000,0.0000,0.0000,0.0000,0.0000,@costo,0.0000,NULL,0.0000,@NroUnico,
    		@NroLote,0.0000,0.0000,GETDATE(),GETDATE(),NULL,0,0,0,0,0,0,1,0);
    
    	COMMIT TRAN;
    END TRY
    BEGIN CATCH
    	IF @@TRANCOUNT > 0
    		ROLLBACK TRAN;
    	THROW;
    END CATCH
    
    end;

    Puntos importantes a tener en cuenta de la solución planteada.

    El nivel de aislamiento

    El max para el numero D

    La inserción del max

    sábado, 8 de agosto de 2020 5:22