2015-04-28 83 views
1

我想使用一些动态SELECT语句。表或列的名称可能是不同的,这就是为什么我使用动态存储过程是这样的:SQL Server:动态函数或重用存储过程

CREATE PROCEDURE [dbo].[test_sp](
    @database nvarchar(70)= '' 
    , @table_name nvarchar(70)= '' 
    , @column nvarchar(70)= '' 
) 
AS 
    DECLARE @sql nvarchar(max)= 'SELECT ' + @column + 
           'FROM [' + @database + 
           '].[dbo].[' + @table_name + '] ' 

    EXEC(@sql); 
GO 

这工作得很好。现在我得到越来越多的存储过程与类似的代码片段。关于维护,我想重复使用片段或使用动态功能。

一个例子:我有另一个存储过程应“过滤器”的结果集的此呈现的示例(一个额外的要求是不添加的参数来过滤该结果直接设置)

如果该SQL将是静态我会使用一个函数。在我的情况下,我需要这种动态。 UDF不允许该“EXEC”-stuff和存储过程的结果集不可重用。

我希望很多人都能像我一样解决问题。我GOOGLE了很多,并尝试了几件事,但没有什么作品...

我需要重用存储过程代码片段或重用存储过程的结果集或像存储过程一样动态的函数的方法。

那么有人能帮助我,给我另一个想法吗?

感谢您的帮助或解决方案的想法:)

V

编辑/解决方案:
所有大家首先应该阅读 “乔尔Coehoorn” 和 “帕纳约蒂斯Kanavos” 的评论。他们是绝对正确的,这个代码片段不建议在Web应用程序或类似的东西。在我的特殊情况下,它是一个本地应用程序,注入和其他安全方面是不相关的。

之后,我不得不感谢你“阿卜杜勒雷曼赛义德”。基于他的想法,下面的解决方案。

要在SQL中实现该需求,必须创建两个存储过程。第一个是创建一个结果集并将其写入临时表。

第二个执行第一个存储过程,筛选结果并删除临时表。

第一招:

ALTER PROCEDURE [dbo].[test_SP_FXN](
@database nvarchar(70)= '' 
) AS 

DECLARE @create nvarchar(max)= ' 
    CREATE TABLE ['[email protected]+'].[dbo].[temp_result](
    [name] [nvarchar](150) NULL 
    ,[id] [nvarchar](150) NULL 
    ,[desc] [nvarchar](450) NULL) '; 

DECLARE @sql nvarchar(max)= ' 
INSERT INTO ['[email protected]+'].[dbo].[temp_result] 
    SELECT TOP 1000 name, id, desc 
    FROM ['[email protected]+'].[dbo].[important_table] '; 

EXEC(@create); 
EXEC(@sql); 

第二个:

ALTER PROCEDURE [dbo].[test_SP_2](
@database nvarchar(70)= '' 
) AS 
-- create the temp table with the result of the store procedure 
EXEC ('['[email protected]+'].[dbo].[test_SP_FXN] '[email protected]) 

-- Execute the real sql 
DECLARE @sql nvarchar(max)= 'select * FROM ['[email protected]+'].[dbo].[temp_result] WHERE ID > 5' 
EXEC(@sql); 

-- drop the temp table because there is no use any more 
EXEC ('DROP TABLE ['[email protected]+'].[dbo].[temp_result] ') 

这只是一个例子,但我认为原则就清楚了。

感谢您的帮助和评论。

+4

这看起来像SQL注入攻击的噩梦。 –

+0

你是否需要这个过程的结果在临时表中? –

+0

这不是SQL(语言)的工作原理。您不要“重复使用”多个表格上的语句。一个表等价于一个类型,你不能拥有任意类型的方法。这个存储过程不是“动态的”,它只是一个使用串联创建的字符串。事实上,客户端生成的SQL语句(例如来自ORM)的表现会更好并且更安全。事实上,它会打开安全漏洞(要求访问不同的数据库),打开数据库以注入攻击,否定执行计划缓存带来的任何好处,并可能升级到分布式事务 –

回答

-1

我认为你这样寻找的东西:

Declare @table Table(/* list of columns */) 

insert into @table 
EXEC test_sp 'aDbName','aTable','somecolumns' 

select * 
from @table 
where /* some filters */ 
+0

@Der_V你可以使用Table变量而不是在你的第一个表中创建一个表,并且你不需要删除它;)。 –

0

以另一种方式,你可以改变你的存储过程是这样的:

CREATE PROCEDURE [dbo].[test_sp](
    , @Select nvarchar(70)= '' 
    , @From nvarchar(70)= '' 
    , @Where nvarchar(max) = '' 
    , @GroupByColumn nvarchar(max) = '' 
    , @Having nvarchar(max) = '' 
    , @OrderBy nvarchar(max) = '' 
) 
AS 
    DECLARE @sql nvarchar(max)= 'SELECT ' + @Select + 
           'FROM ' + @From + 
           CASE WHEN @Where = '' THEN '' ELSE ' WHERE ' + @Where END + 
           CASE WHEN @GroupByColumn = '' THEN '' ELSE ' GROUP BY ' + @GroupByColumn END + 
           CASE WHEN @Having = '' THEN '' ELSE ' HAVING ' + @Having END + 
           CASE WHEN @OrderBy = '' THEN '' ELSE ' ORDER BY ' + @OrderBy END 

    EXEC(@sql); 
GO 

现在你可以像这样测试它:

EXEC test_sp @Select = 'Column1, Column2', @From = 'Table1' 

EXEC test_sp @Select = 'DISTINCT Table1.Column1, Table2.Column1', @FROM = 'Table1 INNER JOIN TABLE2 ON Table1.PK = Table2.FK' 

EXEC test_sp @Select = 't1.[datetime]', @From = '[TestDB].[dbo].[Table1] t1', @Where = 't1.NullableColumn IS NOT NULL' 

EXEC test_sp @Select = 'Column1, Column2', @From = 'Table1', @OrderBy = 'Column2' 

等等......