从另一个存储过程

时间:2018-01-31 22:40:04

标签: sql-server tsql stored-procedures

来自自学成才的数据仓库人员的第一篇文章。我已经做了大量的搜索和阅读以获得我现在的位置,但是无法超越这个难点。

背景:作为我们夜间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人员正在“调查”复制失败的本质。

2 个答案:

答案 0 :(得分:0)

试试这个...

使用 EXEC ..sp_rename '..', ''

答案 1 :(得分:0)

USE sourcedb
EXEC targetdatabase..sp_rename 'schema.oldtable', 'target_table'