Aquí podría ser tu PUBLICIDAD


Compruebe si existe una fila, de lo contrario inserte

votos
194

Necesito escribir un procedimiento almacenado de T-SQL que actualice una fila en una tabla. Si la fila no existe, insértela. Todos estos pasos envueltos por una transacción.

Esto es para un sistema de reserva, por lo que debe ser atómico y confiable . Debe devolver verdadero si la transacción fue comprometida y el vuelo reservado.

Soy nuevo en T-SQL y no estoy seguro de cómo usarlo @@rowcount. Esto es lo que he escrito hasta ahora. ¿Estoy en el camino correcto? Estoy seguro de que es un problema fácil para ti.

-- BEGIN TRANSACTION (HOW TO DO?)

UPDATE Bookings
 SET TicketsBooked = TicketsBooked + @TicketsToBook
 WHERE FlightId = @Id AND TicketsMax < (TicketsBooked + @TicketsToBook)

-- Here I need to insert only if the row doesn't exists.
-- If the row exists but the condition TicketsMax is violated, I must not insert 
-- the row and return FALSE

IF @@ROWCOUNT = 0 
BEGIN

 INSERT IGNORE  INTO Bookings ... (omitted)

END

-- END TRANSACTION (HOW TO DO?)

-- Return TRUE (How to do?)
Publicado el 12/03/2009 a las 19:17
fuente por usuario Robert
En otros idiomas...        العربية       

11 respuestas

votos
138

Eche un vistazo al comando MERGE . Puedes hacerlo UPDATE, INSERT IGNORE y DELETEen una declaración.

Aquí hay una implementación en funcionamiento sobre el uso MERGE
: comprueba si el vuelo está lleno antes de hacer una actualización, de lo contrario, realiza una inserción.

if exists(select 1 from INFORMATION_SCHEMA.TABLES T 
              where T.TABLE_NAME = 'Bookings') 
begin
    drop table Bookings
end
GO

create table Bookings(
  FlightID    int identity(1, 1) primary key,
  TicketsMax    int not null,
  TicketsBooked int not null
)
GO

insert  Bookings(TicketsMax, TicketsBooked) select 1, 0
insert  Bookings(TicketsMax, TicketsBooked) select 2, 2
insert  Bookings(TicketsMax, TicketsBooked) select 3, 1
GO

select * from Bookings

Y entonces ...

declare @FlightID int = 1
declare @TicketsToBook int = 2

--; This should add a new record
merge Bookings as T
using (select @FlightID as FlightID, @TicketsToBook as TicketsToBook) as S
    on  T.FlightID = S.FlightID
      and T.TicketsMax > (T.TicketsBooked + S.TicketsToBook)
  when matched then
    update set T.TicketsBooked = T.TicketsBooked + S.TicketsToBook
  when not matched then
    insert (TicketsMax, TicketsBooked) 
    values(S.TicketsToBook, S.TicketsToBook);

select * from Bookings
Respondida el 13/03/2009 a las 05:34
fuente por usuario Sung


Aquí podría ser tu PUBLICIDAD


votos
132

Supongo que una sola fila para cada vuelo? Si es así:

IF EXISTS (SELECT * FROM Bookings WHERE FLightID = @Id)
BEGIN
    --UPDATE HERE
END
ELSE
BEGIN
   -- INSERT IGNORE  HERE
END

Asumo lo que dije, ya que su forma de hacer las cosas puede sobreescribir un vuelo, ya que insertará una nueva fila cuando haya 10 boletos máximos y usted esté reservando 20.

Respondida el 12/03/2009 a las 07:21
fuente por usuario Gregory A Beamer

votos
61

Pase UPDLOCK, rowlock, consejos HOLDLOCK cuando las pruebas de la existencia de la fila.

begin tran /* default read committed isolation level is fine */

if not exists (select * from Table with (updlock, rowlock, holdlock) where ...)
    /* insert */
else
    /* update */

commit /* locks are released here */

La sugerencia UPDLOCK obliga a la consulta para tomar un bloqueo de actualización en la fila si ya existe, la prevención de otras transacciones modifiquen hasta que confirmar o deshacer.

La sugerencia holdlock obliga a la consulta para tomar una cerradura gama, la prevención de otras transacciones de la adición de una fila a juego con su búsqueda hasta que confirmar o deshacer.

Las fuerzas rowlock sugerencia de bloqueo granularidad a nivel de fila en lugar del nivel de página por defecto, por lo que su operación no bloqueará otras transacciones que intentan actualizar filas independientes en la misma página (pero tenga en cuenta la compensación entre la contención reducida y el aumento de sobrecarga de bloqueo - usted debe evitar tomar un gran número de bloqueos a nivel de fila en una sola transacción).

Ver http://msdn.microsoft.com/en-us/library/ms187373.aspx para más información.

Tenga en cuenta que las cerraduras se toman como las declaraciones que toman ellos se ejecutan - invocando comenzar tran no le da inmunidad contra la otra transacción pellizcos cerraduras en algo antes de llegar a ella. Usted debe tratar de factorizar el SQL para mantener bloqueos durante el menor tiempo posible confirmar la transacción tan pronto como sea posible (adquirir tarde, la libertad anticipada).

Tenga en cuenta que los bloqueos a nivel de fila pueden ser menos eficaces si su PK es un bigint, como el hash interna en SQL Server es degenerado para valores de 64 bits (diferentes valores clave pueden hash en el mismo ID de bloqueo).

