Auto-increment file names for a SSIS package - sql

I'm new here. I have a question in regards to SSIS and dynamic file names. I've found an article that can help for incrementing file names by appending dates, but I actually need to keep a rolling filename counter of 001-999 and start over at 001 for each time the file is created. Here's the article I reviewed:
https://www.red-gate.com/simple-talk/sql/ssis/passing-variables-to-and-from-an-ssis-task/
For instance, my first file would be "P12345001" where "001" is the first instance of the file. The next time the SSIS package runs, I'd want the filename to be "P12345002" and so on, until it gets to "P12345999" and the next file would go back to "P12345001".
Is this possible in SQL or SSIS or do I need to script something via a language like VB.NET or C# (which I only have limited experience with)?
If it helps, I am running the SSIS package on MS SQL Server 2012. Please let me know if I've included all pertinent information or not. Many thanks in advance!

SQL Work
While this is syntactically correct for SQL Server, you can use the same concept with whatever RDBMS you like, just modify it as needed.
Data Storage
To solve this, you need some place of storing the previous run's value. It appears that a key value type of table would be sufficient for your needs.
-- Simple table structure
CREATE TABLE dbo.FileExecutionHistory
(
BaseFileName varchar(50) NOT NULL
, LastSequence int NOT NULL
, CONSTRAINT PK_dbo_FileExecutionHistory PRIMARY KEY
(
BaseFileName
)
);
Into that you will store the name of the file (P12345) and the last sequence number used (1).
Data Access
Within SSIS, you will pass in the file name to this query. Note the use of ? - that will be the OLE DB provider's syntax for ordinal based query parameterization.
The inner query generates at most 2 rows. The last sequence used for this file name (if it is found) and 0 (not found). The outer query uses the TOP(1) argument to only allow one row to be generated and we order by the value descending - thus ensuring we only take the largest value.
We use the modulo % operator with 999 to restrict the allowable domain of values to 0 to 998.
However, since you want 1 to 999, we then add 1 to the resultant value.
-- Access pattern
SELECT TOP(1)
(D.LastSequence % 999) + 1 AS LastSequence
FROM
(
SELECT
FEH.LastSequence
FROM
dbo.FileExecutionHistory AS FEH
WHERE
FEH.BaseFileName = ?
-- Handle the does not exist case
UNION
SELECT
CAST(0 AS int)
) D
ORDER BY
D.LastSequence DESC;
Data Storage
Again, we will see the ? in play but this time we use it for both our file name and the sequence value.
Since the OLE DB provider is ordinal based, we would need to use four place holders and map in the "same" variable twice. That's silly so I declare a SQL Server variables up front and assign into them. For consistency, you can apply this to the Data Access pattern as well.
The Merge statement is clunky and uncertain in results so I use the tested and true update/insert pattern. I update the Value in my table for the file which will match nothing if it doesn't exist. I then attempt to insert a value into our table if it doesn't already exist.
Only one of those last two statements will ever perform an action so this is good. Plus, we have our primary key constraint preventing duplicates on file name so this is double plus good.
-- Storage pattern
DECLARE
#BaseFileName varchar(50) = ?
, #LastSequence int = ?;
UPDATE
FEH
SET
LastSequence = #LastSequence
FROM
dbo.FileExecutionHistory AS FEH
WHERE
FEH.BaseFileName = #BaseFileName;
INSERT INTO
dbo.FileExecutionHistory
(
BaseFileName
, LastSequence
)
SELECT
D.BaseFileName
, D.LastSequence
FROM
(
VALUES (#BaseFileName, #LastSequence)
) D (BaseFileName, LastSequence)
LEFT OUTER JOIN
dbo.FileExecutionHistory AS FEH
ON FEH.BaseFileName = D.BaseFileName
WHERE
FEH.BaseFileName IS NULL;
SSIS work
Break the work down into small pieces and use what SSIS gives you natively. In this approach, we're going to use Variables to do most of the heavy lifting.
Variables
You're looking at a package with a few obvious Variables in it. One Variable to hold your Sequence number. One Variable to hold the base file name. I assume the full file name is probably something like P12345001.txt so you'll want to keep track of that stuff too.
User::BaseFileName String -> P12345
User::SequenceNumber Int -> 0
User::FileExtension String -> .txt
User::CurrentFolder String -> C:\ssisdata\Output
User::SequencePad String > 001
User::FullFileName String > P12345001.txt
User::CurrentFileName String > C:\ssisdata\Output\P12345001.txt
The first 4 variables will be "normal". The last 3 will have Expressions in them. Expressions are simple formulas much as you'd see in Excel. Notice that I've assigned a value of 0 for the sequence number. That's not allowed in your scenario but it will be helpful for us to ensure all of the pieces of the puzzle are working as expected.
The expression for SequencePad will use a classic technique of padding N characters to the left of our value and then slicing the right N most characters out. For values like 999, there will be no net result. For 1, we'll end up with 001. Since we're dealing with a numeric value for SequenceNumber, we must first cast it to a string before concatenating + with the triple zeroes.
RIGHT("000" + (DT_WSTR, 3) #[User::SequenceNumber], 3)
FullFileName simply concatenates 3 variables together. Two "hard coded" values and the padded sequence number we built above.
#[User::BaseFileName] + #[User::SequencePad] + #[User::FileExtension]
Finally, CurrentFileName is built much in the same manner as FullFileName. We're going to concatenate more variables together. The reason for all these little steps is that this is the only way one has to debug Expressions. There is no ability to put break points on them. Thus, make 'em small and composite them together.
The only "trick" to be aware of here is that the slash \ is also an escape character in nearly every language so if you want to use \, you really need to use \\
#[User::CurrentFolder] + "\\" + #[User::FullFileName]
At this point, you can test putting different values in for the first 4 variables and ensure the expressed values look as expected. If you don't need a file extension, just leave that value blank - the expression will work just fine.
Flat File Connection Manager
Create a Flat File Connection manager based on the structure of your file P12345001.txt
Now that you have a variable defined, you will need to use this property as part of a Flat File Connection manager's Connection String property. See here and here for setting the Property. I'll call this Output
Ole DB Connection Manager
You need an OLE DB Connection Manager pointing to the SQL server and database we created the table in (way at the top). I'll assume it's called Config
Execute SQL 1
The purpose of this task is to assign a value into #[User::SequenceNumber]. Drag an Execute SQL Task onto the canvas and configure it much as I show in this answer. For our Connection, we'll use Config. The SQL Statement is the data access query I defined above. In the Result Set tab, we'll specify User::SequenceNumber
Run your package and if it doesn't blow up, we can assume things went well.
Data Flow Task
Somewhere, somehow you're using the computed file name and I assume you're going to query some table and push rows into that file. Your Data Flow will look something like an OLE DB Source component wired into a Flat File Destination. An example, with Biml none-the-less, is here
Execute SQL 2
Now that our data flow is complete, we need to update our original table with our used sequence number.
This will be another Execute SQL Task, using the third query from above, with No resultset defined. We will map in the variables #[User::FileName] and #[User::SequenceNumber] (in that order) as parameters 0 and 1.
Biml
The Business Intelligence Markup Lanaguage, Biml, can be thought of as the operating system for business intelligence. It has pieces in there to handle all aspects of what you need to describe to get the work done. Here, I'm using it to describe what an SSIS package would look like that has an auto increment file name. To make this work for you
Download and install BimlExpress
Open an SSIS project and add a new Biml file. Paste the contents below into it
Edit line 11 and 12 to point to a valid SQL Server and folder on your computer
Right click on BimlScript.biml and select Generate SSIS Package
There should now be an SSIS package in your project called so_45355289.dtsx - run that sucker
Assuming all that works, you can either dig in an modify the supplied Biml to meet your needs (change the queries to actually extract the data you care about and make the flat file format match). Or you can just modify the existing SSIS package and forget about Biml for now. Eventually you'll probably care about it though because it's a huge time saver.
<Biml xmlns="http://schemas.varigence.com/biml.xsd">
<FileFormats>
<FlatFileFormat Name="FF Output">
<Columns>
<Column Name="DatabaseName" Length="128" Delimiter="Tab" />
<Column Name="DatabaseId" DataType="Int32" Delimiter="CRLF" />
</Columns>
</FlatFileFormat>
</FileFormats>
<Connections>
<OleDbConnection Name="Config" ConnectionString="Provider=SQLNCLI11;Server=localhost\DEV2016;Initial Catalog=tempdb;Integrated Security=SSPI;" />
<FlatFileConnection Name="Output" FileFormat="FF Output" FilePath="C:\ssisdata\output\P12345.txt" />
</Connections>
<Packages>
<Package Name="so_45355289" ConstraintMode="Linear">
<Connections>
<Connection ConnectionName="Output">
<Expressions>
<Expression ExternalProperty="ConnectionString">#[User::CurrentFileName]</Expression>
</Expressions>
</Connection>
</Connections>
<Variables>
<Variable Name="BaseFileName" DataType="String">P12345</Variable>
<Variable Name="SequenceNumber" DataType="Int32">0</Variable>
<Variable Name="FileExtension" DataType="String">.txt</Variable>
<Variable Name="CurrentFolder" DataType="String">C:\ssisdata\Output</Variable>
<Variable Name="SequencePad" DataType="String" EvaluateAsExpression="true">RIGHT("000" + (DT_WSTR, 3) #[User::SequenceNumber], 3)</Variable>
<Variable Name="FullFileName" DataType="String" EvaluateAsExpression="true">#[User::BaseFileName] + #[User::SequencePad] + #[User::FileExtension]</Variable>
<Variable Name="CurrentFileName" DataType="String" EvaluateAsExpression="true">#[User::CurrentFolder] + "\\" + #[User::FullFileName]</Variable>
</Variables>
<Tasks>
<ExecuteSQL Name="SQL Get Current Sequence" ConnectionName="Config" ResultSet="SingleRow">
<DirectInput><![CDATA[SELECT TOP(1)
(D.LastSequence % 999) + 1 AS LastSequence
FROM
(
SELECT
FEH.LastSequence
FROM
dbo.FileExecutionHistory AS FEH
WHERE
FEH.BaseFileName = ?
-- Handle the does not exist case
UNION
SELECT
CAST(0 AS int)
) D
ORDER BY
D.LastSequence DESC;]]></DirectInput>
<Parameters>
<Parameter Name="0" DataType="String" Length="128" VariableName="User.BaseFileName" />
</Parameters>
<Results>
<Result Name="0" VariableName="User.SequenceNumber" />
</Results>
</ExecuteSQL>
<Dataflow Name="DFT Generate data">
<Transformations>
<OleDbSource Name="OLESRC Query" ConnectionName="Config">
<DirectInput>SELECT name AS DatabaseName, database_id AS DatabaseId FROM sys.databases;</DirectInput>
</OleDbSource>
<FlatFileDestination Name="FFDST Output" ConnectionName="Output" Overwrite="true">
</FlatFileDestination>
</Transformations>
</Dataflow>
<ExecuteSQL Name="SQL Store Sequence" ConnectionName="Config">
<DirectInput><![CDATA[-- Storage pattern
DECLARE
#BaseFileName varchar(50) = ?
, #LastSequence int = ?;
UPDATE
FEH
SET
LastSequence = #LastSequence
FROM
dbo.FileExecutionHistory AS FEH
WHERE
FEH.BaseFileName = #BaseFileName;
INSERT INTO
dbo.FileExecutionHistory
(
BaseFileName
, LastSequence
)
SELECT
D.BaseFileName
, D.LastSequence
FROM
(
VALUES (#BaseFileName, #LastSequence)
) D (BaseFileName, LastSequence)
LEFT OUTER JOIN
dbo.FileExecutionHistory AS FEH
ON FEH.BaseFileName = D.BaseFileName
WHERE
FEH.BaseFileName IS NULL;]]></DirectInput>
<Parameters>
<Parameter Name="0" DataType="String" Length="128" VariableName="User.BaseFileName" />
<Parameter Name="1" DataType="Int32" VariableName="User.SequenceNumber" />
</Parameters>
</ExecuteSQL>
</Tasks>
</Package>
</Packages>
</Biml>

Related

How to Map Input and Output Columns dynamically in SSIS?

I Have to Upload Data in SQL Server from .dbf Files through SSIS.
My Output Column is fixed but the input column is not fixed because the files come from the client and the client may have updated data by his own style. there may be some unused columns too or the input column name can be different from the output column.
One idea I had in my mind was to map files input column with output column in SQL Database table and use only those column which is present in the row for file id.
But I am not getting how to do that. Any idea?
Table Example
FileID
InputColumn
OutputColumn
Active
1
CustCd
CustCode
1
1
CName
CustName
1
1
Address
CustAdd
1
2
Cust_Code
CustCode
1
2
Customer Name
CustName
1
2
Location
CustAdd
1
If you create a similar table, you can use it in 2 approaches to map columns dynamically inside SSIS package, or you must build the whole package programmatically. In this answer i will try to give you some insights on how to do that.
(1) Building Source SQL command with aliases
Note: This approach will only work if all .dbf files has the same columns count but the names are differents
In this approach you will generate the SQL command that will be used as source based on the FileID and the Mapping table you created. You must know is the FileID and the .dbf File Path stored inside a Variable. as example:
Assuming that the Table name is inputoutputMapping
Add an Execute SQL Task with the following command:
DECLARE #strQuery as VARCHAR(4000)
SET #strQuery = 'SELECT '
SELECT #strQuery = #strQuery + '[' + InputColumn + '] as [' + OutputColumn + '],'
FROM inputoutputMapping
WHERE FileID = ?
SET #strQuery = SUBSTRING(#strQuery,1,LEN(#strQuery) - 1) + ' FROM ' + CAST(? as Varchar(500))
SELECT #strQuery
And in the Parameter Mapping Tab select the variable that contains the FileID to be Mapped to the parameter 0 and the variable that contains the .dbf file name (alternative to table name) to the parameter 1
Set the ResultSet type to Single Row and store the ResultSet 0 inside a variable of type string as example #[User::SourceQuery]
The ResultSet value will be as following:
SELECT [CustCd] as [CustCode],[CNAME] as [CustName],[Address] as [CustAdd] FROM database1
In the OLEDB Source select the Table Access Mode to SQL Command from Variable and use #[User::SourceQuery] variable as source.
(2) Using a Script Component as Source
In this approach you have to use a Script Component as Source inside the Data Flow Task:
First of all, you need to pass the .dbf file path and SQL Server connection to the script component via variables if you don't want to hard code them.
Inside the script editor, you must add an output column for each column found in the destination table and map them to the destination.
Inside the Script, you must read the .dbf file into a datatable:
C# Read from .DBF files into a datatable
Load a DBF into a DataTable
After loading the data into a datatable, also fill another datatable with the data found in the MappingTable you created in SQL Server.
After that loop over the datatable columns and change the .ColumnName to the relevant output column, as example:
foreach (DataColumn col in myTable.Columns)
{
col.ColumnName = MappingTable.AsEnumerable().Where(x => x.FileID = 1 && x.InputColumn = col.ColumnName).Select(y => y.OutputColumn).First();
}
After loop over each row in the datatable and create a script output row.
In addition, note that in while assigning output rows, you must check if the column exists, you can first add all columns names to list of string, then use it to check, as example:
var columnNames = myTable.Columns.Cast<DataColumn>()
.Select(x => x.ColumnName)
.ToList();
foreach (DataColumn row in myTable.Rows){
if(columnNames.contains("CustCode"){
OutputBuffer0.CustCode = row("CustCode");
}else{
OutputBuffer0.CustCode_IsNull = True
}
//continue checking all other columns
}
If you need more details about using a Script Component as a source, then check one of the following links:
SSIS Script Component as Source
Creating a Source with the Script Component
Script Component as Source – SSIS
SSIS – USING A SCRIPT COMPONENT AS A SOURCE
(3) Building the package dynamically
I don't think there are other methods that you can use to achieve this goal except you has the choice to build the package dynamically, then you should go with:
BIML
Integration Services managed object model
EzApi library
(4) SchemaMapper: C# schema mapping class library
Recently i started a new project on Git-Hub, which is a class library developed using C#. You can use it to import tabular data from excel, word , powerpoint, text, csv, html, json and xml into SQL server table with a different schema definition using schema mapping approach. check it out at:
SchemaMapper: C# Schema mapping class library
You can follow this Wiki page for a step-by-step guide:
Import data from multiple files into one SQL table step by step guide

ColdFusion sql issue

Ran a security scan against an URL and received the report below:
The vulnerability affects
/rolecall.cfm , bbb_id
This is the rolecall.cfm code:
<cfscript>
if (isDefined("url") and isDefined("url.bbb_id")) {
if (url.dept_id eq -1)
_include("sql", "getB");
else
_include("sql", "getBNow");
}
/*...*/
_include("sql", "getDPlaces");
/*Set up the model and go*/
model = {
add = 1,
edit = 0,
remove = 0,
places = getDPlaces
};
</cfscript>
If you're using IIS, you should read this article to see how to add SQL Injection protection directly to the web server. This will keep attack requests from ever reaching ColdFusion.
Be cautious of the strings they suggest you deny:
<denyStrings>
<add string="--" />
<add string=";" />
<add string="/*" />
<add string="#" />
Make sure you never pass an email address as the value of a query string parameter, otherwise you'll reject a legitimate request. You can allow the # symbol if needed.
I would also highly suggest you take a look at HackMyCF, which will show you many other security concerns if they exist.
SQL Injection exploits databases by stuffing malicious sql commands into a query where they're not expected. Tricking the query into do something different than what it was designed to do, like performing a DROP or DELETE instead of a SELECT.
Queries that use raw client parameters like this, are vulnerable:
WHERE policy_funct_id = #url.dept_id#
Instead, always wrap client supplied parameters in cfqueryparam. It prevents them from being executed as a command. I don't know your column data types, so modify the cfsqltype as needed.
WHERE policy_funct_id = <cfqueryparam value="#url.dept_id#" cfsqltype="cf_sql_integer">
All of the dynamic table names are another (potential) vulnerability, like:
-- potential sql-injection risk
SELECT * FROM #db.root#
If #db.root# is user supplied, it's a sql-i risk. Unfortunately, cfqueryparam cannot be used on table names. Those must be manually (and carefully) validated.
Few other suggestions, unrelated to sql injection:
All the nested (select * from...) statements decrease readability. Instead, use a single level JOIN.
When using JOIN's, best to specify the source table (or table alias) for all columns. That avoids ambiguity and increases readability for yourself and anyone else reviewing the code. No need to guess which columns comes from which table.
Example
-- psuedo example
SELECT root.ColumnA
, root.ColumnB
, dept.ColumnC
, subcat.ColumnC
, etc...
FROM #db.root# root
INNER JOIN #db.content# content ON root.policy_root_id = content.content_id
INNER JOIN #db.dept# AS dept ON ON content.dept_id = dept.policy_funct_id
INNER JOIN #db.subcat# subcat ON subcat.dept_id = dept.policy_funct_id
WHERE dept.policy_funct_id = <cfqueryparam value="#url.dept_id#" cfsqltype="cf_sql_integer">
AND content.is_newest = 1

How to make LIKE in SQL look for specific string instead of just a wildcard

My SQL Query:
SELECT
[content_id] AS [LinkID]
, dbo.usp_ClearHTMLTags(CONVERT(nvarchar(600), CAST([content_html] AS XML).query('root/Physicians/name'))) AS [Physician Name]
FROM
[DB].[dbo].[table1]
WHERE
[id] = '188'
AND
(content LIKE '%Urology%')
AND
(contentS = 'A')
ORDER BY
--[content_title]
dbo.usp_ClearHTMLTags(CONVERT(nvarchar(600), CAST([content_html] AS XML).query('root/Physicians/name')))
The issue I am having is, if the content is Neurology or Urology it appears in the result.
Is there any way to make it so that if it's Urology, it will only give Urology result and if it's Neurology, it will only give Neurology result.
It can be Urology, Neurology, Internal Medicine, etc. etc... So the two above used are what is causing the issue.
The content is a ntext column with XML tag inside, for example:
<root><Location><location>Office</location>
<office>Office</office>
<Address><image><img src="Rd.jpg?n=7513" /></image>
<Address1>1 Road</Address1>
<Address2></Address2>
<City>Qns</City>
<State>NY</State>
<zip>14404</zip>
<phone>324-324-2342</phone>
<fax></fax>
<general></general>
<from_north></from_north>
<from_south></from_south>
<from_west></from_west>
<from_east></from_east>
<from_connecticut></from_connecticut>
<public_trans></public_trans>
</Address>
</Location>
</root>
With the update this content column has the following XML:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<Physicians>
<name>Doctor #1</name>
<picture>
<img src="phys_lab coat_gradation2.jpg?n=7529" />
</picture>
<gender>M</gender>
<langF1>
English
</langF1>
<specialty>
<a title="Neurology" href="neu.aspx">Neurology</a>
</specialty>
</Physicians>
</root>
If I search for Lab the result appears because there is the text lab in the column.
This is what I would do if you're not into making a CLR proc to use Regexes (SQL Server doesn't have regex capabilities natively)
SELECT
[...]
WHERE
(content LIKE #strService OR
content LIKE '%[^a-z]' + #strService + '[^a-z]%' OR
content LIKE #strService + '[^a-z]%' OR
content LIKE '%[^a-z]' + #strService)
This way you check to see if content is equal to #strService OR if the word exists somewhere within content with non-letters around it OR if it's at the very beginning or very end of content with a non-letter either following or preceding respectively.
[^...] means "a character that is none of these". If there are other characters you don't want to accept before or after the search query, put them in every 4 of the square brackets (after the ^!). For instance [^a-zA-Z_].
As I see it, your options are to either:
Create a function that processes a string and finds a whole match inside it
Create a CLR extension that allows you to call .NET code and leverage the REGEX capabilities of .NET
Aaron's suggestion is a good one IF you can know up front all the terms that could be used for searching. The problem I could see is if someone searches for a specific word combination.
Databases are notoriously bad at semantics (i.e. they don't understand the concept of neurology or urology - everything is just a string of characters).
The best solution would be to create a table which defines the terms (two columns, PK and the name of the term).
The query is then a join:
join table1.term_id = terms.term_id and terms.term = 'Urology'
That way, you can avoid the LIKE and search for specific results.
If you can't do this, then SQL is probably the wrong tool. Use LIKE to get a set of results which match and then, in an imperative programming language, clean those results from unwanted ones.
Judging from your content, can you not leverage the fact that there are quotes in the string you're searching for?
SELECT
[...]
WHERE
(content LIKE '%""Urology""%')

Error when displaying XML as text, with user selected parameters

In my report (BIRT 4.2) I am getting data from a SQL 2008 R2 data base. The client has asked to add a new field to the report. The field has xml data that is formatted as text (example data below). I can display it fine, in html, or Excel; BUT if I have a user selected parameter the report crashes (error message below). Works fine in previews (report & SQL).
I don’t think there is any xml as text in the sample data base, so not seeing how I can create an example using the sample data.
Having string parameters is fine, but as soon as I put the ‘?’ it crashes on deploy via Apache. The parameter is a date.
Works ; where OCMQM1.SUBMIT_DATE >= '2013-12-01'
Fails ; where OCMQM1.SUBMIT_DATE >= ?
I have tried casting the field varchar (1), etc no matter how many characters, or what allowable format I cast to, I get the same error. Even converting the xml to a single character of varchar and then replacing the character with a blank space in at the SQL give the same crash on deploy. I have recreated the report from scratch and modified the existing working report, with identical results.
, replace (
cast (OCMQM1.SVC_OPTIONS as varchar (1) )
, '<' , ' '
) as 'Request_Details'
Error message
The following items have errors:
Table (id = 1293):
+ Cannot execute the statement.
org.eclipse.birt.report.data.oda.jdbc.JDBCException: SQL statement does not return a ResultSet object.
SQL error #1:The value is not set for the parameter number 1.
;
com.microsoft.sqlserver.jdbc.SQLServerException: The value is not set for the parameter number 1. (Element ID:1293)
XML data example
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><form><text id="ExistingServerName" label="Server Name:" multiline="false" readonly="false">WINHPLNSPRD128</text><text id="ProjectFunding" label="How is this project funded?" button="4001" multiline="false" readonly="false">34542 - Enable HealthPlaNET Architecture to Support Multiple Customers</text><text id="ImplementationDate" label="Requested Implementation Date:" multiline="false" readonly="false">12/12/2013 00:00:00</text><text id="CPUNeeded" label="Additinal CPU Needed:" multiline="false" readonly="false">1</text><text id="MemoryNeeded" label="Additional Memory Needed (in GB):" multiline="false" readonly="false">0</text><text id="AdditionalStorage" label="How much additional disk storage is needed (in GB)?" multiline="false" readonly="false">0</text><text id="ExpandDrive" label="If this request is to expand an existing drive, what drive letter should be expanded?" multiline="false" readonly="false"></text></form>
Note The user supplied parameters work fine, when the xml is not part of the report. Adding the field to the SQL, even if not added to the report table, and regardless of placement in the SQL causes the error.

Using SQLXMLBulkLoad, getting exception because column does not accept NULL values

I am trying to bulk insert XML Data into SQL Server 2005 Express with the SQLXMLBulkLoad Object Model in VB.NET using Visual Studio 2010.
When the loader gets to an element in my .xml file which does not hold a value, it throws an error because the corresponding table column in SQL Server is set to not contain null values. This was by design and instead of inserting NULL I need for the Loader to insert a blank space. I do not know how to go about doing this as this is my first time working with BulkLoad. Any suggestions?
The error.xml file text:
<?xml version="1.0" ?>
<Result State="FAILED">
<Error>
<HResult>0x80004005</HResult>
<Description> <![CDATA[ No data was provided for column 'blah' on table 'blah', and this column cannot contain NULL values.]]></Description>
<Source>General operational error</Source>
<Type>FATAL</Type>
</Error>
</Result>
EDIT:
http://social.msdn.microsoft.com/Forums/en-US/sqlxml/thread/bfa31c49-6ae5-4a5d-bcde-cd520e0cdf70/
This guy had the exact same problem as I am having, and was able to solve it by using objBl.Transaction = True. However, when I try that, I get an error that "Cannot bulk load because the file "This is a Local Temp File" could not be opened."
I am answering this for future users with this same circumstance. Albeit illogical, if you set your columns in the SQL Table to accept NULL values AND set the Default Value = (''), then the blank values in your XML file will become blanks instead of NULL values and you will no longer receive this error. I couldn't use objBl.Transaction = True because my server is a different computer than the computer that would run the app to perform the BulkLoad(). This CAN be solved by setting up a shared folder, but it was not an option in my circumstance. Therefore, the next best option was to do the above.