1
votes

How do I get to export blank tables (i.e. just the headers) as well as tables containing data. The reason for the blank table is my customer wants to import the table structure i.e. the headers so it can be used in the future if needed.

When I follow the solution on: Exporting data from multiple SQL tables to different flat files using SSIS Script Task I get the below error:

Error: System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.NullReferenceException: Object reference not set to an instance of an object. at ST_97b3989b700a41f1ad54fb2abd27b891.vbproj.ScriptMain.Main() --- End of inner exception stack trace --- at System.RuntimeMethodHandle._InvokeMethodFast(Object target, Object[] arguments, SignatureStruct& sig, MethodAttributes methodAttributes, RuntimeTypeHandle typeOwner) at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture, Boolean skipVisibilityChecks) at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture) at System.RuntimeType.InvokeMember(String name, BindingFlags bindingFlags, Binder binder, Object target, Object[] providedArgs, ParameterModifier[] modifiers, CultureInfo culture, String[] namedParams) at Microsoft.SqlServer.Dts.Tasks.ScriptTask.VSTATaskScriptingEngine.ExecuteScript()

2
To restate: you'd like to get a flat file created that only contains a header row/column name? No concerns about data types or anything like that?billinkc
Does the customer really want that or would they prefer the actual SQL/DDL/data definition language for your tables?billinkc
Hi, the customer would like to get a flat file created that only contains a header row/column name for tables that contain no datar.blue
Why don't you just take a database backup since you want all tables (including the ones with data... or did you mean the tables but not the data in these?), or use the GUI?scsimon
I would ask why you have 500 empty tables in your database. That seems a bit strange. And a csv is useless as a data tool unless you know what datatypes they are. If all you want is the column names why not write a simple query using sys.columns?Sean Lange

2 Answers

1
votes

There's no need for custom coding - this is all out of the box functionality with SSIS.

Your package is going to look approximately like this

Execute SQL Task to FELC with Dataflow

Variables

Create variables to handle what we need. These will all be string data types except rsObject

  • ColumnList This will contain the column list, populated via the FELC
  • CurrentFileName This will be built on an expression @[User::PathOutput] + "\\" + @[User::SchemaName] + "_" + @[User::TableName] + ".csv"
  • PathOutput This is the place where we want the export files generated. I use C:\ssisdata but you will need to make this work with your environment
  • rsObject This will hold the result set of the Execute SQL Task
  • SchemaName This will be the schema name and is populated from the FELC
  • TableName This is the table name and filled in via the FELC

Connection Manager

There are two connection managers: Source and ExportFile.

Source is an OLE DB Connection manager pointed at my source database (localhost\dev2014 msdb).

ExportFile is a flat file connection manager defined with a single column ColumnList, DT_STR data type, 8000 column width and uses a column delimiter of CRLF. The other thing to note is that I have an Expression defined on the Connection Manager (right click on the Connection Manager and select Properties). Here we'll assign ConnectionString to the variable @[User::CurrentFileName]

Execute SQL Task

The purpose of this task is generate the set of data we're using. I'm going to use the STUFF/FOR XML PATH "trick" to concatenate multiple rows into a single row. We're going to collapse all the columns into a single row which will have SchemaName, TableName and a comma delimited list of column names. Run the following query to see what data it is generating.

SELECT
    S.name AS SchemaName
,   T.name AS TableName
,   (
        SELECT
            STUFF
            (
                (
                SELECT
                    ',' + C.name
                FROM
                    sys.columns AS C
                WHERE
                    C.object_id = T.object_id
                ORDER BY
                    C.column_id
                FOR XML PATH('')
                )
            ,   1
            ,   1
            ,   ''
            )
    ) AS ColumnList
FROM
    sys.schemas AS S
    INNER JOIN
        sys.tables AS T
        ON S.schema_id = T.schema_id
ORDER BY
    1,2;

You can modify the above to filter where the row count is zero or just generate all the tables and then don't send the client the files you don't want. That solution is left to you.

I specify that the Execute SQL Task returns a Full ResultSet. I push the results into our variable, @[User::rsObject].

FELC Shred Results

I used a ForEach Loop Container set to shred ADO.NET results. The source table is our @[User::rsObject]. In each loop around our data set, we assign into our SSIS variables @[User::SchemaName], @[User::TableName], @[User::ColumnList]

DFT Generate Export

This data flow task is designed to select the column list variable via an OLE DB Source, with a parameter, and route to a Flat File Destination.

enter image description here

The OLE DB Source uses the following query

SELECT CAST(? AS varchar(8000)) AS ColumnList

I map @[User::ColumnList] to ordinal position 0. This will allow the source query to "import" the value of the variable.

The Flat File Destination uses our ExportFile connection manager and maps the column ColumnList to the only column in the file.

Biml

Business Intelligence Markup Language, or Biml, is a XML dialect that describes an SSIS package. It makes creating SSIS package much easier since I can give you this code which creates the exact package I've described above.

