jump to navigation

Una stored procedure per la duplicazione delle righe di una tabella generica 25 Aprile 2008

Posted by gianfrasoft in Transact SQL.
Tags: , , , , , ,
trackback

Duplicare la riga di una tabella è una operazione sempre possibile ma spesso non immediata: la tabella in questione può essere caratterizzata dalla presenza di una chiave univoca che coinvolga uno o più campi, da campi NOT NULL che vanno valorizzati talora con i propri valori di default, ecc…

Lo script INSERT che effettua l’inserimento di una riga duplicata deve, nella maggior parte dei casi, specificare la lista dei campi da caricare nel nuovo record e questo perché lo script non deve definire valori duplicati per i campi chiave, se richiesto non deve sovrascrivere i valori di default, non deve definire valori per i campi auto-incrementali e quant’altro.

Consideriamo il caso, piuttosto diffuso, di tabelle caratterizzate da una chiave primaria composta da un campo auto-incrementale, e realizziamo una procedura che:

  • sia in grado di semplificare l’operazione di duplicazione della riga di una tabella, agendo indipendentemente dalla struttura della stessa;
  • restituisca l’indice univoco del campo chiave del nuovo record;
  • non valorizzi i campi che hanno valori di default;
  • agisca su una qualsiasi tabella dove sia presente una chiave primaria composta da un campo auto-incrementale.

Il cuore della procedura è nella generazione al volo di uno script di insert completo della lista esplicita di tutti i campi della tabella su cui andiamo a duplicare il record. La lista dei campi viene recuperata mediante la view di sistema INFORMATION_SCHEMA mentre una chiamata alla stored procedure di sistema sp_executesql permetterà di eseguire lo script INSERT appena creato.

La procedura in questione esporrà, pertanto, tra i parametri:

  1. il nome della tabella della quale s’intende duplicare una riga,
  2. il nome del campo chiave,
  3. l’id univoco della riga che si vuole duplicare

e restituirà l’id del record duplicato generato. Ecco il codice commentato:

IF EXISTS (SELECT SPECIFIC_NAME FROM INFORMATION_SCHEMA.ROUTINES WHERE SPECIFIC_NAME = ‘ClonaRecord’)
 DROP PROCEDURE dbo.ClonaRecord
GO

CREATE PROCEDURE dbo.ClonaRecord(
  @Tabella varchar(90),
  @NomeCampoID varchar(90),
  @IDSorgente varchar(16), 
  @IDDestinazione varchar(16) OUTPUT
) AS
BEGIN
  DECLARE @ErrMsg nvarchar(4000), @ErrSeverity int
  IF @IDSorgente IS NULL
  BEGIN
    SET @IDDestinazione = ‘ERROR’
    
    — Verifica che il campo @NomeCampoID non sia nullo. Se lo è, solleva un’eccezione
    SELECT @ErrMsg = ‘Errore nei parametri - ‘ + @NomeCampoID + ‘ nullo.’,
          @ErrSeverity = 1
    RAISERROR(@ErrMsg, @ErrSeverity, 1)
    RETURN
  END
  
  DECLARE @i int
  DECLARE @Script nvarchar(max)
  
  — PROCESSAMENTO –
  BEGIN TRY
    — La procedura lavora con le transazioni
    BEGIN TRANSACTION
    
    DECLARE @IDValue varchar(100) 
    DECLARE @Fields nvarchar(max)
    DECLARE @Ordinal integer
    
    SET @Script = ”

    — La procedura inoltre accede alla view di sistema INFORMATION_SCHEMA
    — per recuperare la lista dei campi indispensabili per la generazione
    — dello script di INSERT
    SELECT @Ordinal = MIN(ORDINAL_POSITION) FROM 
      INFORMATION_SCHEMA.COLUMNS
      WHERE TABLE_NAME = @Tabella
    
    WHILE @Ordinal IS NOT NULL
    BEGIN
      SELECT @IDValue = COLUMN_NAME FROM 
        INFORMATION_SCHEMA.COLUMNS
        WHERE TABLE_NAME = @Tabella
        AND ORDINAL_POSITION = @Ordinal

      — Un ciclo sul recordset individuato nella view INFORMATION_SCHEMA
      — permette di popolare la stringa @Fields destinate a contenere la
      — lista dei campi 
      IF @IDValue <> @NomeCampoID
      BEGIN
        IF @Fields IS NULL
          SET @Fields = @IDValue
        ELSE
          SET @Fields = @Fields + ‘, ‘ + @IDValue
      END
      
      SELECT @Ordinal = MIN(ORDINAL_POSITION) FROM 
        INFORMATION_SCHEMA.COLUMNS
        WHERE TABLE_NAME = @Tabella
        AND ORDINAL_POSITION > @Ordinal
        AND COLUMN_DEFAULT IS NULL  — * NEW * –
    END

    — Ecco finalmente lo script INSERT che viene generato al volo nell’ambito
    — della stringa @Script di dimensioni nvarchar(max)
    SET @Script = ‘INSERT INTO ‘ + @Tabella + ‘ (’ + @Fields + ‘)’ +
      ‘ SELECT ‘ + @Fields + ‘ FROM ‘ + @Tabella + 
      ‘ WHERE ‘ + @NomeCampoID + ‘ = ”’ + @IDSorgente + ””
    
    — Lo script viene eseguito
    EXECUTE sp_executesql @Script

    — Nella variabile @IDDestinazione viene conservato l’IDENTITY del nuovo
    — record
    SET @IDDestinazione = @@IDENTITY
    
    COMMIT TRANSACTION
  END TRY
  BEGIN CATCH
    IF @@TRANCOUNT > 0
      ROLLBACK
    
    SET @IDDestinazione = ‘ERROR’
    
    — Se @IDDestinazione non è stato popolato, solleva un’eccezione
    SELECT @ErrMsg = ERROR_MESSAGE(),
      @ErrSeverity = ERROR_SEVERITY()
    RAISERROR(@ErrMsg, @ErrSeverity, 1)
  END CATCH  
END
GO

Un esempio di chiamata alla procedura appena descritta è riportata di seguito:

DECLARE @IDDestinazione varchar(16);
EXECUTE dbo.DuplicateRecord 'test', 'id', '6', @IDDestinazione OUTPUT
PRINT 'Nuovo id: ' + @IDDestinazione

dove alla chiamata segue la stampa nello standard output di SQL Server dell’id univoco del record appena generato.

Commenti»

No comments yet — be the first.