Quantcast
Channel: TechNet Blogs
Viewing all articles
Browse latest Browse all 34890

Insert dummy data in tables with relationship

$
0
0

Torno spesso sull’argomento relativo ai test di carico / performance dei database e sull’importanza di poter profilare l’utilizzo che avranno con una mole di dati quantomeno simile allo scenario di produzione.

Diventa importante, quindi, avere un qualsivoglia meccanismo di generazione automatica di dati in modo tale da demandare ad un processo automatico il popolamento delle nostre tabelle.

Ho già parlato qui di una possibile stored procedure che consenta di inserire dati “finti” sulla base del tipo di dati con cui è modellata ciascuna colonna.

 

Il problema, di questa implementazione, è l’impossibilità di inserire valori su colonne che dipendono da valori di una tabella padre.

Prendiamo, ad esempio, due ipotetiche tabelle che consentono di memorizzare l’intestazione e le righe di fatture.

Qualcosa come:

image

 

La necessità, sulla tabella “figlia”, è quella di inserire, nelle colonne di foreign key, soltanto valori che siano presenti nelle colonne primary key della tabella “padre”.

Effettuo il setup delle tabelle come mostrate in figura:

/* start setup database for test */USE demo;GO

if object_id('dbo.invoiceRow') is not nullbegin
  drop table dbo.invoiceRowend
GO

if object_id('dbo.invoiceHeader') is not nullbegin
  drop table dbo.invoiceHeaderend
GO


create table invoiceHeader(idInvoiceHeader smallint identity(-32000,1),idInvoiceHeader2 smallint not null,invoiceDate date,idCustomer int)alter table invoiceHeader add constraint PKInvoiceHeader primary key (idInvoiceHeader, idInvoiceHeader2)GO

create table invoiceRow(idInvoiceRow smallint primary key identity(-32000,1),idInvoiceHeader smallint, idInvoiceHeader2 smallint,idProduct int,qty smallint,price money)alter table invoiceRow add CONSTRAINT FKInvoiceHeader FOREIGN KEY (idInvoiceHeader, idInvoiceHeader2) REFERENCES InvoiceHeader (idInvoiceHeader, idInvoiceHeader2)GO/* end setup database for test */

 

Di seguito, invece, una possibile implementazione della procedura in grado di inserire valori in entrambe le tabelle:

IF EXISTS (SELECT * FROM sys.objects WHERE type = 'P' AND name = 'up_insertDummyDataWithFK')DROP PROCEDURE dbo.up_insertDummyDataWithFKGO

