创建可以使用不同表的存储过程

时间:2011-06-04 11:31:58

标签: tsql stored-procedures view

我需要对我的数据库中具有相同结构的许多表使用相同的存储过程。这是从客户加载的数据,有一个表/客户,数据需要计算/检查在加载到我们的DataWarehouse之前运行。

到目前为止,这些是我发现的选项和问题,我正在寻找更好的模式/方法。

  1. 创建一个指向的视图 我要处理的表,SP 然后谈谈那个观点。这有效 好吧(特别是一旦我解决了 如何'自动'创建视图 基于他们的专栏)。但是 视图只能与一个表一起使用 一次,迫使系统 一次与一位客户打交道。

  2. 在每个SP中使用动态sql -     使SP更加困难     读取/调试,并出于这些原因     被排除

  3. 创建一个分区视图     所有的表然后使用一个     paramatised table函数返回     只是我们感兴趣的数据 -     啊,但后来我无法更新数据     因为该函数返回一个表     只能用于选择

  4. 在函数内使用动态sql     (无法完成)创建视图     (这也无法做到)....给     达
  5. 在SP内创建临时表     使用目标表     动态sql,但然后是临时表     仅存在于运行的会话中     动态sql不是'父'     正在运行SP的会话......     放弃
  6. 使用创建全局临时表     动态SQL以避免范围问题     5,然后运行SP反对     全球临时表。仍然遇到     单个客户问题。
  7. 在a中创建视图,如1所示     事务然后运行所有SP     然后提交 - 适用于一个     用户,但现在阻止任何其他用户     试图创建一个新的视图     同名
  8. 使用临时视图...无法进入     T /的Sql
  9. 将所有代码移动到.Net - 但是     我们有环境问题     tsql更容易托管/运行
  10. 我知道我不是唯一有此问题的人,请各位好人解决,请帮忙。

5 个答案:

答案 0 :(得分:11)

也许你的方法是错误的,我会在一段时间内深入细节,但似乎你的问题可以用SSIS来解决

- 更新回答

首先,大局:

动态处理表的最经济的方法是使用脚本而不是存储过程。如果要随机选择表访问,则肯定不会使用存储过程的任何性能优势,即执行计划。 SQL脚本可以在运行时使用占位符轻松升级到指向一个表,并在执行之前替换它。

可以从文件系统,变量,表中的文本列等加载脚本。加载过程包括将脚本内容读取到字符串变量。此步骤发生一次。

下一步是准备阶段。将对要处理的每个表执行此步骤。此步骤的主要业务是使用正在处理的当前表替换表占位符。也可以设置参数值,就像您可能需要传递到已编写的sp中的任何参数一样。

最后一步是执行脚本。由于已经加载到变量中并且占位符设置为当前表名,因此可以使用sql变量作为输入安全地调用ExecuteSQLTask。当然,这个过程适用于您要处理的每个表。

确定。现在让我们看看这一点。

这是一个示例数据库模型:

CREATE TABLE [dbo].[t_n](
  [id] [int] IDENTITY(1,1) NOT NULL,
  [name] [varchar](50) NOT NULL,
  [start] [datetime] NULL,
  CONSTRAINT [PK_t_n] PRIMARY KEY CLUSTERED ([id] ASC)
) ON [PRIMARY]

其中t_n表示任何表(t_1,t_2,t_3等)。

这是您当前的存储过程:

CREATE PROCEDURE SpProcessT_n 
AS
BEGIN
    SET NOCOUNT ON;
    SELECT * FROM [t1]; 
END
GO

现在,将此存储过程转换为Sql脚本,放置占位符而不是表名

    SET NOCOUNT ON;
    SELECT * FROM [$table_name];

我选择将其保存在文件系统的.sql文件中,以使POC尽可能简单。

接下来,创建一个这样的SSIS包:

Basic SSIS Package

这些是我选择设置循环的设置: enter image description here

这就是你可以将表名分配给一个名为_table_name_的变量的方法 enter image description here

这是脚本任务的设置,在这里您发现变量_table_name_具有只读访问权限,而名为SqlExec的新变量具有读/写访问权限:

enter image description here

这是它的主要功能:

    public void Main()
    {
        String Table_Name = Dts.Variables["table_name"].Value.ToString();
        String SqlScript;
        Regex reg = new Regex(@"\$table_name", RegexOptions.Compiled);
        using (var f = File.OpenText(@"c:\sqlscript.sql")) {
            SqlScript = f.ReadToEnd();
            f.Close();
        }
        SqlScript = reg.Replace(SqlScript, Table_Name);
        Dts.Variables["SqlExec"].Value = SqlScript;
        Dts.TaskResult = (int)ScriptResults.Success;
    }

您可以注意到Dts Variable SqlExec包含将要执行的sql脚本。现在,您可以在ExecuteSqlTask​​中设置以下选项:

enter image description here

在MSSQL 2008中成功测试过,如果在脚本文件中放入一个插件,您会注意到每个表中的新行。

希望这有帮助!

答案 1 :(得分:2)

如果你的应用程序可以负担一个截止日,那么你可以安排一个夜间预定的工作来运行一个SSIS包,将所有150多个表合并到一个巨大的表中。由于针对该巨大表的查询结果的新鲜度将晚1'日期',因此该解决方案将不包括最近加载的任何行。

您可以实际运行此程序包。如果它仍然非常快,比如在30分钟内,那么你可以打赌每隔几个小时运行一次,比如:工作日开始,午休和一天结束。通过这种方式,您可以查询几乎全新的数据。

答案 2 :(得分:1)

写一个包含表名的分区视图?

SELECT 'TableName', t.* FROM TableName t
UNION ALL 
SELECT 'TableName2', t.* FROM TableName2 t

然后编写单个而不是触发器,它使用动态SQL进行编写(与使用动态SQL相关的测试较少,因为您只需为我认为的所有表编写一次简单的CRUD操作)

答案 3 :(得分:1)

我不会用SQL做这件事。您所描述的内容听起来像传统的ETL情况。

由于所有客户表都相同,我将在数据仓库中创建一个表,其中包含客户表中的所有列,代理键列和类型标识符。您可以选择在此处创建一个“临时”表,该表只在ETL过程中包含数据,或者只在单个“实时”表上工作。我会创建临时表。

然后在SSIS包中(不用担心你仍然可以从SQL Server代理安排,它还没有完全离开数据库服务器),启动ETL过程......

E (xtract):将数据从源复制到数据仓库中的临时表中。您最有可能希望在foreach循环中使用子包并更改要从外部存储处理的表的名称(大多数人会说将它放在仓库中,但由您决定)。

T (ransform):运行你正在谈论的计算/检查,但是在整套上进行...

L (oad):将其复制到数据仓库中的真实内容。

有几件事我不会做。      1.修改源表中的数据。      2.尝试在t-sql中执行此操作。它不是tsql擅长的。

如果您需要有关此方法的更多详细信息,我可能会问一些商业智能标记的问题。我将在接下来的一周左右旅行,但如果你需要我,我会尝试查看评论以清除任何问题。

答案 4 :(得分:0)

我很确定解决这个问题的标准方法是在每个sp(你的选项2)中使用动态SQL,这已经被排除了。

您的目标是制作通用的多表SQL。我没有看到你打算如何在不牺牲一些效率和可读性的情况下实现这一目标。