来自自学成才的数据仓库人员的第一篇文章。我已经做了大量的搜索和阅读以获得我现在的位置,但是无法超越这个难点。
背景:作为我们夜间ETL工作的一部分,我们必须将许多远程数据库(链接服务器)中的许多表复制到临时区域数据库中。表格副本完成后,我继续从暂存区域DB转换为生产表。
由于远程数据库都具有相同的模式,因此我在生产数据库中创建了一个存储过程来完成工作。存储过程接受远程数据库名称和表名称的参数。在夜间作业中,SQL Server代理运行SSIS包;该包包含每个远程数据库的一个(重试循环)SSIS任务;所有任务同时运行;每个任务使用一个变量将DB名称传递给SQL文件;然后SQL文件为每个表调用一次存储过程。
示例远程表和本地登台区域表:
远程: [FLTA]。[cstone]。[csdbo]。[客户端]
本地: [FLTAL] .dbo。[FLTA客户端]
存储过程非常简单,删除旧表并使用SELECT从远程数据库创建一个新副本。它看起来大概是这样的:
CREATE PROCEDURE dbo.spTableCopyNew
(@p VARCHAR(50), @Tablename VARCHAR(50))
AS
-- Drop the existing table
EXEC('IF OBJECT_ID(''[' + @p + 'L].dbo.[' + @p + ' ' + @Tablename +']'', ''U'') IS NOT NULL
DROP TABLE [' + @p + 'L].dbo.[' + @p + ' ' + @Tablename +']'
)
-- Copy the new table
EXEC('SELECT * into [' + @p + 'L].dbo.[' + @p + ' ' + @Tablename +']
FROM [' + @p + '].[cstone].[csdbo].[' + @Tablename +']'
)
GO
SQL看起来大致如下:
-- Set local variables for the remote server connection, the local database name, and the table prefix
DECLARE @Prefix varchar(50)
-- Accept the variables passed in from the SSIS task
SET @Prefix = ?
-- Copy the two tables
EXEC Datawarehouse.dbo.spTableCopy @Prefix, 'CLIENT'
EXEC Datawarehouse.dbo.spTableCopy @Prefix, 'PATIENT'
维护非常简单:当我们需要从所有远程数据库中获取新表时,我只需将其添加到“productionLoad.sql”文件中。
它的效果非常好......除非没有。
由于尚未弄清楚原因,有时候桌子无法复制。而且由于我在复制新表之前丢弃了现有的表,这有时会使事情进一步破坏。我的SSIS任务每个远程数据库最多重试三次,因此偶尔的失败并不是什么大问题。但如果同一个远程数据库在一个晚上有三次失败,我将度过一段美好时光。
我目前尝试解决方案是将远程表复制到临时表,然后仅在该复制成功后,删除本地表并将临时表重命名为“真实”表。这让我想到了这个问题:
从存储过程调用时,我无法使sp_rename
工作,重命名存储在与存储过程不同的数据库中的表。我已经创建了新的变量来解析表达式,然后将这些变量发送到sp_rename
,因为我无法将表达式传递给该存储过程。
这是我尝试新的存储过程:
CREATE PROCEDURE dbo.spTableCopy
(@p VARCHAR(50), @Tablename VARCHAR(50))
AS
BEGIN
EXEC('USE [' + @p + 'L]')
-- Create variables for schema and table names
-- Since sp_rename can accept variables, but not expresssions containing variables.
DECLARE @RemoteTable VARCHAR(50) = '[' + @p + '].[cstone].[csdbo].[' + @Tablename +']'
DECLARE @LocalTableTemp VARCHAR(50) = '[' + @p + 'L].dbo.[' + @p + ' ' + @Tablename +'_temp]'
DECLARE @LocalTable VARCHAR(50) = '' + @p + ' ' + @Tablename + ''
-- Check for previous temp table and drop it
EXEC('IF OBJECT_ID(''[' + @p + 'L].dbo.[' + @p + ' ' + @Tablename +'_temp]'', ''U'') IS NOT NULL
DROP TABLE [' + @p + 'L].dbo.[' + @p + ' ' + @Tablename +'_temp]'
)
-- Copy the new table
EXEC('SELECT * into ' + @LocalTableTemp + '
FROM ' + @RemoteTable + ''
)
-- Drop the existing table
EXEC('IF OBJECT_ID(''[' + @p + 'L].dbo.[' + @p + ' ' + @Tablename +']'', ''U'') IS NOT NULL
DROP TABLE [' + @p + 'L].dbo.[' + @p + ' ' + @Tablename +']'
)
-- Rename temp table to real table
EXEC sp_rename @LocalTableTemp, @LocalTable
END
GO
这在将其作为普通SQL代码执行时都有效,但当我将其作为存储过程时,sp_rename
失败(其他一切正常)。最终表格[FLTAL CLIENT_temp]
在那里并包含正确的数据。
sp_rename
返回以下错误:
Msg 290,Level 16,State 2,Procedure sp_rename,Line 318
使用对象“Object”的无效EXECUTE语句,方法“LockMatchID”。
我用这种方式打了太久。
我只是搞砸了语法?
我可以让sp_rename
使用“USE”来处理其他数据库吗?
如果没有,如果我在每个临时区域DB中复制sp_tableCopy
,它会起作用吗?
如果没有,即使我多次同时调用此存储过程,也会在此存储过程中进行catch-try工作?
我还可以做些什么来从失败的表副本中恢复?
我还没有追求的替代解决方案:在成功创建临时表之后,TRUNC现有表并将临时表中的所有内容插入到真实表中。这看起来很麻烦。
P.S。我们的IT人员正在“调查”复制失败的本质。
答案 0 :(得分:0)
试试这个...
使用
EXEC ..sp_rename '..', '
答案 1 :(得分:0)
USE sourcedb
EXEC targetdatabase..sp_rename 'schema.oldtable', 'target_table'