Respondida el 15/04/2010 a las 02:03
fuente por usuario Cassius Porcus

votos
34

estoy escribiendo mi solución. mi método no se sostiene 'si' o 'fusión'. mi método es fácil.

INSERT IGNORE  INTO TableName (col1,col2)
SELECT @par1, @par2
   WHERE NOT EXISTS (SELECT col1,col2 FROM TableName
                     WHERE col1=@par1 AND col2=@par2)

Por ejemplo:

INSERT IGNORE  INTO Members (username)
SELECT 'Cem'
   WHERE NOT EXISTS (SELECT username FROM Members
                     WHERE username='Cem')

Explicación:

(1) col1 SELECT, col2 DE TableName DONDE col1 = @ par1 Y col2 = @ par2 Se selecciona de TableName valores buscado

(2) SELECT @ par1, @ par2 DONDE NO EXISTE Se necesita, si no existe a partir de (1) subconsulta

(3) se inserta en nombreTabla (2) valores de paso

Respondida el 25/02/2013 a las 01:48
fuente por usuario Cem

votos
2

Esto es algo que acabo de hacer recientemente:

set ANSI_NULLS ON
set QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[cjso_UpdateCustomerLogin]
    (
      @CustomerID AS INT,
      @UserName AS VARCHAR(25),
      @Password AS BINARY(16)
    )
AS 
    BEGIN
        IF ISNULL((SELECT CustomerID FROM tblOnline_CustomerAccount WHERE CustomerID = @CustomerID), 0) = 0
        BEGIN
            INSERT IGNORE  INTO [tblOnline_CustomerAccount] (
                [CustomerID],
                [UserName],
                [Password],
                [LastLogin]
            ) VALUES ( 
                /* CustomerID - int */ @CustomerID,
                /* UserName - varchar(25) */ @UserName,
                /* Password - binary(16) */ @Password,
                /* LastLogin - datetime */ NULL ) 
        END
        ELSE
        BEGIN
            UPDATE  [tblOnline_CustomerAccount]
            SET     UserName = @UserName,
                    Password = @Password
            WHERE   CustomerID = @CustomerID    
        END

    END
Respondida el 12/03/2009 a las 07:23
fuente por usuario TheTXI

votos
1

Finalmente pude insertar una fila, con la condición de que no existe aún, usando el siguiente modelo:

INSERT IGNORE  INTO table ( column1, column2, column3 )
(
    SELECT $column1, $column2, $column3
      WHERE NOT EXISTS (
        SELECT 1
          FROM table 
          WHERE column1 = $column1
          AND column2 = $column2
          AND column3 = $column3 
    )
)

lo que he encontrado en:

http://www.postgresql.org/message-id/87hdow4ld1.fsf@stark.xeocode.com

Respondida el 05/01/2015 a las 09:46
fuente por usuario Paul G

votos
1

Puede usar la Funcionalidad Merge para lograrlo. De lo contrario, puedes hacer:

declare @rowCount int

select @rowCount=@@RowCount

if @rowCount=0
begin
--insert....
Respondida el 12/03/2009 a las 07:25
fuente por usuario JoshBerke

votos
0

La mejor aproximación a este problema es hacer primero la columna de la base de datos única

ALTER TABLE table_name ADD UNIQUE KEY

THEN INSERT IGNORE IGNORE INTO table_name , No se insertará el valor si resulta en una clave duplicada / ya existe en la tabla.

Respondida el 12/07/2018 a las 03:03
fuente por usuario Maurice Elagu

votos
0
INSERT IGNORE  INTO Database.dbo.Table SELECT * FROM Database.dbo.Table
 WHERE ID not in (select ID from Database.dbo.Table)
Respondida el 03/04/2017 a las 02:23
fuente por usuario Almamun

votos
0

Solución completa es a continuación (incluyendo la estructura de cursor). Muchas gracias a Cassius Porcus para el begin trans ... commitcódigo para volver arriba.

declare @mystat6 bigint
declare @mystat6p varchar(50)
declare @mystat6b bigint

DECLARE mycur1 CURSOR for

 select result1,picture,bittot from  all_Tempnogos2results11

 OPEN mycur1

 FETCH NEXT FROM mycur1 INTO @mystat6, @mystat6p , @mystat6b

 WHILE @@Fetch_Status = 0
 BEGIN

 begin tran /* default read committed isolation level is fine */

 if not exists (select * from all_Tempnogos2results11_uniq with (updlock, rowlock, holdlock)
                     where all_Tempnogos2results11_uniq.result1 = @mystat6 
                        and all_Tempnogos2results11_uniq.bittot = @mystat6b )
     insert all_Tempnogos2results11_uniq values (@mystat6 , @mystat6p , @mystat6b)

 --else
 --  /* update */

 commit /* locks are released here */

 FETCH NEXT FROM mycur1 INTO @mystat6 , @mystat6p , @mystat6b

 END

 CLOSE mycur1

 DEALLOCATE mycur1
 go
Respondida el 02/10/2013 a las 12:41
fuente por usuario user2836818

votos
-3
INSERT IGNORE  INTO table ( column1, column2, column3 )
SELECT $column1, $column2, $column3
EXCEPT SELECT column1, column2, column3
FROM table
Respondida el 27/03/2015 a las 10:45
fuente por usuario Aaron


Aquí podría ser tu PUBLICIDAD