2

I have an SSIS package on server 1. It does a SQL query on SQL db located on server 2 via the OLEDB source sql command text. The query is:

SELECT * FROM PRODUCTS
WHERE PRODUCT_NAME IN (?)

This fails, since? is a scalar value and not a table. To fix this there are 2 options:

  1. Use STRING_SPLIT
  2. Create string split function

I can't use option 1 because although it is SQL server 2017, the DB compatibility level is set to 2008 (Level 100). STRING_SPLIT is supported only for a higher compatibility level. I'm not allowed to change this.

I can't use option 2 because I am not allowed to create any new custom functions on that database.

Is there a workaround? I have read about adding the custom function into the master DB, but unsure whether future SQL updates may reset it as user functions are not meant to be placed inside the master DB.

Hadi
  • 36,233
  • 13
  • 65
  • 124
variable
  • 8,262
  • 9
  • 95
  • 215
  • The reason it's not working is because `NOT IN (?)` would result in `NOT IN ('String1,String2, String3')` would would be equivalent to `= 'String1,String2,String3'`. You need need to split the string in the SQL. – Thom A Dec 28 '21 at 12:50
  • Does this answer your question? [Passing a varchar full of comma delimited values to a SQL Server IN function](https://stackoverflow.com/questions/878833/passing-a-varchar-full-of-comma-delimited-values-to-a-sql-server-in-function) – Stu Dec 28 '21 at 12:51
  • If you look at my execute SQL task, I have already split the strings such that the parameter have values NOT IN ('String1','String2', 'String3') – variable Dec 28 '21 at 12:52
  • Check your SQL again, @variable . `WHERE PRODUCT_NAME NOT IN (?)`. In that syntax the `?` represents a **scalar** value. – Thom A Dec 28 '21 at 12:59
  • Alas, [STRING_SPLIT requires the compatibility level to be at least 130](https://learn.microsoft.com/en-us/sql/t-sql/functions/string-split-transact-sql?view=sql-server-ver15) – Zohar Peled Dec 28 '21 at 13:48
  • Thanks for the help guys but I am in a tricky situation where I can neither use the STRING_SPLIT nor create a custom function to do the split. Any advise will be helpful. – variable Dec 28 '21 at 13:48
  • Instead of using a SQL command in the OLEDB Source - use a variable. In that variable - create the SQL code as needed, concatenating the values. Something like: "SELECT * FROM PRODUCTS WHERE PRODUCT_NAME IN (" + @[User::YourParameter] + ")" – Jeff Dec 28 '21 at 21:25

3 Answers3

4

One way would be switch context to a database that does have the required compatibility level (tempdb below).

DECLARE @ProductNames VARCHAR(MAX) = ? 

CREATE TABLE #ProductNames
(
PRODUCT_NAME VARCHAR(50) PRIMARY KEY
)

EXEC tempdb.sys.sp_executesql N'INSERT INTO #ProductNames SELECT DISTINCT value FROM STRING_SPLIT(@ProductNames, '','')',
                       N'@ProductNames VARCHAR(MAX)',
                       @ProductNames = @ProductNames;

SELECT *
FROM   PRODUCTS
WHERE  PRODUCT_NAME IN (SELECT pn.PRODUCT_NAME
                        FROM   #ProductNames pn)

DROP TABLE #ProductNames 
Martin Smith
  • 438,706
  • 87
  • 741
  • 845
  • It doesn't accept this inside the `OLEDB source sql command text`. It works in management studio. – variable Dec 29 '21 at 05:20
  • @variable - you have provided no details about what doesn't work You could try adding `IF 1= 0 SELECT * FROM PRODUCTS` to the top of the current script though. Possibly it is struggling to infer the metadata of the resultset at the moment – Martin Smith Dec 29 '21 at 14:57
  • When I click on the parameters button it given message as compile error – variable Dec 29 '21 at 17:05
  • @variable I don't have SSIS dev environment installed as haven't worked with that for years. Maybe try just `EXEC sys.sp_executesql` and adding a `use tempdb` to the start of the SQL Script run by that command – Martin Smith Dec 30 '21 at 06:28
1

STRING_SPLIT alternative

As mentioned in the following answer T-SQL split string, the following SQL query can replace the STRING_SPLIT function:

SELECT LTRIM(RTRIM(Split.a.value('.', 'VARCHAR(100)'))) 'Value' 
FROM  
(     
SELECT CAST ('<M>' + REPLACE(/*Comma separated value should be placed here*/, ',', '</M><M>') + '</M>' AS XML) AS Data            
) AS A 
CROSS APPLY Data.nodes ('/M') AS Split(a)

enter image description here

Using OLE DB Source SQL Command

If you are using an OLE DB Source component, you can use the following SQL Command:

DECLARE @String varchar(100) = ?;

SELECT * FROM PRODUCTS
WHERE PRODUCT_NAME IN (
SELECT LTRIM(RTRIM(Split.a.value('.', 'VARCHAR(100)'))) 'Value' 
FROM  
(     
SELECT CAST ('<M>' + REPLACE(@String , ',', '</M><M>') + '</M>' AS XML) AS Data            
) AS A 
CROSS APPLY Data.nodes ('/M') AS Split(a)
)

Test

Using the AdventureWorks2017 database, I used the following query to search for the person's information stored in the [Person].[Person] table while the filter is on the PersonType column:

DECLARE @String varchar(100) = ?;

SELECT * FROM [AdventureWorks2017].[Person].[Person]
WHERE PersonType IN (
SELECT LTRIM(RTRIM(Split.a.value('.', 'VARCHAR(100)'))) 'Value' 
FROM  
(     
SELECT CAST ('<M>' + REPLACE(@String , ',', '</M><M>') + '</M>' AS XML) AS Data            
) AS A 
CROSS APPLY Data.nodes ('/M') AS Split(a)
)

enter image description here

In the OLE DB Source Editor, if we click on the Parameters button, a parameter is recognized. I will create a new variable and use it as a parameter as shown in the image below:

enter image description here

The variable data type should be set to String and the value is set to EM,SC which are both symbols used in the PersonType column.

enter image description here

Now, if we click on the Preview button in the OLE DB Source Editor, the accurate data is visualized.

enter image description here

Hadi
  • 36,233
  • 13
  • 65
  • 124
  • 1
    Thanks. This works, can I ask whether there is any way to place the results of the split into a temporary table and then use the temp table inside the IN clause? When I try it I get an error - metadata could not be determined because statement ... uses a temp table – variable Dec 30 '21 at 04:50
  • 1
    @variable check this solution: https://social.msdn.microsoft.com/Forums/SqlServer/en-US/8674dddf-730a-4f31-8c1e-d9410337d6f4/temp-table-not-able-to-be-recognized-in-ssis-package?forum=sqlintegrationservices – Hadi Dec 30 '21 at 08:29
  • That's to get data from temp table, where as what I wanted to ask you was to insert data into temp table as part of the command text. – variable Dec 30 '21 at 08:31
  • 1
    @variable You should split the statement into 2 parts: inserting data into a temp table will be done in an Execute SQL Task while reading it is done in the OLE DB Source. – Hadi Dec 30 '21 at 08:44
0

You can load you string into an parameter object @ProductNames and then use that as a source and then inner join the product table in another data flow.

1st DF: Script Component Source with @stringToParse as a variable. Also have output string in DF of ProductName.

Code:

string prods = Variables.stringToParse.Value;
string[] productNames = prods.Split(',');
foreach(string p in productNames)
{
    OutputBuffer0.AddRow();
    OutputBuffer0.ProductName = p;
}

Then load the results into a recordset destination mapped to @ProductNames.

2nd DF: 2 sources.

  1. Product Table
  2. Your recordset @ProductNames

do a merge join (inner) and you will have your records.

KeithL
  • 5,348
  • 3
  • 19
  • 25