Archivo de la etiqueta: Developer Tool

Tracking data changes


Sooner or later, it happens. Someone asks “When this data has been modified? Who changed this information?”

A reliable system must ensure this information could be obtained any time.

In this post I offer you a simple way to add columns to any table you want to preserve the metadata about the last changes.

In another post, I’ll talk about tracking all the changes.

Like in the last posts, I prepare it as stored procedure, which receives two parameters:

  • The Table Name
  • The Schema Name (optional) in which case, “dbo” is assumed

The procedure will add the following columns:

Column  NameInformation StoredDefault Value
Created byusername in the insert actionCURRENT_USER
Modified Byusername in the update action 
Creation Datedate & time of the creationSYSUTCDATETIME
Modification Datedate & time of the update 
Note: An “Active” column is added as well, to manage “soft deletes” in case you need it.

Notice there is no default value for update actions, since they are not supported by the database. The values must be provided by application procedures. In fact, they could be provided for the creation process as well, if desired.

Some code highlights

Using a select from INFORMATION_SCHEMA.COLUMNS the procedure detects if the column to add already exists.

DECLARE 
   @Isthere INT;
DECLARE 
   @CheckForColumn NVARCHAR(300) =
   'select @Isthere =count(*) from INFORMATION_SCHEMA.COLUMNS T where TABLE_SCHEMA=@schema and TABLE_NAME=@TableName and T.COLUMN_NAME=@ColumnName'

Like in other examples I already post, using sp_executesql stored procedure, can execute the T-SQL sentence passing parameters.

DECLARE 
   @ColumnName SYSNAME = 'Created by';
EXEC [sp_executesql] 
       @CheckForColumn
 ,
 N'@Schema sysname,@TableName sysname,@ColumnName sysname,@Isthere int OUTPUT'
 , @schema
 , @TableName
 , @ColumnName
 , @Isthere OUTPUT;

If the column doesn’t exist, two other dynamic T-SQL sentences are executed, adding the column and later configuring the default value.

DECLARE 
   @SqlToExecute NVARCHAR(MAX);
IF @Isthere = 0
  BEGIN
    SET @SqlToExecute =
                        'ALTER TABLE ['
                        + @schema
                        + '].['
                        + @TableName
                        + '] ADD ['
                        + @columnName
                        + '] nvarchar(150) NULL';
    EXEC [sp_executesql] 
           @SqlToExecute;
    SET @SqlToExecute =
                        'ALTER TABLE ['
                        + @schema
                        + '].['
                        + @TableName
                        + '] ADD CONSTRAINT [DF_'
                        + @TableName
                        + '_'
                        + @columnName
                        + '] DEFAULT CURRENT_USER FOR ['
                        + @columnName
                        + ']';
    EXEC [sp_executesql] 
           @SqlToExecute;
  END;

The same steps are repeated for the other columns.

In similar way with other previous posts, this stored procedure could be executed over all the tables in a database, using a select from INFORMATION_SCHEMA.TABLES.

You can find the procedure in my repository here.

HTH

T-sql SP Builder


A Developer tool

When things are complex, and it is preferable to not use any ORM like EF, or the database needs to be secured, probably you prefer to use SQL Server Stored procedures.

However, it happens that in those cases, the database uses to have hundreds of tables, and it takes a lot of time to write the standard procedures for CRUD operations for them. This code helps you to build the Insert, Update and Delete procedure for each table. In all cases, the task is performed in just one row, using the Primary key to identify it for update and delete.

How it works.

By getting information from the INFORMATION_SCHEMA view, and using some special system tables like sys.columns and sys.computed_columns, the code retrieves the information for the required table.

The information is stored in a table variable which contains columns to make easier to build the different parts of the sentence, like adding parenthesis, default values, etc. which contains data as you can see in the next image

Table variable columns

Then, several queries against the table variable constructs the parameters, sentence, and where segments of the different procedures.

In this case, I will not explain each part.  Most of it use STRING_AGG to concatenate the different parts.

However, some comments about the different “issues” I found making this:

  • The parameters for the Insert procedures, include an OUTPUT statement, in case a column has identity specification to get the value for the inserted row. To do so, the procedure uses IDENT_CURRENT function. But the parameters for Update and Delete do not need the OUTPUT.
  • The computed columns must not be inserted or updated, so must be excluded of the procedure. This happens in case a column is of timestamp datatype as well.
  • Just to be sure what the script generates, the results are printed so you can review them.

You can download this script here.

The script as Stored Procedure.

If you want to use this several times during your development, you can use this second script, which add this as a Stored Procedure, so you can call it just passing the table name, and, when needed, the schema name.

Moreover, it accepts a third parameter, to perform the procedures’ creation directly.

Finally, if you want to use the procedure for all the tables of a database at once, just use this sentence for creating a complete script.

SELECT     
   'EXECUTE @RC = [Development].[SPBuilder]    @TableName'
   + QUOTENAME([table_schema])
   + ',@SchemaName ='
   + QUOTENAME([table_name])
   + ' ,@Create=1'
FROM     
   [INFORMATION_SCHEMA].[TABLES]
WHERE
       [table_type]
       = 'BASE TABLE'
       AND [table_name] NOT LIKE 'sys%';