<#@ import namespace="System.Data" #> <#@ import namespace="System.Data.SqlClient" #> <#@ import namespace="System.IO" #> <# /* ========================================================================= */ /* === GENERATE EXTRACTOR PACKAGE === */ /* ========================================================================= */ /* Version: 1 */ /* Release Date: 2013-07-19 */ /* Authors: Luca Zavarella & Mauro Regoli (SolidQ.com) */ /* License: Creative Commons Attribution-ShareAlike 3.0 */ /* Italy License */ /* (http://creativecommons.org/licenses/by-sa/3.0/it/deed.en) */ /* ===-------------------------------------------------------------------=== */ /* === DESCRIPTION === */ /* ===-------------------------------------------------------------------=== */ /* This Biml script will generate a SSIS package that will extract */ /* data from a stored procedure and will persist them in CSV or */ /* EXCEL. */ /* SQL Server 2008 (R2) and SQL Server 2012 are supported. */ /* ===-------------------------------------------------------------------=== */ /* === LIMITATIONS === */ /* ===-------------------------------------------------------------------=== */ /* 1. If SQL Server 2012 or greater is NOT used, every stored */ /* procedure date parameter MUST be DATETIME due to a SSIS OLEDB */ /* Command bug (*); */ /* 2. DECIMAL and NUMERIC data types precision cannot exceed 28; */ /* 3. SMALLMONEY and SMALLDATETIME data types are not supported; */ /* 4. Temporary tables in stored procedures cause the failure of the */ /* Biml script because it uses FMTONLY ON in order to retrieve */ /* the stored procedure schema infos; in that case you can */ /* compile the Biml script: */ /* a. using a dummy stored procedure that has just a SELECT */ /* that returns a dummy resultset with the same schema of */ /* the original one; */ /* b. using Table Variables instead of temporary tables if you */ /* don't have performance problems */ /* 5. Excel files generated with the ACE provider have incorrect column */ /* data types due to a SSIS bug (**). All works fine using the */ /* Jet one */ /* ========================================================================= */ // (*) http://connect.microsoft.com/SQLServer/feedback/details/628743/ssis-oledb-command-date-datatype-in-stored-procedure-sqlcommand-yields-operand-error // (**) https://connect.microsoft.com/SQLServer/feedback/details/477970/excel-data-types-from-ssis /* ======================================================================= */ /* === INPUT PARAMETERS === */ /* ======================================================================= */ // // Stored procedure's SQL Server server name/instance and database name // string serverName = @"localhost\denali"; string databaseName = "AdventureWorks2012"; // // OLE DB Provider used by the OLEDB Source Component in order to execute the Stored Procedure // string oledbProvider = "SQLNCLI10.1"; // // Stored Procedure details: // - storedProcedureSchema: schema to which the Stored Procedure belongs; // - storedProcedureName: Stored Procedure name (eg. "stp_attestati_rischio"); // - storedProcedurePrefix: Stored Procedure name's prefix, if present (eg. "stp_"); // - storedProcedureParameters: comma separated list of parameters in the order expected by the Stored Procedure; // the token "GETDATE()" can be used if you want the package will replace it with the // date it'll be executed (useful if you want to schedule it in a job); an example of // provided parameters is: "GETDATE(), '00760', NULL, 3"; // string storedProcedureSchema = "dbo"; string storedProcedureName = "uspGetWhereUsedProductID"; string storedProcedurePrefix = "usp"; string storedProcedureParameters = "819, GETDATE()"; // // SSIS Package details: // - packagePrefix: the package name's prefix (the full name will be composed // by the package name's prefix and the Stored Procedure // name without its prefix; // - packageProtectionLevel: value of the package ProtectionLevel // - packageConnectionManagerName: name of the package's Connection Manager that points to // the Stored Procedure database; // string packagePrefix = "test_"; string packageProtectionLevel = "EncryptSensitiveWithUserKey"; string packageConnectionManagerName = "ADW"; // // Output file details: // - destinationFileType: output file format type (can be CSV or EXCEL) // - fileName: output file name (without extension) // - destinationFolder: output file destination folder // string destinationFileType = "CSV"; string fileName = "test_GetWhereUsedProductID"; string destinationFolder = @"C:\Users\LZavarella\Desktop"; // // CSV file details (used if destinationFileType = "CSV"): // - columnDelimiter: delimiter used to separate file columns // - rowDelimiter: delimiter used to separate file rows // string columnDelimiter = "Semicolon"; string rowDelimiter = "CRLF"; // // Excel file details (used if destinationFileType = "EXCEL") // - excelProvider: provider to use in order to create the Excel file; // the Jet one si used for Excel 97-2003; the ACE one for Excel 2007 // - excelExtendedProperties: extended properties to be used in the Excel connection string // string excelProvider = "Microsoft.Jet.OLEDB.4.0"; // e.g. for ACE: "Microsoft.ACE.OLEDB.12.0" string excelExtendedProperties = "Excel 8.0;HDR=YES;IMEX=0"; // e.g. for ACE: "Excel 12.0 XML;HDR=YES;" /* ======================================================================= */ /* === DO NOT DELETE/MODIFY ANYTHING UNDER THIS PLACEHOLDER!! === */ /* ======================================================================= */ string filePath = ""; string fileExtension = ""; int excelStringFormat = -1; string excelDecimalFormat = ""; int excelDecimalPrecision = -1; int excelDecimalScale = -1; string excelFieldTerminator = ","; string excelTableTerminator = ")"; string excelTempTerminator = ""; string excelColumnType = ""; string sourceConnectionString = ""; string connStrOLEDB = String.Format("Data Source={0};Initial Catalog={1};Provider={2};Integrated Security=SSPI;Auto Translate=False;", serverName, databaseName, oledbProvider); string connStr = String.Format("Data Source={0};Initial Catalog={1};Integrated Security=SSPI;", serverName, databaseName); if (destinationFileType == "CSV") { fileExtension = ".csv"; filePath = destinationFolder + fileName + fileExtension; } else if (destinationFileType == "EXCEL") { fileExtension = excelProvider.ToUpper().Contains("JET") ? ".xls" : ".xlsx"; filePath = destinationFolder + fileName + fileExtension; sourceConnectionString = String.Format("Provider={0};Data Source=\"{1}\";Extended Properties=\"{2}\"", excelProvider, filePath, excelExtendedProperties); } string packageName = packagePrefix + (storedProcedurePrefix == "" ? storedProcedureName : storedProcedureName.Replace(storedProcedurePrefix, "")); string executeStatement = storedProcedureSchema + "." + storedProcedurePrefix + (storedProcedurePrefix == "" ? storedProcedureName : storedProcedureName.Replace(storedProcedurePrefix, "")) + " " + storedProcedureParameters.Replace("GETDATE()", "'20130101'"); string storedProcedure = storedProcedureSchema + "." + storedProcedurePrefix + (storedProcedurePrefix == "" ? storedProcedureName : storedProcedureName.Replace(storedProcedurePrefix, "")); string storedProcedureMetadataQuery = "SET FMTONLY ON; EXEC " + executeStatement + "; SET FMTONLY OFF;"; string queryGetMajorVersion = "DECLARE @version nvarchar(128) = CAST(serverproperty('ProductVersion') AS nvarchar); " + "DECLARE @ver INT; " + "SET @ver = CAST(SUBSTRING(@version, 1, CHARINDEX('.', @version) - 1) AS INT); " + "SELECT major_version = @ver;"; using (SqlConnection sqlConnection = new SqlConnection(connStr)) { SqlCommand cmdMajorVer = new SqlCommand(queryGetMajorVersion, sqlConnection); SqlCommand cmdMetadata = new SqlCommand(storedProcedureMetadataQuery, sqlConnection); sqlConnection.Open(); int major_version = (int)cmdMajorVer.ExecuteScalar(); SqlDataReader reader = cmdMetadata.ExecuteReader(CommandBehavior.SchemaOnly); DataTable storedProcedureTable = reader.GetSchemaTable(); #> <Biml xmlns="http://schemas.varigence.com/biml.xsd"> <# if (destinationFileType == "CSV") { #> <FileFormats> <FlatFileFormat Name="CSVFlatFile" RowDelimiter="CRLF" HeaderRowDelimiter="CRLF" ColumnNamesInFirstDataRow="true" IsUnicode="1"> <Columns> <!--The RowDelimiter SSIS takes into account is the one of the last column.--> <# string tempDelimiter = ""; foreach (DataRow column in storedProcedureTable.Rows) { if (((int)column["ColumnOrdinal"] + 1) < storedProcedureTable.Rows.Count) tempDelimiter = columnDelimiter; else tempDelimiter = rowDelimiter; #> <Column Name="<#=column["ColumnName"].ToString()#>" DataType="String" Length="256" Delimiter="<#=tempDelimiter#>" /> <# } #> </Columns> </FlatFileFormat> </FileFormats> <# } #> <Connections> <OleDbConnection Name="<#=packageConnectionManagerName#>" ConnectionString="<#=connStrOLEDB#>"/> <# if (destinationFileType == "CSV") { #> <FlatFileConnection Name="CSV" FilePath="<#=filePath#>" FileFormat="CSVFlatFile"> <Expressions> <Expression PropertyName="ConnectionString">@[User::param_DestinationFolder] + @[User::param_FileName] + "<#=fileExtension#>"</Expression> </Expressions> </FlatFileConnection> <# } #> <# if (destinationFileType == "EXCEL") { #> <FileConnection Name ="<#=filePath #>" FilePath="<#=filePath#>" DelayValidation="true"/> <ExcelConnection DelayValidation="true" Name="EXCEL" ConnectionString="<#=sourceConnectionString.Replace("\"", """)#>"> <Expressions> <Expression PropertyName="ExcelFilePath">@[User::param_DestinationFolder] + @[User::param_FileName] + "<#=fileExtension#>"</Expression> </Expressions> </ExcelConnection> <# } #> </Connections> <Packages> <Package Name="<#=packageName#>" ConstraintMode="Linear" DelayValidation ="true" ProtectionLevel="<#=packageProtectionLevel#>"> <Variables> <Variable Name="param_StoredProcedureParameters" DataType="String" EvaluateAsExpression="1"><#=storedProcedureParameters#></Variable> <Variable Name="param_DestinationFolder" DataType="String"><#=destinationFolder#></Variable> <Variable Name="param_FileName" DataType="String" EvaluateAsExpression="1"><#=fileName#></Variable> <Variable Name="ExecuteStatement" DataType="String" EvaluateAsExpression="1">EXEC <#=executeStatement#></Variable> </Variables> <Connections> <Connection ConnectionName="<#=packageConnectionManagerName#>"></Connection> </Connections> <Tasks> <# if (destinationFileType == "EXCEL") { #> <FileSystem Name="Delete Excel File" Operation="DeleteFile"> <FileInput ConnectionName="<#=filePath #>"/> </FileSystem> <# } #> <Script Name="Add Data and Time to the File Name"> <ScriptTaskProject> <ScriptTaskProject Name="Add Data and Time to the File Name and Parameters"> <AssemblyReferences> <AssemblyReference AssemblyPath="Microsoft.SqlServer.ManagedDTS.dll" /> <AssemblyReference AssemblyPath="Microsoft.SqlServer.ScriptTask.dll" /> <AssemblyReference AssemblyPath="System.dll" /> <# if (major_version < 11) { #> <AssemblyReference AssemblyPath="System.AddIn.dll" /> <# } #> <AssemblyReference AssemblyPath="System.Data.dll" /> <AssemblyReference AssemblyPath="System.Windows.Forms.dll" /> <AssemblyReference AssemblyPath="System.Xml.dll" /> </AssemblyReferences> <ReadWriteVariables> <Variable VariableName="param_FileName" DataType="String"/> <Variable VariableName="param_StoredProcedureParameters" DataType="String"/> <Variable VariableName="param_DestinationFolder" DataType="String"/> <Variable VariableName="ExecuteStatement" DataType="String"/> </ReadWriteVariables> <Files> <File Path="ScriptMain.cs"> using System; using System.Data; using Microsoft.SqlServer.Dts.Runtime; using System.Windows.Forms; <# if (major_version < 11) { #> [System.AddIn.AddIn("ScriptMain", Version = "1.0", Publisher = "", Description = "")] <# } else { #> [Microsoft.SqlServer.Dts.Tasks.ScriptTask.SSISScriptTaskEntryPointAttribute] <# } #> public partial class ScriptMain : Microsoft.SqlServer.Dts.Tasks.ScriptTask.VSTARTScriptObjectModelBase { enum ScriptResults { Success = Microsoft.SqlServer.Dts.Runtime.DTSExecResult.Success, Failure = Microsoft.SqlServer.Dts.Runtime.DTSExecResult.Failure }; public void Main() { bool fireAgain = true; string fileName = Dts.Variables["User::param_FileName"].Value.ToString(); string stpParam = Dts.Variables["User::param_StoredProcedureParameters"].Value.ToString(); string destinationFolder = Dts.Variables["User::param_DestinationFolder"].Value.ToString(); // Add date and time to the file name <#if (destinationFileType == "CSV") {#> Dts.Variables["User::param_FileName"].Value = fileName + "_" + DateTime.Now.ToString("yyyyMMdd_HHmmss"); <#} else if (destinationFileType == "EXCEL") {#> Dts.Variables["User::param_FileName"].Value = fileName + "_" + DateTime.Now.ToString("yyyyMMdd_HHmmss"); <#}#> // Replace the GETDATE() token with the current datetime string currentDate = "'" + DateTime.Now.ToString("yyyyMMdd HH:mm:ss.fff") + "'"; Dts.Variables["User::param_DestinationFolder"].Value = destinationFolder.EndsWith("\\") ? destinationFolder : destinationFolder + "\\"; Dts.Variables["User::param_StoredProcedureParameters"].Value = stpParam.Replace("GETDATE()", currentDate); Dts.Variables["User::ExecuteStatement"].Value = String.Format("EXEC <#=storedProcedure#> {0}", stpParam.Replace("GETDATE()", currentDate)); Dts.Events.FireInformation(0, string.Empty, string.Format("Calling procedure: [{0}]", Dts.Variables["User::ExecuteStatement"].Value), string.Empty, 0, ref fireAgain); Dts.TaskResult = (int)ScriptResults.Success; } } </File> </Files> </ScriptTaskProject> </ScriptTaskProject> </Script> <# if (destinationFileType == "EXCEL") { #> <ExecuteSQL Name="Create Excel Sheet" FailPackageOnFailure="true" ConnectionName="EXCEL"> <DirectInput>CREATE TABLE [Query] ( <# foreach (DataRow column in storedProcedureTable.Rows) { if (((int)column["ColumnOrdinal"] + 1) < storedProcedureTable.Rows.Count) excelTempTerminator = excelFieldTerminator; else excelTempTerminator = excelTableTerminator; excelStringFormat = (int)column["ColumnSize"]; excelDecimalFormat = String.Format("{0},{1}", column["NumericPrecision"], column["NumericScale"]); if (column["DataTypeName"].ToString() == "bigint") excelColumnType = "decimal(18,0)"; else if (column["DataTypeName"].ToString() == "bit") excelColumnType = "bit"; else if (column["DataTypeName"].ToString() == "char") if (excelStringFormat <= 255) excelColumnType = "char(" + excelStringFormat + ")"; else excelColumnType = "longtext"; else if (column["DataTypeName"].ToString() == "datetime" || column["DataTypeName"].ToString() == "date") excelColumnType = "datetime"; else if (column["DataTypeName"].ToString() == "decimal" || column["DataTypeName"].ToString() == "numeric") if (excelDecimalPrecision <= 28) excelColumnType = "decimal(" + excelDecimalFormat + ")"; else excelColumnType = "decimal(28,4)"; else if (column["DataTypeName"].ToString() == "float") excelColumnType = "float"; else if (column["DataTypeName"].ToString() == "int") excelColumnType = "int"; else if (column["DataTypeName"].ToString() == "money") excelColumnType = "currency"; else if (column["DataTypeName"].ToString() == "nchar") if (excelStringFormat <= 255) excelColumnType = "nchar(" + excelStringFormat + ")"; else excelColumnType = "longtext"; else if (column["DataTypeName"].ToString() == "ntext") excelColumnType = "longtext"; else if (column["DataTypeName"].ToString() == "nvarchar") if (excelStringFormat <= 255) excelColumnType = "varchar(" + excelStringFormat + ")"; else excelColumnType = "longtext"; else if (column["DataTypeName"].ToString() == "real") excelColumnType = "real"; else if (column["DataTypeName"].ToString() == "smallint") excelColumnType = "smallint"; else if (column["DataTypeName"].ToString() == "text") excelColumnType = "longtext"; else if (column["DataTypeName"].ToString() == "tinyint") excelColumnType = "byte"; else if (column["DataTypeName"].ToString() == "varchar") if (excelStringFormat <= 255) excelColumnType = "varchar(" + excelStringFormat + ")"; else excelColumnType = "longtext"; #> [<#=column["ColumnName"].ToString()#>] <#=excelColumnType#><#=excelTempTerminator#> <# } //...foreach #> </DirectInput> </ExecuteSQL> <# } //...if (destinationFileType == "EXCEL")#> <Dataflow DelayValidation="true" Name="Load Destination File"> <Transformations> <OleDbSource Name="<#=storedProcedureName#>" ConnectionName="<#=packageConnectionManagerName#>" ValidateExternalMetadata="false"> <VariableInput VariableName="User.ExecuteStatement"/> </OleDbSource> <# if (destinationFileType == "CSV") { #> <FlatFileDestination Name="CSV" ConnectionName="CSV"/> <# } #> <# if (destinationFileType == "EXCEL") { bool dataConversionNeeded = false; foreach (DataRow column in storedProcedureTable.Rows) { if (column["DataTypeName"].ToString() == "char" || column["DataTypeName"].ToString() == "varchar" || column["DataTypeName"].ToString() == "text") { dataConversionNeeded = true; break; } } if (dataConversionNeeded) { #> <DataConversion Name="Data Conversion" ValidateExternalMetadata="false"> <Columns> <# foreach (DataRow column in storedProcedureTable.Rows) { excelStringFormat = (int)column["ColumnSize"]; excelDecimalFormat = String.Format("{0},{1}", column["NumericPrecision"], column["NumericScale"]); if (column["DataTypeName"].ToString() == "char" || column["DataTypeName"].ToString() == "varchar") { #> <Column SourceColumn="<#=column["ColumnName"].ToString()#>" DataType="String" Length="<#=excelStringFormat#>"/> <# } else if (column["DataTypeName"].ToString() == "text") { #> <Column SourceColumn="<#=column["ColumnName"].ToString()#>" DataType="String" Length="4000"/> <# } #> <# } //...foreach #> </Columns> </DataConversion> <# } //... if dataConversionNeeded // In order to compile the current Biml script, the next code will create the Excel file if(File.Exists(filePath)) File.Delete(filePath); string excelSheetMetadata = "CREATE TABLE [Query] ("; foreach (DataRow column in storedProcedureTable.Rows) { if (((int)column["ColumnOrdinal"] + 1) < storedProcedureTable.Rows.Count) excelTempTerminator = excelFieldTerminator; else excelTempTerminator = excelTableTerminator; excelStringFormat = (int)column["ColumnSize"]; excelDecimalFormat = String.Format("{0},{1}", column["NumericPrecision"], column["NumericScale"]); if (column["DataTypeName"].ToString() == "bigint") excelColumnType = "decimal(18,0)"; else if (column["DataTypeName"].ToString() == "bit") excelColumnType = "bit"; else if (column["DataTypeName"].ToString() == "char") if (excelStringFormat <= 255) excelColumnType = "char(" + excelStringFormat + ")"; else excelColumnType = "longtext"; else if (column["DataTypeName"].ToString() == "datetime" || column["DataTypeName"].ToString() == "date") excelColumnType = "datetime"; else if (column["DataTypeName"].ToString() == "decimal" || column["DataTypeName"].ToString() == "numeric") if (excelDecimalPrecision <= 28) excelColumnType = "decimal(" + excelDecimalFormat + ")"; else excelColumnType = "decimal(28,4)"; else if (column["DataTypeName"].ToString() == "float") excelColumnType = "float"; else if (column["DataTypeName"].ToString() == "int") excelColumnType = "int"; else if (column["DataTypeName"].ToString() == "money") excelColumnType = "currency"; else if (column["DataTypeName"].ToString() == "nchar") if (excelStringFormat <= 255) excelColumnType = "nchar(" + excelStringFormat + ")"; else excelColumnType = "longtext"; else if (column["DataTypeName"].ToString() == "ntext") excelColumnType = "longtext"; else if (column["DataTypeName"].ToString() == "nvarchar") if (excelStringFormat <= 255) excelColumnType = "varchar(" + excelStringFormat + ")"; else excelColumnType = "longtext"; else if (column["DataTypeName"].ToString() == "real") excelColumnType = "real"; else if (column["DataTypeName"].ToString() == "smallint") excelColumnType = "smallint"; else if (column["DataTypeName"].ToString() == "text") excelColumnType = "longtext"; else if (column["DataTypeName"].ToString() == "tinyint") excelColumnType = "byte"; else if (column["DataTypeName"].ToString() == "varchar") if (excelStringFormat <= 255) excelColumnType = "varchar(" + excelStringFormat + ")"; else excelColumnType = "longtext"; excelSheetMetadata += String.Format("[{0}] {1}{2}", column["ColumnName"].ToString(), excelColumnType, excelTempTerminator); } DataTable excelSheet = ExternalDataAccess.GetDataTable(sourceConnectionString, excelSheetMetadata); #> <ExcelDestination Name="EXCEL" ConnectionName="EXCEL" UseFastLoadIfAvailable="false" MaximumInsertCommitSize="2147483647" ValidateExternalMetadata="false"> <ErrorHandling ErrorRowDisposition="IgnoreFailure" TruncationRowDisposition="IgnoreFailure"> <Inputs> <Input ErrorRowDisposition="FailComponent" TruncationRowDisposition="IgnoreFailure" InputName="Destination Input" /> </Inputs> </ErrorHandling> <ExternalTableOutput Table="[Query]"/> <Columns> <# foreach (DataRow column in storedProcedureTable.Rows) { #> <Column SourceColumn="<#=column["ColumnName"].ToString()#>" TargetColumn="<#=column["ColumnName"].ToString()#>"/> <# } #> </Columns> </ExcelDestination> <#reader.Close(); } //...if (destinationFileType == "EXCEL") } //...using #> </Transformations> </Dataflow> </Tasks> </Package> </Packages> </Biml>
Extract a Stored Procedure resultset in CSV or Excel
lucazav
This Biml script will generate a SSIS package that will extract data from a stored procedure and will persist them in a CSV file or an EXCEL file. SQL Server 2008 (R2) and SQL Server 2012 are supported.
published 07.19.13
last updated 07.21.13
Comments
There are no comments yet.