我在VS 2013中使用SSIS。
我需要从1个数据库中获取ID列表,并且使用该ID列表,我想查询另一个数据库,即SELECT ... from MySecondDB WHERE ID IN ({list of IDs from MyFirstDB})
。
答案 0 :(得分:11)
有3种方法可以实现这一目标:
首先你必须添加Lookup Transformation
来回答@TheEsisia,但还有更多要求:
在查找中,您必须编写包含ID列表的查询(例如:SELECT ID From MyFirstDB WHERE ...
)
至少你必须从查询表中选择一列
要过滤行WHERE ID IN ({list of IDs from MyFirstDB})
,您必须在查找错误输出Error case
中执行一些工作,有两种方法:
Ignore Row
,以便添加的列(来自查找)值为空,因此您必须添加Conditional split
来过滤具有等于NULL的值的行。 假设您已选择col1
作为查阅列,因此您必须使用类似的表达式
ISNULL([col1]) == False
Redirect Row
,因此所有行都将被发送到错误输出行,这些行可能不会被使用,因此数据将被过滤此方法的缺点是在执行期间加载和过滤所有数据。
此外,如果在加载所有数据后在本地计算机(服务器上的第二个方法)上进行网络过滤,则是内存。
为了避免加载所有数据,您可以执行一种解决方法,您可以使用脚本任务来实现此目的:(在VB.NET中回答)
假设连接管理器名称为TestAdo
,"Select [ID] FROM dbo.MyTable"
是获取id列表的查询,而User::MyVariableList
是您要存储列表的变量的id
注意:此代码将从连接管理器中读取连接
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
User::MyVariableList
应该用作源(变量中的Sql命令)
与第二种方法类似,但这将使用Execute SQL Task
构建IN子句,然后将整个查询用作OLEDB Source
,
ResultSet
属性设置为single
User::MyVariableList
作为结果集使用以下SQL命令
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
确保您已将DataFlow Task
Delay Validation
媒体资源设为True
答案 1 :(得分:5)
这是使用LookUp Transformation
的经典案例。首先,使用OLE DB Source
从第一个数据库中获取数据。然后,使用LookUp Transformation
根据第二个数据集中的ID
值过滤此数据集。以下是使用LookUp Transformation
:
General
标签中,选择Full Cash
,OLE DB Connection Manager
和Redirect rows to no match output
,如下图所示。请注意,使用Full Cash
可以为您的软件包提供出色的性能。常规设置
答案 2 :(得分:1)
我首先要创建一个String变量,例如SQL_Select,在包的范围内。然后我将使用执行SQL任务为第一个数据库分配一个值。 常规页面上的 ResultSet 属性应设置为单行。在结果集标签中添加一个条目,将其分配给您的变量。
使用的SQL语句需要设计为在单行文本中为第二个数据库返回所需的SELECT语句。示例如下所示:
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
删除 TOP 5 ,并将 [name] 和 dbo.spt_values 替换为您的列名和表名。
然后,您可以在下游任务中使用变量SQL_Select,例如OLE DB源数据库2. OLE DB源和OLE DB命令任务都允许您将变量指定为SQL语句源。
答案 3 :(得分:1)
“最佳”答案取决于所涉及的数据量和源系统。
许多其他答案建议根据SQL Server中的智能连接构建值列表。如果引用的系统是Oracle,MySQL,DB2,Informix,PostGres等,那么效果不会很好。可能是一个等效的概念,但可能不。
为了获得最佳性能,您需要在任何这些行访问数据流之前对第二个db进行过滤。这意味着将其他人建议的过滤条件添加到源查询中。这种方法的挑战在于您的查询将受到一些我不记得的实际界限的限制。你的where子句中的十,一百,一千个值可能没问题。十万,一百万 - 可能不是那么多。
如果您有大量值要对源表进行筛选,则可以在该服务器上创建表并截断并重新加载该表(执行sql task + data flow)。这允许您将所有数据都置于本地,然后您可以索引过滤器表并让数据库引擎执行它真正擅长的操作。
但是,你说源数据库是一些你无法创建表的自定义解决方案。你可以使用临时表查看上述方法,在SSIS中你只需要将连接标记为singleton / persisted(TODO:看看这个)。我不太关心SSIS的临时表,因为调试它们是我不希望我的死敌的噩梦。
如果您还在阅读,我们已经确定了为什么源系统中的过滤可能不“可行”,即使它可以提供最佳性能。
现在我们坚持使用纯粹的SSIS解决方案。要获得最佳性能,请不要在下拉列表中选择表名 - 除非您绝对需要每列。另外,请注意您的数据类型。将LOB(XML,text,image(n)varchar(max),varbinary(max))拉入数据流是导致性能不佳的一个因素。
默认建议是使用查找组件过滤数据流中的数据。只要您的源系统支持和OLE DB提供程序(或者您可以将数据强制转换为Cache Connection Manager)
如果由于某种原因无法使用Lookup组件,那么您可以在源系统中显式排序数据,标记源组件,然后在数据流中使用内部连接类型的合并连接只引入匹配的数据。
但是,请注意,源系统中的排序将根据本机规则进行排序。我遇到了SQL Server基于默认ASCII排序进行排序的情况,我在zOS上运行的DB2实例提供了EBCDIC排序。当我的域名只是整数但是在键盘变成字母数字时会在一个手提篮中下地狱时很棒(AAA,A2B和AZZ会根据这个不同排序)。
最后,排除最后一段,上面假设你有整数。如果您正在执行字符串匹配,则会获得额外的丑陋程度,因为不同的组件可能会或可能不会执行区分大小写的匹配(使用区分大小写的系统排序也可能是一个因素)。
答案 4 :(得分:0)
您可以在两台服务器之间添加LinkedServer。 SQL命令将是这样的:
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'