CREATE PROCEDURE dbo.up_insertDummyDataWithFK(@schemaName nvarchar( 250 ) , @tableName nvarchar( 250 ) , @numberOfRows int )AS
BEGIN
    SET NOCOUNT ON;DECLARE @i int = 0, @tsql varchar( max ) = '';WHILE (@i < @numberOfRows)BEGIN/* check if exists relationship in the table */IF EXISTS (SELECT TOP 1 1 FROM sys.foreign_keys WHERE parent_object_id = OBJECT_ID( @tableName ))BEGIN
             declare @TSqlFromForeignKeyValues nvarchar(max)declare @nrFK tinyint
             declare @x tinyint = 0declare @PrimaryKeyValues table (columnName varchar(100), value nvarchar(100), tsqlStatement nvarchar(max))/* retrieve primary keys from parent table */INSERT @PrimaryKeyValues (columnName, tsqlStatement) SELECT DISTINCT CU.COLUMN_NAME, 'SELECT TOP 1 @v = ' + CU.COLUMN_NAME + ' FROM ' + PK.TABLE_NAME + ' ORDER BY NEWID()' as tSQL
               FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS C INNER JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS FK ON C.CONSTRAINT_NAME = FK.CONSTRAINT_NAMEINNER JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS PK ON C.UNIQUE_CONSTRAINT_NAME = PK.CONSTRAINT_NAMEINNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE CU ON C.CONSTRAINT_NAME = CU.CONSTRAINT_NAMEINNER JOIN
                ( SELECT i1.TABLE_NAME , i2.COLUMN_NAMEFROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS i1 INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE i2 ON i1.CONSTRAINT_NAME = i2.CONSTRAINT_NAMEWHERE i1.CONSTRAINT_TYPE = 'PRIMARY KEY' ) PT ON PT.TABLE_NAME = PK.TABLE_NAMEWHERE FK.TABLE_NAME = @tableName AND FK.TABLE_SCHEMA = @schemaName;set @nrFK = (select @@rowcount)--> select @nrFK as numberOfForeignKeysdeclare @v nvarchar(100)declare @cName varchar(100) = ''/* for each primary key retrieve a valid value from parent table */while (@x < @nrFK)begin
                    set @TSqlFromForeignKeyValues = 
                          (select top 1 [@PrimaryKeyValues].tSQLStatement from @PrimaryKeyValues where [@PrimaryKeyValues].[value] is null order by [@PrimaryKeyValues].tsqlStatement)if (@x > 0)begin
                       declare @tsqlModified nvarchar(max)set @tsqlModified = replace(@TSqlFromForeignKeyValues,'ORDER BY NEWID()',' WHERE ' + @cName + ' = ' + @v + ' ORDER BY NEWID()')exec sp_executesql @tsqlModified, N'@v nvarchar(100) out', @v out   
                    end
                    else
                    begin
                       exec sp_executesql @TSqlFromForeignKeyValues, N'@v nvarchar(100) out', @v out
                    end

                    set @cName = (select top 1 [@PrimaryKeyValues].columnName from @PrimaryKeyValues where [@PrimaryKeyValues].[value] is null order by [@PrimaryKeyValues].tsqlStatement)update @PrimaryKeyValues set [@PrimaryKeyValues].[value] = @v where [@PrimaryKeyValues].tsqlStatement = @TSqlFromForeignKeyValuesset @x += 1end
          END

          declare @FKColumnName nvarchar(max) = ''declare @FKValues nvarchar(max) = ''if exists(select top 1 1 from @PrimaryKeyValues)begin
             select @FKColumnName = @FKColumnName + [@PrimaryKeyValues].columnName + ', ' , @FKValues = @FKValues + [@PrimaryKeyValues].[value] + ', ' from @PrimaryKeyValuesset @FKColumnName = left(@FKColumnName, len(@FKColumnName)-1)set @FKColumnName = ', ' + @FKColumnNameset @FKValues = left(@FKValues, len(@FKValues)-1)set @FKValues = ', ' + @FKValuesend

          delete from @PrimaryKeyValues/* retrieve column list without foreign keys */declare @columnListWhitoutFK nvarchar(max)set @columnListWhitoutFK = ''SELECT @columnListWhitoutFK = @columnListWhitoutFK + T.columnName + ', ' FROM(SELECT C.name as columnNameFROM sys.columns CJOIN sys.tables T on t.OBJECT_ID = c.OBJECT_IDWHERE T.name = @tableName AND T.schema_id = schema_id(@schemaName) ANDC.is_identity = 0EXCEPT
          SELECT COL_NAME(fc.parent_object_id, fc.parent_column_id) AS FKColumnNameFROM sys.foreign_keys AS fINNER JOIN sys.foreign_key_columns AS fc ON f.OBJECT_ID = fc.constraint_object_idwhere OBJECT_NAME(f.parent_object_id) = @tableName and f.schema_id = schema_id(@schemaName)
       ) Tset @columnListWhitoutFK = left(@columnListWhitoutFK, len(@columnListWhitoutFK)-1)/* generate random value for each column (no foreign keys) */SELECT @tsql = @tsql + CASE
                                 WHEN c.column_id = 1 THEN 'INSERT ' + @schemaName + '.[' + t.name + '] ' + '(' + @columnListWhitoutFK + @FKColumnName + ') VALUES ( 'ELSE ''END + CASE
                          WHEN c.is_identity = 0 THEN CASE
                            WHEN y.name IN ( 'bit' , 'bigint' , 'int' , 'smallint' , 'tinyint' , 'float' , 'decimal' , 'numeric' , 'money' , 'smallmoney' , 'real' )THEN SUBSTRING( CAST(1000*RAND() AS VARCHAR(50)) , 1 , c.max_length)WHEN y.name IN ( 'binary' , 'varbinary' ) THEN SUBSTRING( '0x546869732069732044756D6D792044617461' , 1, c.max_length )WHEN y.name IN ( 'varchar' , 'char' , 'text', 'nchar' , 'nvarchar' , 'ntext' ) THEN '''' + SUBSTRING( 'some data some data some data' , 1 , c.max_length /2 ) + ''''WHEN y.name IN ( 'date' , 'time' , 'datetime' , 'datetime2' , 'smalldatetime' , 'datetimeoffset' ) THEN '''' + CONVERT( varchar( 25 ) , GETDATE( ) , 121 ) + ''''WHEN y.name IN ( 'uniqueidentifier' ) THEN '''' + CAST( NEWID( ) AS varchar( 36 )) + ''''ELSE '' END + CASE
                                WHEN c.column_id = (SELECT MAX( column_id ) FROM sys.columns WHERE OBJECT_ID = c.OBJECT_ID )THEN @FKValues + ');'ELSE ','END
                              ELSE ''END 
        FROM sys.tables AS t INNER JOIN sys.schemas s ON t.schema_id = s.schema_idINNER JOIN sys.columns c ON t.OBJECT_ID = c.OBJECT_IDINNER JOIN sys.types AS y ON c.user_type_id = y.user_type_idWHERE t.name = @tableName AND s.name = @schemaName ANDc.name IN
          (SELECT COLUMN_NAME as columnNameFROM INFORMATION_SCHEMA.COLUMNSWHERE TABLE_NAME = @tableName AND TABLE_SCHEMA = @schemaNameEXCEPT
             SELECT COL_NAME( fc.parent_object_id , fc.parent_column_id )AS FKColumnNameFROM sys.foreign_keys AS f INNER JOIN sys.foreign_key_columns AS fc ON f.OBJECT_ID = fc.constraint_object_idWHERE OBJECT_NAME( f.parent_object_id ) = @tableName AND f.schema_id = SCHEMA_ID( @schemaName )
          )SET @i+=1;/* insert the values */
        --> SELECT @tsqlEXEC ( @tsql );SET @tsql = '';END
END;GO

Un esempio di caricamento dati (attenzione: naturalmente va popolata prima la tabella “padre”):

/* test the procedure (before the parent, next the child table) */EXEC dbo.up_insertDummyDataWithFK @schemaName = 'dbo', @tableName = 'invoiceHeader' , @numberOfRows = 10;EXEC dbo.up_insertDummyDataWithFK @schemaName = 'dbo', @tableName = 'invoiceRow' , @numberOfRows = 50;GO

Vedo qualche dato:

/* select the dummy data */select count(*) as nrHeaders, (select count(*) from invoiceRow) as nrRows from invoiceHeaderselect top 2 * from invoiceHeader select top 2 * from invoiceRow GO

image


Viewing all articles
Browse latest Browse all 34890

Trending Articles