2013-06-18 10 views
8
While @@Fetch_Status = 0 
Begin 
    INSERT INTO [server].MyDatabase.dbo.Mytabletobeinserted (

    UPC, 
    Sale_date) 
    VALUES(
    @UPC, 
    @Sale_date) 

    'Inserting the error trapping here' 

    IF (@@ERROR <> 0) 
    BEGIN 
     ROLLBACK TRANSACTION; 
     RETURN; 
    END 

    Update t_sale_from_pos 
    set been_sent = 'y' 
    where UPC = @UPC and [email protected]_date 

    Fetch Next from CursorSale 
    into 
    @UPC, 
    @Sale_date 
end 
close CursorSale 

deallocate CursorSale 

Questa procedura memorizzata viene eseguita ogni giorno con uno scheduler ei dati vengono passati sulla rete. Quando si esegue questa stored procedure e quindi lungo il processo di esecuzione, si verifica un improvviso timeout di rete. Questo esegue ogni riga da inviare all'altro server sulla rete. Come si influisce sui dati utilizzando cicli con transazione di rollback


Ora, il mio problema è:

  • Quali saranno le conseguenze i dati chiamando l'operazione di rollback all'interno del ciclo?
  • Leggerà di nuovo tutte le righe e lo invierà al server per l'inserimento o leggerà solo la riga dove fallisce durante l'esecuzione?

Hai bisogno di un consiglio. Grazie

+0

C'è un motivo particolare per cui si sta facendo questo con un cursore invece di un inserto seguito da un aggiornamento all'interno di una transazione? – peterm

+0

Poiché questa tabella contiene migliaia di dati all'interno, deve essere posizionata su un cursore in modo che il server non sia troppo occupato durante questa esecuzione. È una cattiva pratica? – Androidz

+3

@Androidz - sì, questo genererà un sacco di lavoro in più per il database che farlo come un set. Nella mia esperienza, questa mitica "best practice" di fare blocchi di righe è diffusa dagli amministratori di database che non possono essere disturbati a gestire il registro delle transazioni e gli spazi tabella in modo appropriato per il carico di lavoro. – LoztInSpace

risposta

6

Si potrebbe provare begin distributed transaction. Distributed transaction si adatta perfettamente in questo caso poiché le transazioni distribuite sono state progettate per estendersi su due o più server. Con la transazione, anche il sistema si blocca, o c'è un'interruzione di corrente, il sistema è ancora in grado di ripristinare il suo stato coerente.

BEGIN DISTRIBUTED TRANSACTION; 

BEGIN TRY 
    //Your code here to create the cursor. 
    While @@Fetch_Status = 0 
    Begin 
     INSERT INTO [server].MyDatabase.dbo.Mytabletobeinserted(UPC,Sale_date) 
                  VALUES(@UPC,@Sale_date) 

     Update t_sale_from_pos 
     set been_sent = 'y' 
     where UPC = @UPC and [email protected]_date 

     Fetch Next from CursorSale into @UPC,@Sale_date 
    END 
    close CursorSale 

    deallocate CursorSale 
END TRY 
BEGIN CATCH 
    close CursorSale 

    deallocate CursorSale 

    IF @@TRANCOUNT > 0 
     ROLLBACK TRANSACTION; 
END CATCH; 

IF @@TRANCOUNT > 0 
    COMMIT TRANSACTION; 
GO 

In realtà, con le transazioni distribuite non è necessario utilizzare il cursore. Nel tuo caso, è meglio considerare anche di evitare i problemi di concorrenza creando una tabella temporanea. La ragione di ciò è la seguente: l'istruzione di inserimento può richiedere tempo e mentre inserisce dati, la tabella può essere aggiornata da un altro utente e l'istruzione di aggiornamento che si verifica dopo può aggiornare le righe errate.

BEGIN DISTRIBUTED TRANSACTION; 

