SQL Azure:如何跨所有模式执行通用 DDL 脚本?
SQL Azure: How to execute a common DDL script across all schemas?
是否有一种内置方法可以跨所有模式执行通用 DDL 脚本?
我正在开发一个多租户应用程序,它为每个租户创建一个数据库模式。每个模式都包含每个租户的相同 table 定义。例如:
Schema named "tenant1" contains tables: tenant1.Users, tenant1.HistoryRecords, etc.
Schema named "tenant2" contains tables: tenant2.Users, tenant2.HistoryRecords, etc.
当我添加字段时,我希望将它添加到 tenant1 架构、tenant2 架构等中
初步想法:
我有一个 table,其中包含租户的架构名称和相关信息。我正在考虑向此 table 添加数据库版本字段以跟踪架构更改。然后我会创建一个存储过程,它接受 DDL 脚本和模式版本作为参数。
CREATE PROCEDURE UpdateSchema(DDLScript, InitialSchemaName, DbVersion)
@DDLScript nvarchar(5000),
@InitialSchemaName nvarchar(10),
@DbVersion nvarchar(5)...
该脚本将遍历一组模式,运行 每个模式的 DDL 脚本,将 InitialSchemaName 替换为当前循环的模式名称,如果成功则提交所有更改。
这是一个合理的前进计划,还是我错过了更通用的方法?
你必须对多租户环境格外小心。有很多简单的方法可以解决问题。正如您所指出的,您的 DDL 脚本必须按模式进行编辑,这意味着更改脚本中的对象名称。这很可怕,但我明白这是必要的。它引入了一个用于 SQL 注入的向量。
我希望你有一个或多个 "safe" 模式来测试。哎呀,我希望你有一个完整的测试 world 来测试。但是 - 撇开所有担忧不谈,这是我过去用来将更改应用到多模式环境的脚本脚手架:
create type dbo.ObjectNamesType as table ( Name sysname )
go
create procedure RunDDL( @scriptTemplate nvarchar( max ), @objName sysname, @objType nvarchar( 10 ), @schemas dbo.ObjectNamesType readonly )
as
begin
set nocount on
declare @script nvarchar( max )
declare @objectName nvarchar( 256 )
declare c cursor for
select N'[' + s.name + N'].[' + o.name + N']'
from
sys.objects o
inner join
@schemas s
on
o.schema_id = schema_id( s.Name )
where
o.name = @objName
and
o.type = @objType
open c
while ( 1=1 )
begin
fetch next from c into @objectName
if ( @@fetch_status != 0 ) break;
select @script = replace( @scriptTemplate, N'@objectName', @objectName )
exec sys.sp_executesql @script
end
close c
deallocate c
end
go
...并对其进行测试...
declare @script nvarchar( max )
declare @objName sysname
declare @objType sysname
declare @schemas dbo.ObjectNamesType
insert @schemas values( 'dbo' )
select @objName = 'someTable'
select @objType = 'u'
select @script = 'select * from @objectName' --> not really ddl, eh?
exec dbo.RunDDL @script, @objName, @objType, @schemas
我实际使用的那个要复杂得多-所以我只留下了多汁的部分。一些注意事项:
输入的设置方式使得脚本可以 运行 针对一组模式。这让您可以先 运行 它在您的测试模式上,看看它是否合适 - 假设您喜欢它,然后您可以 运行 它针对剩余的模式 en masse.
在我的世界中,@templateScript
、@objName
和 @objType
驻留在我加入的 table 中 - 但不会传入。我不会建议 运行 将这样的程序与外界的输入结合起来,因为这是对灾难的邀请……但对于 illustration/testing,它达到了目的。此外,在我的世界中,输入 table 有一个版本 ID 和一个序列。对于版本 x 的任何模式,我们按顺序 运行 所有脚本并假设成功,将该模式的版本升级到 y。每个脚本适用于单个对象。
这里的要点是,您需要将数据库更新作为常规工作流程 - 而此过程是该工作流程的核心。
请注意,它是从 sys.objects 中选择的,而不仅仅是 相信 输入。这只是防止您的脚本因名称拼写错误而吐槽的另一个小保护措施。如果我们编辑的对象数量与指定的对象数量不匹配,我们会为自己记录一个警告。
这种类型的过程还应该 try/catch 脚本的实际执行,并且应该记录它沿途尝试的所有内容。它应该回滚错误。确保您有足够的事务日志 space,因为即使是很小的 DDL 也有可能导致大量更改。
它通过指定的对象工作,将 @templateScript
编辑为 @script
变量,然后 运行 将 @script
变量与 sys.sp_executesql
.这样,它永远不会更改源变量,因此替换目标保持不变。
不幸的是,您不能为 tsql 对象名称使用变量,否则您可以减少注入攻击的面。因此,建议输入 table 而不是参数。这也意味着 SQL 无法真正对 parameterize/reuse statement/execution 计划做任何事情——但话又说回来,这不是 运行 数十亿次的东西,对吧?
是否有一种内置方法可以跨所有模式执行通用 DDL 脚本?
我正在开发一个多租户应用程序,它为每个租户创建一个数据库模式。每个模式都包含每个租户的相同 table 定义。例如:
Schema named "tenant1" contains tables: tenant1.Users, tenant1.HistoryRecords, etc.
Schema named "tenant2" contains tables: tenant2.Users, tenant2.HistoryRecords, etc.
当我添加字段时,我希望将它添加到 tenant1 架构、tenant2 架构等中
初步想法: 我有一个 table,其中包含租户的架构名称和相关信息。我正在考虑向此 table 添加数据库版本字段以跟踪架构更改。然后我会创建一个存储过程,它接受 DDL 脚本和模式版本作为参数。
CREATE PROCEDURE UpdateSchema(DDLScript, InitialSchemaName, DbVersion)
@DDLScript nvarchar(5000),
@InitialSchemaName nvarchar(10),
@DbVersion nvarchar(5)...
该脚本将遍历一组模式,运行 每个模式的 DDL 脚本,将 InitialSchemaName 替换为当前循环的模式名称,如果成功则提交所有更改。
这是一个合理的前进计划,还是我错过了更通用的方法?
你必须对多租户环境格外小心。有很多简单的方法可以解决问题。正如您所指出的,您的 DDL 脚本必须按模式进行编辑,这意味着更改脚本中的对象名称。这很可怕,但我明白这是必要的。它引入了一个用于 SQL 注入的向量。
我希望你有一个或多个 "safe" 模式来测试。哎呀,我希望你有一个完整的测试 world 来测试。但是 - 撇开所有担忧不谈,这是我过去用来将更改应用到多模式环境的脚本脚手架:
create type dbo.ObjectNamesType as table ( Name sysname )
go
create procedure RunDDL( @scriptTemplate nvarchar( max ), @objName sysname, @objType nvarchar( 10 ), @schemas dbo.ObjectNamesType readonly )
as
begin
set nocount on
declare @script nvarchar( max )
declare @objectName nvarchar( 256 )
declare c cursor for
select N'[' + s.name + N'].[' + o.name + N']'
from
sys.objects o
inner join
@schemas s
on
o.schema_id = schema_id( s.Name )
where
o.name = @objName
and
o.type = @objType
open c
while ( 1=1 )
begin
fetch next from c into @objectName
if ( @@fetch_status != 0 ) break;
select @script = replace( @scriptTemplate, N'@objectName', @objectName )
exec sys.sp_executesql @script
end
close c
deallocate c
end
go
...并对其进行测试...
declare @script nvarchar( max )
declare @objName sysname
declare @objType sysname
declare @schemas dbo.ObjectNamesType
insert @schemas values( 'dbo' )
select @objName = 'someTable'
select @objType = 'u'
select @script = 'select * from @objectName' --> not really ddl, eh?
exec dbo.RunDDL @script, @objName, @objType, @schemas
我实际使用的那个要复杂得多-所以我只留下了多汁的部分。一些注意事项:
输入的设置方式使得脚本可以 运行 针对一组模式。这让您可以先 运行 它在您的测试模式上,看看它是否合适 - 假设您喜欢它,然后您可以 运行 它针对剩余的模式 en masse.
在我的世界中,@templateScript
、@objName
和 @objType
驻留在我加入的 table 中 - 但不会传入。我不会建议 运行 将这样的程序与外界的输入结合起来,因为这是对灾难的邀请……但对于 illustration/testing,它达到了目的。此外,在我的世界中,输入 table 有一个版本 ID 和一个序列。对于版本 x 的任何模式,我们按顺序 运行 所有脚本并假设成功,将该模式的版本升级到 y。每个脚本适用于单个对象。
这里的要点是,您需要将数据库更新作为常规工作流程 - 而此过程是该工作流程的核心。
请注意,它是从 sys.objects 中选择的,而不仅仅是 相信 输入。这只是防止您的脚本因名称拼写错误而吐槽的另一个小保护措施。如果我们编辑的对象数量与指定的对象数量不匹配,我们会为自己记录一个警告。
这种类型的过程还应该 try/catch 脚本的实际执行,并且应该记录它沿途尝试的所有内容。它应该回滚错误。确保您有足够的事务日志 space,因为即使是很小的 DDL 也有可能导致大量更改。
它通过指定的对象工作,将 @templateScript
编辑为 @script
变量,然后 运行 将 @script
变量与 sys.sp_executesql
.这样,它永远不会更改源变量,因此替换目标保持不变。
不幸的是,您不能为 tsql 对象名称使用变量,否则您可以减少注入攻击的面。因此,建议输入 table 而不是参数。这也意味着 SQL 无法真正对 parameterize/reuse statement/execution 计划做任何事情——但话又说回来,这不是 运行 数十亿次的东西,对吧?