Hello guys..I am new in this forum and am working in sql 2005 for 7-8 months now.My current activity consisted of working on a requirement where 1. Data import from excel file to production DB.2. The xls file will pass through 4 validation processes (DataFormat check, Validity check,RequiredFields check and permissions check ) once uploaded.3. Once all the validation are done, valid records should be imported to production.We had written an windows service to run on the backgroud. the user will upload the .xls file from the application and the status will be uploaded in a uploadstatus table.once past the integrity checks, all the data is bulk copied to a temp table which has all the columns as nvarchar type.after this the stored procedure is called for the further processing.The whole issue about the thing is that , the import was to be generic and the target table schema was to be taken from a schema config .xml file.its something like
<CONFIGURATION> <INFORMATION_SCHEMA.TABLE IMPORT_TYPE="Person" WORKSHEET_NAME="WorkSheetName1" HEADING_ROW="1"> <INFORMATION_SCHEMA.COLUMNS REQUIRED="TRUE" EXCEL_COLUMN_NAME="UploadID" COLUMN_NAME="UploadID" DATA_TYPE="uniqueidentifier" IS_NULLABLE="FALSE" VISIBLE="FALSE" /> <INFORMATION_SCHEMA.COLUMNS REQUIRED="TRUE" EXCEL_COLUMN_NAME="" COLUMN_NAME="ID" DATA_TYPE="uniqueidentifier" IS_NULLABLE="FALSE" VISIBLE="FALSE" /> <INFORMATION_SCHEMA.COLUMNS REQUIRED="FALSE" EXCEL_COLUMN_NAME="PersonID" COLUMN_NAME="PersonID" DATA_TYPE="uniqueidentifier" IS_NULLABLE="TRUE" VISIBLE="FALSE" /> <INFORMATION_SCHEMA.COLUMNS REQUIRED="TRUE" EXCEL_COLUMN_NAME="PersonAge" COLUMN_NAME="PersonAge" DATA_TYPE="decimal" IS_NULLABLE="FALSE" NUMERIC_PRECISION="18" NUMERIC_SCALE="3" VISIBLE="TRUE" /> < <INFORMATION_SCHEMA.COLUMNS REQUIRED="FALSE" EXCEL_COLUMN_NAME="PersonName" COLUMN_NAME="PersonName" DATA_TYPE="nvarchar" CHARACTER_MAXIMUM_LENGTH="200" IS_NULLABLE="TRUE" VISIBLE="TRUE" /> <INFORMATION_SCHEMA.COLUMNS REQUIRED="FALSE" EXCEL_COLUMN_NAME="Comments" COLUMN_NAME="Comments" DATA_TYPE="nvarchar" CHARACTER_MAXIMUM_LENGTH="200" IS_NULLABLE="TRUE" VISIBLE="TRUE" /> </INFORMATION_SCHEMA.TABLE> </CONFIGURATION>
now this xml will be processed by the sp ,
DECLARE @config xmlDECLARE @idoc int EXEC sp_xml_preparedocument @idoc OUTPUT, @config INSERT INTO TempImportSchema (ColumnName,DataType,MaxLength,Nullable,NumericPrecision,NumericScale,DateTimePrecision,CondensedType,Visible,Required,ExcelColumnName) SELECT ColumnName,DataType,MaxLength,Nullable,NumericPrecision,NumericScale,DateTimePrecision, (CASE DataType WHEN 'uniqueidentifier' THEN 'uniqueidentifier' WHEN 'decimal' THEN 'decimal(' + CAST(NumericPrecision AS nvarchar) + ',' + CAST(NumericScale AS nvarchar) + ')' WHEN 'nvarchar' THEN 'nvarchar(' + CAST(MaxLength AS nvarchar) + ')' WHEN 'varchar' THEN 'varchar('+ CAST(MaxLength AS varchar) + ')' WHEN 'datetime' THEN 'datetime' ELSE DataType END),Visible,Required,ExcelColumnName FROM OPENXML (@idoc, '/CONFIGURATION/INFORMATION_SCHEMA.TABLE/INFORMATION_SCHEMA.COLUMNS',0) WITH (ImportType nvarchar(20) '../@IMPORT_TYPE', ColumnName nvarchar(20) '@COLUMN_NAME', DataType nvarchar(20) '@DATA_TYPE', MaxLength nvarchar(10) '@CHARACTER_MAXIMUM_LENGTH', Nullable nvarchar(5) '@IS_NULLABLE', NumericPrecision nvarchar(5) '@NUMERIC_PRECISION', NumericScale nvarchar(5) '@NUMERIC_SCALE', DateTimePrecision nvarchar(5) '@DATETIME_PRECISION', Visible nvarchar(20) '@VISIBLE', Required nvarchar(20) '@REQUIRED', ExcelColumnName nvarchar(20) '@EXCEL_COLUMN_NAME') WHERE ImportType like @UploadType;
so the above code will convert the xml schema into a table.we have a temp table, say table1 which will contain raw data from excel file. then another table, table2 with similar columns , but the actual data type.the stored procedure will take all the records from table1 and push it to the table2, performing the type validations.the further 3 types of validations(validity,completeness and permission) are further performed on the table2.once all checks are done, valid data from table2 is moved to production table , tableProduction.the sp is something like
/* For each operationn, i am trying to create dynameic SQL and am executing it in the main sp.*/ SET @ImportOperationCursor = dbo.[Generate_ImportOperationCursor_DataFormat_SQL]( @UploadID , @UploadType , @UploadTableName , @TempUploadTableName ) EXEC sp_executesql @ImportOperationCursor --Proceed for those rows only whose CanImport = true; SET @ImportOperationCursor = dbo.[Generate_ImportOperationCursor_InvalidRecords_SQL]( @UploadID , @UploadType , @UploadTableName , @TempUploadTableName ) EXEC sp_executesql @ImportOperationCursor select @ImportOperationCursor as Invalid --Proceed for those rows only whose CanImport = true; SET @ImportOperationCursor = dbo.[Generate_ImportOperationCursor_DataPermission_SQL]( @UploadID , @UploadType , @UploadTableName , @TempUploadTableName ) EXEC sp_executesql @ImportOperationCursor --Proceed for those rows only whose CanImport = true; -- SET @ImportOperationCursor = dbo.[Generate_ImportOperationCursor_IncompleteRecords_SQL]( @UploadID , @UploadType , @UploadTableName , @TempUploadTableName ) EXEC sp_executesql @ImportOperationCursor --update the status in Upload SET @UpdateImportStatus_SQL = N' IF EXISTS (SELECT * FROM ' + @UploadTableName + N' Where CanImport = 0 AND UploadID = @UploadID) UPDATE Upload SET StatusID = 4 WHERE UploadID = @UploadID ELSE UPDATE Upload SET StatusID = 7 WHERE UploadID = @UploadID ' EXEC sp_executesql @UpdateImportStatus_SQL,N'@UploadID UniqueIdentifier', @UploadID /* some code missing for clarity.. if all checks are passed, then...*/ BEGIN TRANSACTION --if status is pass, import into production. /* If all the records of the Current Import passes validations , then move the records to the production table as ADD/EDIT */ IF (@UploadIntoProduction = 1 AND @StatusID = 7) EXEC dbo.[ImportIntoProduction] @UploadID , @UploadType , @UploadTableName , @TempUploadTableName
I have made different scalar-values functions for generatino the SQL which i have to execute on the fly based on the schema.eg. [Generate_ImportOperationCursor_DataFormat_SQL]
set ANSI_NULLS ONset QUOTED_IDENTIFIER ONgoALTER Function [dbo].[Generate_ImportOperationCursor_DataFormat_SQL]( @UploadID uniqueidentifier, @UploadType nvarchar(20), @UploadTableName varchar(30), @TempUploadTableName varchar(30))Returns nvarchar(max)AS BEGINDECLARE @SQL nvarchar(max), @ImportOperationCursor_DeclareVariables_SQL nvarchar(max), @ImportOperationCursor_SelectPlaceholders_SQL nvarchar(max) , @ImportOperationCursor_SelectPlaceholders_WhereClause_SQL nvarchar(max) , @ImportOperationCursor_BeforeCellOperationCode_SQL nvarchar(max), @ImportOperationCursor_CellOperationCode_SQL nvarchar(max) , @ImportOperationCursor_AfterCellOperationCode_SQL nvarchar(max) , @ImportOperationCursor_Insert_SQL nvarchar(max) , @ImportOperationCursor_BeforeNextLoop_SQL nvarchar(max) , @ImportOperationCursor_InvalidDataFormatColumns_SQL nvarchar(max), @ColumnName nvarchar(20), @DataType nvarchar(20), @CondensedType nvarchar(20), @MaxLength nvarchar(10), @NumericPrecision nvarchar(5), @NumericScale nvarchar(5)--loop through the #TempImportSchema and make dynamic 'DECLARE' and format validation code.DECLARE GenerateDynamicValidationSQL CURSORFOR SELECT ColumnName,DataType,CondensedType,MaxLength,NumericPrecision,NumericScale FROM TempImportSchema OPEN GenerateDynamicValidationSQLFETCH NEXT FROM GenerateDynamicValidationSQL INTO @ColumnName, @DataType, @CondensedType,@MaxLength,@NumericPrecision,@NumericScaleWHILE @@FETCH_STATUS = 0 BEGIN --make dynamic 'DECLARE'. SET @ImportOperationCursor_DeclareVariables_SQL = ISNULL(@ImportOperationCursor_DeclareVariables_SQL,'') + '; DECLARE @' + ISNULL(@ColumnName,'') + ' ' + 'nvarchar(255) ' + '; DECLARE @_' + ISNULL(@ColumnName,'') + ' ' + ISNULL(@CondensedType,'') --SELECT columns comma seperated for cursor SET @ImportOperationCursor_SelectPlaceholders_SQL = ISNULL(@ImportOperationCursor_SelectPlaceholders_SQL,'') + ', @' + ISNULL(@ColumnName,'') --Call Method to generate sql for data format validation code. SET @ImportOperationCursor_CellOperationCode_SQL = ISNULL(@ImportOperationCursor_CellOperationCode_SQL,'') + dbo.[Generate_ImportOperation_DataFormat_CellValidation_SQL](ISNULL(@DataType,''),ISNULL(@MaxLength,''),ISNULL(@ColumnName,''),ISNULL(@CondensedType,''),ISNULL(@NumericScale,''),ISNULL(@NumericPrecision,''),ISNULL(@UploadType,'')) FETCH NEXT FROM GenerateDynamicValidationSQL INTO @ColumnName, @DataType, @CondensedType,@MaxLength,@NumericPrecision,@NumericScale ENDCLOSE GenerateDynamicValidationSQLDEALLOCATE GenerateDynamicValidationSQL -- remove the starting comma SET @ImportOperationCursor_DeclareVariables_SQL = Substring(@ImportOperationCursor_DeclareVariables_SQL,2,len(@ImportOperationCursor_DeclareVariables_SQL)) -- remove the extra semicolans and commas in front.--declare and set additional flag variables SET @ImportOperationCursor_DeclareVariables_SQL = ISNULL(@ImportOperationCursor_DeclareVariables_SQL,'')+ ';DECLARE @DFCanImport bit,@ImportOperationCursor_InvalidDataFormatColumns_SQL nvarchar(max); SET @DFCanImport=1 ;SET @ImportOperationCursor_InvalidDataFormatColumns_SQL = '''';' -- remove the starting comma SET @ImportOperationCursor_SelectPlaceholders_SQL = Substring(@ImportOperationCursor_SelectPlaceholders_SQL,2,len(@ImportOperationCursor_SelectPlaceholders_SQL))-- WHERE clause for records of the current upload request.SET @ImportOperationCursor_SelectPlaceholders_WhereClause_SQL = ' WHERE Ltrim(Rtrim(UploadID)) LIKE ''' + CAST(@UploadID AS nvarchar(40)) + ''' '/* To do data format and transfer valid data from [Temp_UploadData] --> [Upload_Data] table*/SET @ImportOperationCursor_Insert_SQL=' INSERT INTO ' + @UploadTableName + ' ('SET @ImportOperationCursor_Insert_SQL = ISNULL(@ImportOperationCursor_Insert_SQL,'') + Replace(@ImportOperationCursor_SelectPlaceholders_SQL,'@','') + ', InvalidDataFormatColumns, CanImport) VALUES(' + Replace(@ImportOperationCursor_SelectPlaceholders_SQL,'@','@_') + ',@ImportOperationCursor_InvalidDataFormatColumns_SQL,@DFCanImport) ' -- the insert code into the target table like Upload_Position--reset the flags for the next record from the temp_upload table.SET @ImportOperationCursor_BeforeNextLoop_SQL = ' SET @ImportOperationCursor_InvalidDataFormatColumns_SQL = ''''; SET @DFCanImport = 1; ' -- construct the dynamic sql which will contain the cursor which loops on the temp upload table.SET @SQL = ISNULL(@SQL,'')+ ISNULL(@ImportOperationCursor_DeclareVariables_SQL,'')+ N' DECLARE DFCursor CURSOR FOR SELECT '+ Replace(ISNULL(@ImportOperationCursor_SelectPlaceholders_SQL,''),'@','') + ' FROM ' + @TempUploadTableName+ ISNULL(@ImportOperationCursor_SelectPlaceholders_WhereClause_SQL,'')+ ' OPEN DFCursor '+ ' FETCH NEXT FROM DFCursor INTO '+ ISNULL(@ImportOperationCursor_SelectPlaceholders_SQL,'')+ ' WHILE @@FETCH_STATUS = 0 '+ ' BEGIN '+ ISNULL(@ImportOperationCursor_BeforeCellOperationCode_SQL,'')+ ISNULL(@ImportOperationCursor_CellOperationCode_SQL,'')+ ISNULL(@ImportOperationCursor_AfterCellOperationCode_SQL,'')+ ISNULL(@ImportOperationCursor_Insert_SQL,'')+ ' FETCH NEXT FROM DFCursor INTO '+ ISNULL(@ImportOperationCursor_SelectPlaceholders_SQL,'')+ ISNULL(@ImportOperationCursor_BeforeNextLoop_SQL,'')+ ' END '+ ' CLOSE DFCursor '+ ' DEALLOCATE DFCursor 'return @SQLENDset ANSI_NULLS ONset QUOTED_IDENTIFIER ONgoALTER Function [dbo].[Generate_ImportOperation_DataFormat_CellValidation_SQL]( @DataType nvarchar(20), @MaxLength nvarchar(10), @ColumnName nvarchar(20), @CondensedType nvarchar(20), @NumericScale nvarchar(5), @NumericPrecision nvarchar(5), @UploadType nvarchar(20))Returns nvarchar(max)AS BEGINDECLARE @SQL nvarchar(max)/* To verify if the datatype of the record is compatible with tht of its datatype in the Production table*//* To verify if the datalength of the record is within its length specified in the Production table*/--update the flags of database, decalred in the calling function [Generate_ImportOperationCursor_DataFormat_SQL] based on the cell validation. if(@UploadType = 'Position') BEGIN SET @SQL = N' BEGIN TRY SET @_' + @ColumnName + ' = null ' + ' IF(@' + @ColumnName + ' IS NOT NULL) ' + ' IF(Ltrim(Rtrim(@' + @ColumnName + ')) <> '''') ' + N' BEGIN IF(''' + @DataType + ''' LIKE ''varchar'' OR ''' + @DataType + ''' LIKE ''nvarchar'' OR ''' + @DataType + ''' LIKE ''decimal'') IF(dbo.CheckMaxLength(''' + @DataType + ''',''' + @MaxLength + ''',''' + @NumericPrecision + ''',''' + @NumericScale + ''',@' + @ColumnName + N')<>1) RAISERROR(''MaxLength check failed'',1,1); SET @_' + @ColumnName + ' = CAST(Ltrim(Rtrim(@' + @ColumnName + ')) AS ' + @CondensedType + '); END END TRY BEGIN CATCH SET @DFCanImport = 0; SET @ImportOperationCursor_InvalidDataFormatColumns_SQL = @ImportOperationCursor_InvalidDataFormatColumns_SQL + ''~'' + ''' + @ColumnName + N'''; END CATCH ' ENDreturn @SQLEND
So the basic idea was to create sql containing cursors on the fly, based on the number and type of columns and executing it. functin [Generate_ImportOperation_DataFormat_CellValidation_SQL] generates sql which will try to convert nvarchar to the target type and if any errors, it will update the correspondig flags.I had developed this in a short time and had no time for extended reviews. i have a couple of question for the talented members of this forum.1. how is this approach. did u guys find any major issues? 2. Can we have any other approaches for this kind of requirement. 3. any other valuable comments. Let me know if u guys have any trobule in code.Some best things in life can't be bought by money.