Sql 如何将表名传递到存储的进程中?
我刚刚遇到了一件奇怪的事情……我们网站上的一些代码采用了一个巨大的SQL语句,通过基于一些用户值进行搜索和替换,在代码中对其进行修改,然后将其作为查询传递给SQL Server 我原以为这将更简洁,因为它是对存储过程的参数化查询,以用户值作为参数,但当我仔细观察时,我明白了他们为什么会这样做……他们从中选择的表在很大程度上取决于这些用户值 例如,在一种情况下,如果值为(“FOO”、“BAR”),则查询结果将类似于“SELECT*FROM FOO\u BAR” 有没有一种简单明了的方法可以做到这一点?我所尝试的一切似乎都不雅观 EDIT:当然,我可以在存储过程中动态生成sql,并执行它(bleh),但在这一点上,我想知道我是否获得了什么Sql 如何将表名传递到存储的进程中?,sql,sql-server,parameterized,Sql,Sql Server,Parameterized,我刚刚遇到了一件奇怪的事情……我们网站上的一些代码采用了一个巨大的SQL语句,通过基于一些用户值进行搜索和替换,在代码中对其进行修改,然后将其作为查询传递给SQL Server 我原以为这将更简洁,因为它是对存储过程的参数化查询,以用户值作为参数,但当我仔细观察时,我明白了他们为什么会这样做……他们从中选择的表在很大程度上取决于这些用户值 例如,在一种情况下,如果值为(“FOO”、“BAR”),则查询结果将类似于“SELECT*FROM FOO\u BAR” 有没有一种简单明了的方法可以做到这一
EDIT2:以某种智能的方式重构表名,比如说,将所有表名放在一个具有不同名称的表中作为一个新列,这将是解决所有这些问题的一种很好的方法,一些人已经直接指出或暗示了这一点。遗憾的是,在这种情况下,这不是一个选项。我反对在存储过程中动态生成SQL;这将给您带来麻烦,并可能导致注射漏洞 相反,我将分析可能受查询影响的所有表,并创建某种枚举,以确定用于查询的表 (Un)幸运的是,没有办法做到这一点-除了用于动态sql生成之外,不能将表名作为参数传递给存储代码。在决定在何处生成sql代码时,我更喜欢应用程序代码而不是存储代码。应用程序代码通常更快,更易于维护
如果您不喜欢正在使用的解决方案,我建议进行更深入的重新设计(即更改模式/应用程序逻辑,这样您就不必在任何地方将表名作为参数传递) 听起来您最好使用ORM解决方案 当我在存储过程中看到动态sql时,我会感到畏缩。首先,您不应该在这样的客户端应用程序上执行sql命令组合,这就是sql注入。(对于一个没有priv的管理工具来说是可以的,但对于一个共享使用的应用程序来说不是) 其次,是的,对存储过程的参数化调用更干净、更安全 但是,由于需要使用动态SQL来执行此操作,因此仍然不希望在已执行查询的文本中包含传递的字符串。相反,您希望使用传递的字符串来查找实际表的名称,应该允许用户以这种方式查询这些表 下面是一个简单而天真的例子:
CREATE PROC spCountAnyTableRows( @PassedTableName as NVarchar(255) ) AS
-- Counts the number of rows from any non-system Table, *SAFELY*
BEGIN
DECLARE @ActualTableName AS NVarchar(255)
SELECT @ActualTableName = QUOTENAME( TABLE_NAME )
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME = @PassedTableName
DECLARE @sql AS NVARCHAR(MAX)
SELECT @sql = 'SELECT COUNT(*) FROM ' + @ActualTableName + ';'
EXEC(@SQL)
END
有些人公平地问,为什么这样更安全。希望小Bobby Tables能让这一点更清楚: 0
更多问题的答案:
我应该注意,对于动态列名和
INFORMATION\u SCHEMA.COLUMNS
表,您也可以做同样的事情
您还可以使用参数化SQL查询(请参见此处:)来绕过对存储过程的需求。但我认为存储过程为此类情况提供了一种更易于管理且不易出错的安全机制。根据这些表中的列集是相同的还是不同的,从长远来看,我将以两种方式进行处理: 1) 如果它们相同,为什么不创建一个用作选择器的新列,其值是从用户提供的参数派生的?(这是性能优化吗?) 2) 如果它们不同,处理它们的方式也可能不同。因此,对我来说,将select/handle代码拆分成单独的块,然后分别调用它们似乎是一种最模块化的方法。您将重复“选择*自”部分, 但在这种情况下,表集有望是有限的
允许调用代码提供表名的两个任意部分来执行select from操作感觉非常危险 我不知道你为什么把数据分散在几个表中,但听起来你好像在打破一个基本原则。数据应该在表中,而不是作为表名
如果表具有大致相同的布局,那么考虑是否最好将数据放在单个表中。这将解决动态查询的问题,并使数据库布局更加灵活。
您可以选择过程,而不是根据用户输入值查询表。 也就是说1.创建一个过程FOO\u BAR\u prc,并将查询“select*from FOO\u BAR”放入其中,这样查询将由数据库预编译。
2.然后根据用户输入,从应用程序代码执行正确的过程
因为您有大约50个表,这可能不是一个可行的解决方案,因为这将需要大量的工作在您的部分。
您可以考虑的是,将一个case语句包含一个相同的SQL命令,每个有效的表一次,然后作为一个字符串将表名传递到这个过程中,并具有这种情况。
SET @SQL = N'CREATE TABLE [DBO].['+ @tName + '] (DocID nvarchar(10) null);'
EXECUTE sp_executesql @SQL
SELECT @ActualTableName = QUOTENAME( TABLE_NAME )
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME = @PassedTableName
DECLARE @sql AS NVARCHAR(MAX)
--SELECT @sql = 'SELECT COUNT(*) FROM [' + @ActualTableName + '];'
-- changed to this
SELECT @sql = 'SELECT COUNT(*) FROM ' + @ActualTableName + ';'
EXEC(@SQL)