SSIS String variable size - sql

I would like to know what is the size limit on the SSIS string variable. I have a OLE DB data source that queries a column (alpha-numeric 10 digit) values and feeds the result set into a script task, the script task then creates a set based delete sql statement(i.e. DELETE FROM Table Where ID IN("all id's go here") and assigns the sql statement to a string variable, a third task (execute sql) then executes the sql from the variable. I am wondering if I get 10,000 values in the IN clause would that cause any issues with the string variable? Please advise

There is no limit on a string variable size .SSIS data types are derived from .net sub system so incase if you do have a very very large string value then you may run out of memory

One possible solution can be to put the ID in a temporary table in your destination database and then you can do a DELETE statement with a join between the temporary table and the table you are deleting from.
Or, you can rewrite your statement: DELETE FROM Table WHERE ID IN (SELECT ID FROM TempTable)

Related

SQL server query : how to check value within range

In application I am working on.
I have to input from user through excel and first put it in temporary sql table & then from temporary table to final target table.
My query is failing while putting data from temporary table to target table.
Because some values present in temporary table are out of range of columns in target table.
How can I check if values present in temporary table are within range of column of target table?
I have to check like this
20 < len(temporary_table.column1) < 50
or is there any better way
If you are using SQL server you can use below query for data checking.
temporary_table.column1 between 20 and 50
If you are looking based on the column max length. For example, your columns have datatype varchar(100) then you can use the condition like this
where len(temporary_table.column1)<=100
Extending on the above answer you can just use col_length instead of hard coding the value on the target column. This makes it more automated and less prone to mistakes (entering a value mistakenly)
where len(temporary_table.column1) <= COL_LENGTH ( 'target_table' , 'column1' )

Executing stored procedure in a loop until a condition is met using SSIS

I have a customers table whose fields have to be updated with recent changes and also an additional column in the same table which specifies if that particular row has been updated or not, I have a procedure that does this for me
CustomerID CustomerName Address Updated
1 abc xyz Yes
2 cdb tyy No
As you can see above , the second row hasn't been updated due to NO connection to the client server.Later when the connection to server is available, all I have to do is update the column value to blank and run the procedure again till the row is updated with any changes.
I have to flow this process in a loop until all rows in the table are updated.
Could someone please help me out to do this in SSIS
The following will allow your stored procedure run until all the rows have been updated. A couple important things to note, if it's possible that there will be one or more iterations where no rows in the table will have been updated, a condition, such as a T-SQL IF statement, will need to be added to the second Execute SQL Task to check for this and assign a value to the variable accordingly. Also, the parameter syntax (using ? for the parameter place holder) is for an OLE DB connection.
Add a For Loop container, in the EvalExpression field, select an int variable and set the condition as the variable not equal to 0, i.e. #[User::Count] != 0
Within the For Loop container, add an Execute SQL Task that executes the stored procedure
Add another Execute SQL Task linked to the first one and still inside the For Loop, that has a query which returns the count of rows from the table without the updated value that will be mapped to the variable from the For Loop, such as
select ? = count(*) from dbo.YourTable where Updated != 'Yes'
On the second Execute SQL Task, go to the Parameter Mapping pane, pick your variable in the Variable Name field, set the Direction to Output, Data Type to Long, and Parameter Name to 0. The Parameter size can remain at the default of -1.

Query a database based on result of query from another database

I am using SSIS in VS 2013.
I need to get a list of IDs from 1 database, and with that list of IDs, I want to query another database, ie SELECT ... from MySecondDB WHERE ID IN ({list of IDs from MyFirstDB}).
There is 3 Methods to achieve this:
1st method - Using Lookup Transformation
First you have to add a Lookup Transformation like #TheEsisia answered but there are more requirements:
In the Lookup you Have to write the query that contains the ID list (ex: SELECT ID From MyFirstDB WHERE ...)
At least you have to select one column from the lookup table
These will not filter rows , but this will add values from the second table
To filter rows WHERE ID IN ({list of IDs from MyFirstDB}) you have to do some work in the look up error output Error case there are 2 ways:
set Error handling to Ignore Row so the added columns (from lookup) values will be null , so you have to add a Conditional split that filter rows having values equal NULL.
Assuming that you have chosen col1 as lookup column so you have to use a similar expression
ISNULL([col1]) == False
Or you can set Error handling to Redirect Row, so all rows will be sent to the error output row, which may not be used, so data will be filtered
The disadvantage of this method is that all data is loaded and filtered during execution.
Also if working on network filtering is done on local machine (2nd method on server) after all data is loaded is memory.
2nd method - Using Script Task
To avoid loading all data, you can do a workaround, You can achieve this using a Script Task: (answer writen in VB.NET)
Assuming that the connection manager name is TestAdo and "Select [ID] FROM dbo.MyTable" is the query to get the list of id's , and User::MyVariableList is the variable you want to store the list of id's
Note: This code will read the connection from the connection manager
Public Sub Main()
Dim lst As New Collections.Generic.List(Of String)
Dim myADONETConnection As SqlClient.SqlConnection
myADONETConnection = _
DirectCast(Dts.Connections("TestAdo").AcquireConnection(Dts.Transaction), _
SqlClient.SqlConnection)
If myADONETConnection.State = ConnectionState.Closed Then
myADONETConnection.Open()
End If
Dim myADONETCommand As New SqlClient.SqlCommand("Select [ID] FROM dbo.MyTable", myADONETConnection)
Dim dr As SqlClient.SqlDataReader
dr = myADONETCommand.ExecuteReader
While dr.Read
lst.Add(dr(0).ToString)
End While
Dts.Variables.Item("User::MyVariableList").Value = "SELECT ... FROM ... WHERE ID IN(" & String.Join(",", lst) & ")"
Dts.TaskResult = ScriptResults.Success
End Sub
And the User::MyVariableList should be used as source (Sql command in a variable)
3rd method - Using Execute Sql Task
Similar to the second method but this will build the IN clause using an Execute SQL Task then using the whole query as OLEDB Source,
Just add an Execute SQL Task before the DataFlow Task
Set ResultSet property to single
Select User::MyVariableList as Result Set
Use the following SQL command
DECLARE #str AS VARCHAR(4000)
SET #str = ''
SELECT #str = #str + CAST([ID] AS VARCHAR(255)) + ','
FROM dbo.MyTable
SET #str = 'SELECT * FROM MySecondDB WHERE ID IN (' + SUBSTRING(#str,1,LEN(#str) - 1) + ')'
SELECT #str
If the column has string data type you should add quotation before and after values as below:
SELECT #str = #str + '''' + CAST([ID] AS VARCHAR(255)) + ''','
FROM dbo.MyTable
Make sure that you have set the DataFlow Task Delay Validation property to True
This is a classic case for using LookUp Transformation. First, use a OLE DB Source to get data from the first database. Then, use a LookUp Transformation to filter this data-set based on the ID values from the second data-set. Here is the steps for using a LookUp Transformation:
In the General tab, select Full Cash, OLE DB Connection Manager and Redirect rows to no match output as shown in the following picture. Notice that using Full Cash provides great performance for your package.
General Setting
In the Connection tab, use OLE DB Connection Manager to connect to your second server. Then, you can either directly select the data-set with ID values or (as is shown in the picture below) you can use SQL code to select the IDs from the filtering data-set.
Connection:
Go to Columns tab and select ID columns from the both datasets. For each record from your first data-set, it will check to see if its ID is in the Available LookUp Column. If it is, it will go to the Matching output, else to No Matching output.
Match ID columns:
Click on OK to close the LookUp. Then you need to select the LookUp Match Output.
Match Output:
The "best" answer depends on data volumes and source systems involved.
Many of the other answers propose building out a list of values based on clever concatenation within SQL Server. That doesn't work so well if the referenced system is Oracle, MySQL, DB2, Informix, PostGres, etc. There may be an equivalent concept but there might not be.
For best performance, you need to filter against the second db before any of those rows ever hit the data flow. That means adding a filtering condition, as the others have suggested, to your source query. The challenge with this approach is that your query is going to be limited by some practical bounds that I don't remember. Ten, one hundred, a thousand values in your where clause is probably fine. A lakh, a million - probably not so much.
In the cases where you have large volumes of values to filter against the source table, it can make sense to create a table on that server and truncate and reload that table (execute sql task + data flow). This allows you to have all of the data local and then you can index the filter table and let the database engine do what it's really good at.
But, you say the source database is some custom solution that you can't make tables in. You can look at the above approach with temporary tables and within SSIS you just need to mark the connection as singleton/persisted (TODO: look this up). I don't much care for temporary tables with SSIS as debugging them is a nightmare I'd not wish upon my mortal enemy.
If you're still reading, we've identified why filtering in the source system might not be "doable", even if it will provide the best performance.
Now we're stuck with purely SSIS solutions. To get the best performance, do not select the table name in the drop down - unless you absolutely need every column. Also, pay attention to your data types. Pulling LOB (XML, text, image (n)varchar(max), varbinary(max)) into the dataflow is a recipe for bad performance.
The default suggestion is to use a Lookup Component to filter the data within the data flow. As long as your source system supports and OLE DB provider (or you can coerce the data into a Cache Connection Manager)
If you can't use a Lookup component for some reason, then you can explicitly sort your data in your source systems, mark your source components as such, and then use a Merge Join of type Inner Join in the data flow to only bring in matched data.
However, be aware that sorts in source systems are going to be sorted according to native rules. I ran into a situation where SQL Server was sorting based on the default ASCII sort and my DB2 instance, running on zOS, provided an EBCDIC sort. Which was great when my domain was only integers but went to hell in a handbasket when the keys became alphanumeric (AAA, A2B, and AZZ will sort differently based on this).
Finally, excluding the final paragraph, the above assumes you have integers. If you're performing string matching, you get an extra level of ugliness because different components may or may not perform a case sensitive match (sorting with case sensitive systems can also be a factor).
I would first create a String variable e.g. SQL_Select, at the Scope of the Package. Then I would assign that a value using an Execute SQL Task against the 1st database. The ResultSet property on the General page should be set to Single row. Add an entry to the Result Set tab to assign it to your Variable.
The SQL Statement used needs to be designed to return the required SELECT statement for your 2nd database, in a single row of text. An example is shown below:
SELECT
'SELECT * from MySecondDB WHERE ID IN ( '
+ STUFF ( (
SELECT TOP 5
' , ''' + [name] + ''''
FROM dbo.spt_values
FOR XML PATH(''), TYPE).value('(./text())[1]', 'VARCHAR(4000)'
) , 1 , 3, '' )
+ ' ) '
AS SQL_Select
Remove the TOP 5 and replace [name] and dbo.spt_values with your column and table names.
Then you can use the variable SQL_Select in a downstream task e.g. an OLE DB Source against database 2. OLE DB Sources and OLE DB Command Tasks both let you specify a Variable as the SQL Statement source.
You could add a LinkedServer between the two servers. The SQL command would be something like this:
EXEC sp_addlinkedserver #server='SRV' --or any name you want
EXEC sp_addlinkedsrvlogin 'SRV', 'false', null, 'username', 'password'
SELECT * FROM SRV.CatalogNameInSecondDB.dbo.SecondDBTableName s
INNER JOIN FirstDBTableName f on s.ID = f.ID
WHERE f.ID IN (list of values)
EXEC sp_dropserver 'SRV', 'droplogins'

Jet engine (Access) : Passing a list of values to a stored procedure

I am currently writing a VBA-based Excel add-in that's heavily based on a Jet database backend (I use the Office 2003 suite -- the problem would be the same with a more recent version of Office anyway).
During the initialization of my app, I create stored procedures that are defined in a text file. Those procedures are called by my app when needed.
Let me take a simple example to describe my issue: suppose that my app allows end-users to select the identifiers of orders for which they'd like details. Here's the table definition:
Table tblOrders: OrderID LONG, OrderDate DATE, (other fields)
The end-user may select one or more OrderIDs, displayed in a form - s/he just has to tick the checkbox of the relevant OrderIDs for which s/he'd like details (OrderDate, etc).
Because I don't know in advance how many OrderID s/he will select, I could dynamically create the SQL query in the VBA code by cascading WHERE clauses based on the choices made on the form:
SELECT * FROM tblOrders WHERE OrderID = 1 OR OrderID = 2 OR OrderID = 3
or, much simpler, by using the IN keyword:
SELECT * FROM tblOrders WHERE OrderID IN (1,2,3)
Now if I turn this simple query into a stored procedure so that I can dynamically pass list of OrderIDs I want to be displayed, how should I do? I already tried things like:
CREATE PROCEDURE spTest (#OrderList varchar) AS
SELECT * FROM tblOrders WHERE OrderID IN (#OrderList)
But this does not work (I was expecting that), because #OrderList is interpreted as a string (e.g. "1,2,3") and not as a list of long values. (I adapted from code found here: Passing a list/array to SQL Server stored procedure)
I'd like to avoid dealing with this issue via pure VBA code (i.e. dynamically assigning list of values to a query that is hardcoded in my application) as much as possible. I'd understand if ever this is not possible.
Any clue?
You can create the query-statement string dynamically. In SQL Server you can have a function whose return value is a TABLE, and invoke that function inline as if it were a table. Or in JET you could also create a kludge -- a temporary table (or persistent table that serves the function of a temporary table) that contains the values in your in-list, one per row, and join on that table. The query would thus be a two-step process: 1) populate temp table with INLIST values, then 2) execute the query joining on the temp table.
MYTEMPTABLE
autoincrementing id
QueryID [some value to identify the current query, perhaps a GUID]
myvalue one of the values in your in-list, string
select * from foo
inner join MYTEMPTABLE on foo.column = MYTEMPTABLE.myvalue and MYTEMPTABLE.QueryId = ?
[cannot recall if JET allows ANDs in INNER JOIN as SQL Server does --
if not, adjust syntax accordingly]
instead of
select * from foo where foo.column IN (... )
In this way you could have the same table handle multiple queries concurrently, because each query would have a unique identifier. You could delete the in-list rows after you're finished with them:
DELETE FROM MYTEMPTABLE where QueryID = ?
P.S. There would be several ways of handling data type issues for the join. You could cast the string value in MYTEMPTABLE as required, or you could have multiple columns in MYTEMPTABLE of varying datatypes, inserting into and joining on the correct column:
MYTEMPTABLE
id
queryid
mytextvalue
myintvalue
mymoneyvalue
etc

How to send record(which is a weblink) from a table to the value of the variable in SSIS package and send that weblink through an email?

I have table called Table1 with columns, col1 and col2 with col1 having weblinks for the report and col2 the name of the report. Now, i have a package with a variables var1 and var2 which should get the col1 and col2 values respectively from table1 and send it through an email. if the weblink gets updated in the table, package should send the updated link. i know the reverse way of it but trying to do somethig like this.
Appreciate any help from you guys.
Thanks
There are a few ways you can do this. You can try one of the following based on whether you are processing one row or many rows.
One-Row Solution
If you are running the SSIS package so that it will process one and only one row from the table, you can use the Execute SQL Task to get the values from your table. Begin by adding your two variables
In the task, specify the connection to your SQL Server and enter a SQL statement like this.
SELECT col1, col2
FROM Table1
WHERE <your condition to retrieve one row>
Also, on the General tab, set the ResultSet value to Single row.
On the Result Set tab, add two elements. The first row should be Result Name of 0, Variable Name of Var1. The second row will be Result Name 1, Variable Name Var2.
When the task runs, the results of the SQL statement will be stored to the variables.
Multi-Row Solution
If the SSIS package must return multiple rows, then you need a different approach. First, you will need to create another variable that is an Object data type. The SQL results will be saved to this variable.
You will have an Execute SQL Task that is similar to the one above. However, your SQL statement should return all rows to be processed. The ResultSet property should be set to Full result set. On the Result Set tab, there should be one result set, with 0 as Result Name and your new object variable as the Variable Name. Close the Execute SQL Task editor.
Add a Foreach Loop Container. Edit the container and select the Collection tab. Set the Enumerator property to Foreach ADO Enumerator. Under the Enumerator configuration, set your object variable as the ADO object source variable.
On the Variable Mappings tab, add to rows. The first row will have a Variable Var1 and Index 0, and the second row will have Variable Var2 and Index 1. The Foreach Loop Container will process each for in the result set and assign the column values to your variable. You can add any tasks in the container and they will run once for each row in the result set.