BEGIN TRY 
    CREATE TABLE #LocalTempTable(UPC int,Sale_date datetime) 
    INSERT INTO #LocalTempTable(UPC,Sale_date) 
    SELECT UPC,Sale_date 
    FROM YourTable 

    INSERT INTO [server].MyDatabase.dbo.Mytabletobeinserted(UPC,Sale_date) 
    SELECT UPC,Sale_date 
    FROM #LocalTempTable 

    Update t_sale_from_pos 
    set been_sent = 'y' 
    where EXISTS (SELECT * 
        FROM #LocalTempTable 
        WHERE #LocalTempTable.UPC = t_sale_from_pos.UPC 
         AND #LocalTempTable.Sale_date = t_sale_from_pos.Sale_date) 

END 
END TRY 
BEGIN CATCH 
    IF @@TRANCOUNT > 0 
     ROLLBACK TRANSACTION; 
END CATCH; 

IF @@TRANCOUNT > 0 
    COMMIT TRANSACTION; 
GO 
+0

+1 per la tua persistenza e la buona risposta. – Devart

+0

@khan ko gli utenti non hanno nulla a che fare con questo perché questa stored procedure viene eseguita quando il negozio si chiude. non si inserisce correttamente nell'altra tabella quando si verifica un errore come timeout? – Androidz

+0

@Androidz: non importa se questo è per gli utenti o per il processo in background. Il fatto è che tutte le nostre istruzioni SQL eseguite devono garantire la coerenza dei dati. Se c'è un errore, il modo più sicuro è quello di eseguire il rollback della transazione, altrimenti i dati potrebbero essere in uno stato incoerente. –

3

Se ho capito bene. Try/catch dovrebbe aiutare -

WHILE @@FETCH_STATUS = 0 BEGIN 

    BEGIN TRY 

      INSERT INTO [server].MyDatabase.dbo.Mytabletobeinserted (UPC, Sale_date) 
      SELECT @UPC, @Sale_date 

      UPDATE dbo.t_sale_from_pos 
      SET been_sent = 'y' 
      WHERE UPC = @UPC 
      AND sale_date = @sale_date 

    END TRY 
    BEGIN CATCH 

      UPDATE dbo.t_sale_from_pos 
      SET been_sent = 'n' 
      WHERE UPC = @UPC 
      AND sale_date = @sale_date 

    END CATCH 

    FETCH NEXT FROM CursorSale INTO @UPC, @Sale_date 

END 

CLOSE CursorSale 
DEALLOCATE CursorSale 
+0

Non è necessario stampare l'errore poiché la stored procedure viene eseguita su un pianificatore di processi come quello che ho menzionato nel mio post. – Androidz

+0

Ok. Ho rimosso 'PRINT' dalla mia risposta. – Devart

+0

@Magnus grazie per la tua recensione. È davvero divertente leggere questo: 'CRY/CATCH' :) – Devart

1

Penso che si sta confondendo al posto sbagliato,

refere @@fetch_status

qui, 0 = The FETCH statement was successful.

Così fino a quando il recupero è successo il ciclo andrà on, e se si verifica un errore @@, termina con tutte le righe del cursore creato. E se non si sono verificati errori

si è logicamente a destra, e funzionerà bene se il CursorSale è definito correttamente, al posto giusto ..

Per ulteriori Idea riferimento esempio dal link qui sopra

spero questo lo farà ..

+0

Sulla stored procedure c'è una riga che aggiornerà "n" a "y" quindi se mai incontrerà degli errori durante il processo lo aggiornerà ancora su "y". Correggimi se sbaglio. – Androidz

0

Suggerirei invece di eseguire il ciclo di ogni record, facendo l'inserto in blocchi. Come 5000 record alla volta, se si tratta di un lavoro automatico, questo sarebbe fattibile.

Ecco un collegamento che ho trovato, ma in pratica si farebbero i primi 5000 ogni volta nel ciclo in alto. Immagino che se hai un lavoro con un cursore su ogni record per un inserto, è probabile che diventi molto costoso.

http://social.msdn.microsoft.com/Forums/sqlserver/en-US/1b3dbf8d-252f-43c4-80d6-d5724fe912b4/how-to-insert-rows-in-chunk-size