我正在使用OLEDB源来调用存储过程来获取一组行,每行都标记为I
U
或D
,方法是在三个SELECT语句之间执行UNION存储过程。
A是一个填充了每晚从旧系统带来的数据的表。 B是我们希望用遗留系统数据更新的系统数据。
SET NOCOUNT ON;
-- entries in A that dont exist in B need to be inserted into B
SELECT
A.ID,
B.ID,
A.data1,
A.data2,
A.data3,
A.data4,
F1.ID as Fkey1,
F2.ID as Fkey2,
'I' as InsertUpdateDeactivateFlag
FROM A
LEFT JOIN B ON A.val = B.val
LEFT JOIN F1 ON A.f1val = F1.val
LEFT JOIN F2 ON A.f2val = F2.val
WHERE B.ID is null
UNION ALL
-- entries in A that do exist in B that have different field values
SELECT
A.ID,
B.ID,
A.data1,
A.data2,
A.data3,
A.data4,
F1.ID as Fkey1,
F2.ID as Fkey2,
'U' as InsertUpdateDeactivateFlag
FROM A
INNER JOIN B ON A.val = B.val
LEFT JOIN F1 ON A.f1val = F1.val
LEFT JOIN F2 ON A.f2val = F2.val
WHERE
A.data1 <> B.data1 OR
A.data2 <> B.data2 OR
A.data3 <> B.data3 OR
A.data4 <> B.data4 OR
F1.ID <> B.Fkey1 OR
F2.ID <> B.Fkey2
UNION ALL
-- entries in B that dont exist in A should have Active set to 0
SELECT
A.ID,
B.ID,
B.data1,
B.data2,
null as data3, -- dont need this value for deactivates
null as data4, -- dont need this value for deactivates
B.Fkey1,
B.Fkey2,
'D' as InsertUpdateDeactivateFlag
FROM A
RIGHT JOIN B ON A.val = B.val
WHERE A.ID is null
然后我正在使用该标志进行条件分割,以将行定向到三个OLEDB命令之一,该命令执行每行的存储过程(插入,更新或停用)。这三个程序写入表B.
如果只有几十行通过我的SSIS包,它可以正常工作。但是,当我们消灭表B并重新填充(几十万个插入)时,程序包会挂起。它只是挂起,没有错误信息,没有失败。 OLEDB源读取除了最后几行(每次大约700次,每次都是相同的数字)以外的所有行,通过流的其余部分提供行,然后挂起。我的同事说它实际上在75分钟后完成了。
存储过程在SSMS中完美运行,并且每次都会在几秒钟内返回整个结果集。除了在SSIS之外从未有任何问题。
现在奇怪的是,如果我将第三个SELECT语句(去激活)从最后一个结果集移动到union再到union的第一个结果集,那么一切正常并且包在大约3分钟内执行!咦?!
另外,如果我没有在停用时进行连接,只返回其中一个表中的所有行,它也可以正常工作。为什么SSIS会关心这次加入,或者它怎么会知道呢?我也尝试将UNION包装在外部SELECT中无济于事。
我们已经检查了SQL Server Profiler,查询正在执行,没有锁等等。据我所知,SSIS在开始将行放入管道之前已经从OLEDB源获取了所有数据。因此,写入我在SQL Server上选择的同一个表所引起的并发问题应该不是问题,因为在程序包开始处理之前已经完成了读取(如果这是错误的,请纠正我)。
当UNION处于特定顺序时,就好像SSIS正在分析存储过程。是这样的吗?有没有人遇到类似的情况?或者有人可以对这个过程有所了解,看看我应该在哪里寻找这个问题?
在实际存储过程中,SELECT项中有CASE语句,WHERE子句项上有ISNULL(),WHERE子句项和SELECT项中有其他UDF调用,以防万一。
感谢。
答案 0 :(得分:1)
我遇到的几个问题是,存储过程在SSMS中正常运行并且在外部执行非常严重是由parameter sniffing引起的。
答案 1 :(得分:1)
我会尝试两件事:
(1)在制作此SQL语句的OLE DB组件的属性窗口中,将ValidateExternalMetadata设置为False(远射但尝试)。
(2)制作3个OLEDB组件源并使用SSIS Union组件。我认为SSIS希望你这样做,坦率地说,我通常使用组件而不是试图让UNION在一个OLEDB组件源中工作。
答案 2 :(得分:0)
听起来你看过我推荐的大部分内容。在这种情况下,我通常会责怪grelmins,建立一个解决方案(例如你将第三个查询作为第一个),并继续前进。
您在75分钟内检查了系统中存在哪些锁?这可能表明当SSIS试图在后续任务中“跳过”时数据是否仍在被拉动。
根据键和基数,将第三个查询更改为“不存在”形式可能更有效 - 尽管这并不能解释当前行为。 (太,右外连接让我感到紧张 - 你可以像其他人一样尝试左外连接。)
-- entries in B that dont exist in A should have Active set to 0
SELECT
A.ID,
B.ID,
B.data1,
B.data2,
null as data3, -- dont need this value for deactivates
null as data4, -- dont need this value for deactivates
B.Fkey1,
B.Fkey2,
'D' as InsertUpdateDeactivateFlag
FROM B
WHERE NOT EXISTS (SELECT 1 FROM A WHERE val = B.val)
答案 3 :(得分:0)
由于SSIS可能会在收到行时开始处理,我的猜测是无法处理其中一个停用行,因为联合中的其他一个语句已经抓取该行(或创建它)。 / p>
您是否尝试过订购UNION中的行?请记住,如果没有ORDER BY,更改语句的顺序可能会改变接收和处理行的顺序。
答案 4 :(得分:0)
你在SSIS做重要的工作吗?
难道你不能将数据并行输入SSIS中的UNION吗?我知道你有UNION ALL,但是SQL Server执行它并不是特别的。另外,为什么UNION如果再次分裂呢?
好像你可以制作三个独立的表值UDF,只需:
INSERT INTO B
SELECT *
FROM udf_INSERTS()
UPDATE B
SET whatever
FROM B
INNER JOIN udf_UPDATES() AS u
ON u.key = B.key
DELETE
FROM B
WHERE B.key IN (SELECT key FROM udf_DELETES())
全部直接SQL。
在SSIS中调用SP进行单独插入/更新的速度不是很快。通常,我喜欢流式传输到表,然后使用SQL语句来执行set操作。
在你的情况下,SSIS正在做很多擅长的工作(枢轴,排序,聚合,查找,异构数据,脚本,验证)并不是那么清楚(你在SSIS之外工作,然后在SSIS中分裂)并且为结果行调用单个SP),所以我只是把它扔出去了。