How do you use it?

  1. Download BimlExpress
  2. Register it. It's free but does require an email address that can receive the license key.
  3. Open an SSIS project and right click on the project and Add New Biml File
  4. Replace the contents of BimlScript.biml with the below Biml.
  5. Update line 3 to change the Data Source to point to your server and Initial Catalog to match your database
  6. Update line 4 to point to where you'd like your CSVs generated
  7. Update line 26 to match the folder location (same as in step 6)
  8. Right click on BimlScript and it should generate a package named SO_51195870.dtsx

Code

<Biml xmlns="http://schemas.varigence.com/biml.xsd">
    <Connections>
        <OleDbConnection ConnectionString="Data Source=localhost\dev2014;Initial Catalog=msdb;Provider=SQLNCLI11.1;Integrated Security=SSPI;Auto Translate=False;Packet Size=32767;" Name="Source" />
        <FlatFileConnection Name="ExportFile" FileFormat="FFF Header File" FilePath="C:\ssisdata\Demo.csv" DelayValidation="true" />
    </Connections>
    <FileFormats>
        <FlatFileFormat Name="FFF Header File" CodePage="1252" IsUnicode="false" >
            <Columns>
                <Column Name="ColumnList" DataType="AnsiString" Length="8000" Delimiter="CRLF" />
            </Columns>
        </FlatFileFormat>
    </FileFormats>
    <Packages>
        <Package Name="SO_51195870" ConstraintMode="Linear">
            <Connections>
                <Connection ConnectionName="ExportFile">
                    <Expressions>
                        <Expression ExternalProperty="ConnectionString">@[User::CurrentFileName]</Expression>
                    </Expressions>
                </Connection>
            </Connections>
            <Variables>
                <Variable Name="SchemaName" DataType="String"></Variable>
                <Variable Name="TableName" DataType="String"></Variable>
                <Variable Name="ColumnList" DataType="String"></Variable>
                <Variable Name="PathOutput" DataType="String">C:\ssisdata</Variable>
                <Variable Name="CurrentFileName" DataType="String" EvaluateAsExpression="true">@[User::PathOutput] + "\\" + @[User::SchemaName] + "_" + @[User::TableName] + ".csv"</Variable>
                <Variable Name="rsObject" DataType="Object" />
            </Variables>
            <Tasks>
                <ExecuteSQL Name="SQL GetData" ConnectionName="Source" ResultSet="Full">
                    <Results>
                        <Result Name="0" VariableName="User.rsObject"/>
                    </Results>
                    <DirectInput>SELECT
    S.name AS SchemaName
,   T.name AS TableName
,   (
        SELECT
            STUFF
            (
                (
                SELECT
                    ',' + C.name
                FROM
                    sys.columns AS C
                WHERE
                    C.object_id = T.object_id
                ORDER BY
                    C.column_id
                FOR XML PATH('')
                )
            ,   1
            ,   1
            ,   ''
            )
    ) AS ColumnList
FROM
    sys.schemas AS S
    INNER JOIN
        sys.tables AS T
        ON S.schema_id = T.schema_id
ORDER BY
    1,2</DirectInput>
                </ExecuteSQL>
                <ForEachAdoLoop Name="FELC Shred Results" SourceVariableName="User.rsObject">
                    <VariableMappings>
                        <VariableMapping Name="0" VariableName="User.SchemaName"/>
                        <VariableMapping Name="1" VariableName="User.TableName"/>
                        <VariableMapping Name="2" VariableName="User.ColumnList"/>
                    </VariableMappings>
                        <Tasks>
                            <Dataflow Name="DFT Generate Export" DelayValidation="true">
                                <Transformations>
                                    <OleDbSource Name="OLESRC Query" ConnectionName="Source">
                                        <DirectInput>SELECT CAST(? AS varchar(8000)) AS ColumnList</DirectInput>
                                        <Parameters>
                                            <Parameter Name="0" VariableName="User.ColumnList" />
                                        </Parameters>
                                    </OleDbSource>
                                    <FlatFileDestination Name="DST File" ConnectionName="ExportFile"></FlatFileDestination>

                                </Transformations>
                            </Dataflow>
                        </Tasks>
                </ForEachAdoLoop>
            </Tasks>

        </Package>
    </Packages>
</Biml>

Why does the referenced solution fail

The referenced solution doesn't expect to export an empty recordset. It has explicit logic to test for the empty case If reader.HasRows Then ...which is also the source of the supplied error. writer.Close() in the finally block is Nothing so the Close operation cannot be invoked against it.

A possible rewrite to avoid the exception would be

    Finally
        connection.Close()
        If writer IsNot Nothing Then
            writer.Close()
        End If
    End Try

That said, even if you take out the has rows check, you will still get an empty file.

Fin

I sincerely recommend the approach I outlined. I use the system tables to generate the set of schema, tables, and columns. That data set is used to build out the external files. This is script free SSIS approach that solves the stated problem. The referenced VB approach does not do that and it will be a complete rewrite of that code to make it so.

0
votes

It sounds like you want to script out all tables. Here is how to do it:

Right Click on your Database > Tasks > Generate Scripts

enter image description here

Then follow the GUI. Specifically, you want to select the Tables objects int he second step:

objects

Then chose the file location in the next step, and bam you're done.