我是T-SQL的新手;我的所有经验都在完全不同的数据库环境(Openedge)中。我已经学会了写下面的程序 - 但也足以知道我不够了解!
这个例程很快就会进入一个现场环境,但它确实有效,但我确信它中有许多c ** k-ups和陷阱,我一无所知。
例程将数据从表A复制到表B,替换表B中的数据。表可以在任何数据库中。我计划从另一个存储过程多次调用此例程。权限不是问题:例程将由dba作为定时作业运行。
我是否可以就如何使其符合最佳做法提出建议?防弹吗?
ALTER PROCEDURE [dbo].[copyTable2Table]
@sdb varchar(30),
@stable varchar(30),
@tdb varchar(30),
@ttable varchar(30),
@raiseerror bit = 1,
@debug bit = 0
as
begin
set nocount on
declare @source varchar(65)
declare @target varchar(65)
declare @dropstmt varchar(100)
declare @insstmt varchar(100)
declare @ErrMsg nvarchar(4000)
declare @ErrSeverity int
set @source = '[' + @sdb + '].[dbo].[' + @stable + ']'
set @target = '[' + @tdb + '].[dbo].[' + @ttable + ']'
set @dropStmt = 'drop table ' + @target
set @insStmt = 'select * into ' + @target + ' from ' + @source
set @errMsg = ''
set @errSeverity = 0
if @debug = 1
print('Drop:' + @dropStmt + ' Insert:' + @insStmt)
-- drop the target table, copy the source table to the target
begin try
begin transaction
exec(@dropStmt)
exec(@insStmt)
commit
end try
begin catch
if @@trancount > 0
rollback
select @errMsg = error_message(),
@errSeverity = error_severity()
end catch
-- update the log table
insert into HHG_system.dbo.copyaudit
(copytime, copyuser, source, target, errmsg, errseverity)
values( getdate(), user_name(user_id()), @source, @target, @errMsg, @errSeverity)
if @debug = 1
print ( 'Message:' + @errMsg + ' Severity:' + convert(Char, @errSeverity) )
-- handle errors, return value
if @errMsg <> ''
begin
if @raiseError = 1
raiserror(@errMsg, @errSeverity, 1)
return 1
end
return 0
END
谢谢!
答案 0 :(得分:3)
我在这里从Sybase的角度讲(我不确定你是使用SQLServer还是Sybase),但我怀疑你会在任何一个环境中发现同样的问题,所以这里......
首先,我回应一下前面答案中关于假设的表的dbo所有权的评论。
然后我会向你的DBA核实这个存储过程将被授予删除tempdb以外的任何数据库中的表的权限。根据我的经验,DBA讨厌这一点,很少将其作为一种选择,因为可能存在灾难。
如果已使用选项sp_dboption my_database, "ddl in tran", true
配置数据库,则只允许在事务中使用drop table等DDL操作。一般来说,在涉及DDL的事务中完成的事情应该非常短,因为它们会锁定频繁引用的系统表,如sysobjects,这样做会阻止其他dataserver进程的进度。鉴于我们无法知道需要复制多少数据,它最终可能会成为一个非常长的事务,会在一段时间内为每个人锁定事物。更重要的是,DBA需要在每个包含可能包含此存储过程的'@Target'表的表的数据库上运行该命令。如果您要使用drop table
的交易,最好将其与处理数据插入的任何交易分开。
如果设置了drop table
选项,您可以在事务中执行ddl in tran
命令,但无法在事务中执行select * into
。由于select * into
是表创建与插入的组合,如果它在事务中执行,它将隐式锁定数据库(如果有大量数据,可能会暂时锁定)。
如果你的@target表有外键约束,你将无法在不首先删除外键约束的情况下删除它。
如果您的“id”列依赖于数字标识类型(通常用作自动编号功能来生成代理主键的值),请注意您将无法复制“@Source”表的“id”列中的值到“@Target”表的id列。
我还会检查任何可能存在与任何可能的“@Source”表大小相关的“@Target”表的数据库中的事务日志的大小。鉴于所有复制都是在单个事务中完成的,您可能会发现自己复制的表太大,以至于它会破坏prod dataserver中的事务日志,从而导致所有进程停止运行。我已经看到人们使用分块来实现特别大的表,但最后你需要将自己的检查放入代码中,以确保你实际捕获了表的一致快照。
< / LI>只是一个想法 - 如果用于获取快照,BCP怎么样?这可以用来转储表格的内容,为您提供您正在寻找的快照。如果你使用-c选项,你甚至可以用人类可读的形式获得它。
一切顺利, 斯图尔特
答案 1 :(得分:1)
这条线似乎有点危险:
set @dropStmt = 'drop table ' + @target
如果目标表不存在怎么办?
我试图以某种方式保护 - 比如:
set @dropStmt =
'if object_id(' + @target + ') IS NOT NULL DROP TABLE ' + @target
这样,只有当对OBJECT_ID(tablename)
的调用没有返回NULL(这意味着:表不存在)并且保证表存在时,才发出DROP TABLE
语句。
答案 2 :(得分:0)
首先,替换所有代码,如
set @source = '[' + @sdb + '].[dbo].[' + @stable + ']'
代码如
set @source = QuoteName(@sdb) + '.[dbo].' + QuoteName(@stable)
其次,您的程序假设所有对象都归dbo所有 - 可能不是这种情况。
第三,你的变量名太短,不超过30个字符 - 128是sysname的长度。
答案 3 :(得分:0)
我发现你写的整个过程非常危险。即使这是从数据库运行而不是由用户运行,动态SQL也是一种糟糕的做法!在使用它的数据库中,任何时候都可以对任何表执行此操作是危险的,并且在我使用的数据库中将禁止进出。不小心丢掉错误的桌子太容易了!也不可能正确地测试sp可以运行的所有可能的值,因此这也可能是错误的代码,并且在它生产之前你不会知道。
此外,在使用select into删除和重新创建时,您不能拥有索引或feoriegn键约束或您需要具备的性能和数据完整性。 BAD BAD IDEA一般来说(好吧,如果这些只是某种类型的暂存表而不是其他任何东西)。
这是SSIS的任务。我们保存我们的SSIS包,并像其他一样将它们提交给Subversion。我们可以对它们进行区分(它们只是XML文件),我们可以告诉我们在prod上运行什么以及我们正在使用什么配置。
除非表格相对较小,否则不应删除并重新创建表格。您应该更新现有记录,删除不再需要的记录,并且只添加新记录。如果你有一百万条记录,27000条已经改变,10条已被删除,3000条是新的,为什么要删除并插入所有1,000,000条记录。它浪费了服务器资源,可能导致锁定和阻塞问题,并且如果用户在您运行此表时查看表并且数据突然消失并且需要几分钟才能返回,则可能会产生问题。用户对此表示不满。