我应该如何将表名传递给存储过程?

我只是遇到了一个奇怪的事情…我们的网站上有一些代码正在采取一个巨大的SQL语句,修改代码中的一些search和replace基于一些用户值,然后将其传递到SQL Server作为一个查询。

我认为这将是一个更清洁的参数化查询存储过程,用户的价值作为参数,但是当我仔细观察,我明白他们为什么可能会这样做…他们正在select的表是可变地依赖于那些用户值。

例如,在一种情况下,如果值是(“FOO”,“BAR”),查询将最终变成“SELECT * FROM FOO_BAR”

有没有一个简单明了的方法来做到这一点? 我所尝试的一切似乎都不够高雅。

编辑:当然,我可以dynamic生成存储过程中的SQL,并执行(bleh),但在那一刻,我想知道如果我已经获得了什么。

编辑2:以某种智能的方式重构表格名称,把它们全部放在一张表中,用不同的名字作为一个新的列,这将是一个很好的解决所有这些问题的方法,这是几个人直接指出的或暗指的。 可悲的是,在这种情况下,这不是一个选项。

首先,你永远不要在这样的客户端应用上执行SQL命令组合, 就是SQL注入的内容。 (对于没有自己的权限的pipe理工具,但不适用于共享使用应用程序)。

其次,是的,参数化调用存储过程既清洁又安全。

但是 ,因为您将需要使用dynamicSQL来执行此操作,所以您仍然不希望将传递的string包含在执行的查询的文本中。 相反,您希望使用传递的string来查找用户应该被允许查询的实际表的名称。

这是一个简单的天真的例子:

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 

有些人相当质疑为什么这是更安全的。 希望小鲍比表可以使这个更清楚:

替代文字


更多问题的答案:

  1. 单独QUOTENAME不保证是安全的。 MS鼓励我们使用它,但是他们没有保证它不会被黑客挡住。 仅供参考,真正的安全就是保证。 用QUOTENAME查表是另外一个故事,它是牢不可破的。

  2. QUOTENAME对于这个例子并不是绝对必要的,仅在INFORMATION_SCHEMA上的查找翻译通常就足够了。 QUOTENAME在这里,因为它是一个很好的安全forms,包括一个完整和正确的解决scheme。 这里的QUOTENAME实际上是防止一个明显的,但类似的潜在问题,知道潜在的注入

(联合国)幸运的是没有办法做到这一点 – 你不能使用表名传递作为参数存储的代码,而不是dynamicSQL生成。 当决定在哪里生成sql代码时,我更喜欢应用程序代码而不是存储代码。 应用程序代码通常更快,更容易维护。

如果你不喜欢你正在使用的解决scheme,我会build议进行更深入的重新devise(即更改模式/应用程序逻辑,以便不必在任何地方将表名作为parameter passing)。

我会反对dynamic生成存储过程中的SQL; 这会让你陷入困境,并可能导致注入漏洞。

相反,我将分析所有可能受查询影响的表,并创build一些枚举来确定哪个表用于查询。

听起来你用ORM解决scheme会更好。

当我在存储过程中看到dynamicsql时,我感到畏缩。

你可以考虑的一件事情是创build一个包含你想要的SQL命令的case语句,每个有效的表一次,然后把一个表名作为string传递给这个过程,并且select要运行的命令。

作为一个安全人员的方式上面的build议告诉你从系统表中select,以确保你有一个有效的表格,似乎对我来说是一个浪费的操作。 如果有人可以通过QUOTENAME()注入,那么注入将在系统表上以及在底层表上工作。 唯一有助于确保它是一个有效的表名称,我认为上面的build议是一个更好的方法,因为你根本没有使用QUOTENAME()。

根据这些表中的列是相同的还是不同的,我会在两个方面从长远来看:

1)如果他们相同,为什么不创build一个新的列将被用作select器,其值是从用户提供的参数派生? (这是一个性能优化?)

2)如果它们不同,那么处理它们的机会也是不同的。 因此,将select/处理代码拆分为单独的块,然后分别调用它们对我来说是一种最模块化的方法。 您将重复“select * from”部分,但是在这种情况下,表格集希望是有限的。

允许调用代码提供表名的两个任意部分来进行select感觉非常危险。

我不知道你为什么把数据分散在几张桌子上,但这听起来像是你打破了一个基本面。 数据应该在表中,而不是表名。

如果表格的布局或多或less相同,请考虑是否最好将数据放在一个表中。 这将解决您的问题与dynamic查询,这将使数据库布局更灵活。

而不是根据用户input值查询表,您可以select该过程。 也就是说
1.创build一个程序FOO_BAR_prc,在里面你把查询'select * from foo_bar',这样查询就会被数据库预编译。
2.然后根据用户input现在从您的应用程序代码执行正确的程序。

由于你有大约50张桌子,这可能不是一个可行的解决scheme,因为这需要很多工作。

实际上,我想知道如何传递表名来在存储过程中创build表。 通过阅读一些答案,并尝试在我的一些修改,我终于可以创build一个名称传递参数的表。 这里是其他人检查任何错误的存储过程。

使用[数据库名称] GO / ******对象:StoredProcedure [dbo]。[sp_CreateDynamicTable]脚本date:06/20/2015 16:56:25 ****** / SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO CREATE PROCEDURE [dbo]。[sp_CreateDynamicTable] @tName varchar(255)AS BEGIN SET NOCOUNT ON; DECLARE @SQL nvarchar(max)

 SET @SQL = N'CREATE TABLE [DBO].['+ @tName + '] (DocID nvarchar(10) null);' EXECUTE sp_executesql @SQL 

结束

@RBarry Young您不需要将括号添加到查询string中的@ActualTableName,因为它已经包含在INFORMATION_SCHEMA.TABLES中查询的结果中。 否则,执行时会出现错误。

CREATE PROC spCountAnyTableRows(@PassedTableName as NVarchar(255))AS – 计算任何非系统表的行数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 + '];' -- changed to this SELECT @sql = 'SELECT COUNT(*) FROM ' + @ActualTableName + ';' EXEC(@SQL) 

结束