在sql语句中,怎样将参数做为表名传递到查询语句中

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了在sql语句中,怎样将参数做为表名传递到查询语句中相关的知识,希望对你有一定的参考价值。

参考技术A 今天为了提取出公共的fuction提高执行效率,需要传递表的字段作为参数,语法可以通过,但是查询结果不正确。将表字段参数换成实际的字段就可以,问题出在如果将表名,字段名做为参数传递到Sql Server中create function backtoCount(@tablename varchar(50))returns intasbegindeclare @count intselect @count=count(*) from @tablenamereturn @countend但是这样的时候会报错,说变量@tablename要声明对于表名作为变量,我们可以使用object_name(id)方法create function backtoCount(@tablename varchar(50))returns intasbegindeclare @count intselect @count=rows from sysindexes where indid in (0,1) and object_name(id)=@tablenamereturn @countendGO--查sysobjects表有多少行select dbo.backtoCount( 'sysobjects ')对于字段变量的话,必须执行动态语句,但是函数里边不能用exec,建议使用存储过程实现。解决方法:动态SQLcreate procedure f_count(@tablename varchar(50))asdeclare @str as varchar(1000)set @str= 'select count(*) as a from本回答被提问者采纳

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

【中文标题】我应该如何将表名传递到存储过程中?【英文标题】:How should I pass a table name into a stored proc? 【发布时间】:2009-08-07 20:09:37 【问题描述】:

我刚刚遇到了一件奇怪的事情......我们网站上的一些代码正在使用一个巨大的 SQL 语句,通过基于一些用户值进行一些搜索和替换来在代码中修改它,然后将其传递给SQL Server 作为查询。

我认为这将作为对存储过程的参数化查询更清晰,以用户值作为参数,但是当我更仔细地观察时,我明白他们为什么会这样做......他们所在的表从中选择取决于这些用户值。

例如,在一种情况下,如果值为 ("FOO", "BAR"),则查询最终会变成类似于 "SELECT * FROM FOO_BAR"

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

编辑:当然,我可以在存储的过程中动态生成 sql,然后执行它(bleh),但那时我想知道我是否获得了什么。

EDIT2: 以某种智能方式重构表名,例如将它们全部放在一个具有不同名称的表中作为新列将是解决所有这些问题的好方法,几个人直接指出或暗示。遗憾的是,在这种情况下,它不是一个选项。

【问题讨论】:

SQL-Server 2008 允许 TABLE 变量。 @Lance:确实如此,但我认为(我可能错了)它们的工作方式与此不同。表变量存储表数据,而不是表名。这是一个类似于临时表的概念。 您不必“重构表名”,它们已经在系统提供的视图中:INFORMATION_SCHEMA.TABLES。 【参考方案1】:

首先,您应该永远在这样的客户端应用程序上执行 SQL 命令组合,这就是什么是 SQL 注入。 (对于没有自己权限的管理工具可以,但对于共享使用的应用程序则不行)。

其次,是的,对存储过程的参数化调用既干净又安全。

然而,由于您需要使用动态 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


更多问题的答案:

    不能保证单独的 QUOTENAME 是安全的。 MS 鼓励我们使用它,但他们没有保证它不会被黑客攻击。仅供参考,真正的安全就是保证。使用 QUOTENAME 查找表是另一回事,它是牢不可破的。

    QUOTENAME 对于此示例并非绝对必要,仅 INFORMATION_SCHEMA 上的查找转换通常就足够了。 QUOTENAME 在这里是因为包含一个完整且正确的解决方案是一种很好的安全形式。此处的 QUOTENAME 实际上是在防止一个独特但类似的潜在问题,即潜在注入


我应该注意,您可以对动态列名和INFORMATION_SCHEMA.COLUMNS 表执行相同的操作。

您还可以通过使用参数化 SQL 查询来绕过对存储过程的需求(参见此处:https://docs.microsoft.com/en-us/dotnet/api/system.data.sqlclient.sqlcommand.parameters?view=netframework-4.8)。但我认为存储过程为此类情况提供了更易于管理且不易出错的安全设施。

【讨论】:

虽然我不喜欢使用动态 SQL,但这似乎是解决手头问题的最佳选择。 +1 查看我对 AlexS 回答的评论。 哦,是的...我喜欢小鲍比桌。那个贴在我的立方体里已经有一段时间了。 @RBarry。我删除了之前的评论,因为我意识到第二位是错误的,但在这种情况下,您不需要手动添加方括号,这让我感到困惑。 QUOTENAME('foo') = [foo] @MichaelFreidgeim 是的。【参考方案2】:

(Un)幸运的是,没有办法做到这一点 - 除了动态 sql 生成之外,您不能使用作为参数传递给存储代码的表名。在决定在哪里生成 sql 代码时,我更喜欢应用程序代码而不是存储代码。应用程序代码通常更快更容易维护。

如果您不喜欢正在使用的解决方案,我建议您进行更深入的重新设计(即更改架构/应用程序逻辑,这样您就不必再将表名作为参数传递到任何地方)。

【讨论】:

嗯,并不是我不喜欢它,而是我希望有更好的方法来做到这一点。如果目前的解决方案(在代码中修改一个sql字符串)是最好的方法,那么如果我想成为一名优秀的程序员,我最好喜欢它,对吧? AlexS:你推荐的是 SQL 注入。 @RBarryYoung:我建议“进行更深入的重新设计……这样您就不必再将表名作为参数传递”。我猜这不是“SQL 注入”;-) @RBarryYoung:使用动态SQL不是“SQL注入”,它只是一种容易产生“SQL注入”的方法,必须小心操作。 AlexS:“在决定在哪里生成 sql 代码时,我更喜欢应用程序代码而不是存储代码”是我关心的问题。 “更深层次的重新设计”当然是一个很好的建议。【参考方案3】:

我反对在存储过程中动态生成 SQL;这会给你带来麻烦,并可能导致注入漏洞。

相反,我会分析所有可能受查询影响的表,并创建某种枚举来确定要用于查询的表。

【讨论】:

我想过这个问题,但问题是有很多(大约 50 个)表可能会发生这种情况,但也许更关键的是,会定期添加更多表。 我并不太担心 SQL 注入,因为我们从不向用户查询任何将进入参数的内容......它们都是用户不会拥有的内部值进入。尽管如此,比我想象的更好的人可能在他们不安全的时候是安全的,所以警告是正确的。 他已经在客户端代码中进行了 SQL 注入。那里并不比在 SQL Server 中更安全。并且完全可以使用动态 SQL,包括解决这个问题,而无需任何注入。 看起来@RBarry Young 的回答可能是最好的选择。它是动态 SQL,但它安全足以让您确定表名。【参考方案4】:

听起来你最好使用 ORM 解决方案。

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

【讨论】:

有时这是不可避免的,比如使用 PIVOT 命令时。 是的,但通常情况下,如果你被黑了,你就会发现自己做得不对。 @RBarryYoung:是的,如果安全完成,那完全没问题。当你看到它时,你仍然应该畏缩。如果您正在这样做,那么您可能会更好地构建系统。【参考方案5】:

您可以考虑的一件事是创建一个包含您想要的相同 SQL 命令的 case 语句,对每个有效表执行一次,然后将表名作为字符串传递到此过程中,并让 case 选择要运行的命令。

顺便说一句,作为安全人员,上面的建议告诉您从系统表中进行选择以确保您拥有一个有效的表,这对我来说似乎是一种浪费的操作。如果有人可以通过 QUOTENAME() 注入,那么注入将在系统表和基础表上工作。这有助于确保它是一个有效的表名,我认为上面的建议是一种更好的方法,因为你根本没有使用 QUOTENAME()。

【讨论】:

最后的观察是错误的。 RBarryYoung 编写的查询不是动态的,不会进行任何更新,也不会使用不受信任的数据填充任何变量。因此,直接、潜在或持久 SQL 注入的风险为零。与执行 EXEC('UPDATE' + @tablename + 'SET...') 非常不同。不仅没有风险,而且它是使整个事情安全的关键项目。【参考方案6】:

根据这些表中的列集是相同还是不同,从长远来看,我会以两种方式处理它:

1) 如果它们相同,为什么不创建一个新列用作选择器,其值来自用户提供的参数? (是性能优化吗?)

2) 如果它们不同,则它们的处理方式也可能不同。因此,似乎将选择/句柄代码拆分为单独的块,然后单独调用它们对我来说是一种最模块化的方法。您将重复“select * from”部分, 但在这种情况下,表的集合希望是有限的。

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

【讨论】:

遗憾的是,虽然我可以通过枚举来决定从哪个表中选择,但它会非常大(现在大约有 50 个表),但更糟糕的是,会定期添加更多表,这会导致到维护问题。 所以你有更多的情况(1)而不是(2)?例如类似于包含“姓名、姓氏”的表列表,表名为“程序员”、“经理”、“董事”、“用户”等?【参考方案7】:

我不知道您将数据分布在多个表中的原因,但听起来您正在破坏其中一个基本面。数据应该在表中,而不是作为表名。

如果表格具有或多或少相同的布局,请考虑是否最好将数据放在单个表格中。这将解决您的动态查询问题,并使数据库布局更加灵活。

【讨论】:

理论上我同意,但没办法。这是 Commerce Server 问题。我不了解它的所有细节,但它是一个巨大的站点,它的各种目录表部分是站点的核心。我们无法在短期内改变它。【参考方案8】:

您可以选择过程,而不是根据用户输入值查询表。 也就是说 1. 创建一个过程 FOO_BAR_prc 并在其中放入查询 'select * from foo_bar' ,这样查询将由数据库预编译。 2. 然后根据用户输入从您的应用程序代码中执行正确的过程。

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

【讨论】:

好吧,如果是一次性工作,我不怕做很多工作,但是如果我们需要维护 50 个存储的过程,例如,向查询中添加一列,那将是一场噩梦。 【参考方案9】:

实际上,我想知道如何通过表名在存储过程中创建表。通过阅读一些答案并在最后尝试进行一些修改,我终于能够创建一个名称作为参数传递的表。这是其他人检查其中任何错误的存储过程。

使用 [数据库名称] 去 /****** 对象:StoredProcedure [dbo].[sp_CreateDynamicTable] 脚本日期:06/20/2015 16:56:25 ******/ 设置 ANSI_NULLS ON 去 设置 QUOTED_IDENTIFIER ON 去 创建过程 [dbo].[sp_CreateDynamicTable] @tName varchar(255) 作为 开始 设置无计数; 声明 @SQL nvarchar(max)

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

    EXECUTE sp_executesql @SQL

结束

【讨论】:

【参考方案10】:

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

CREATE PROC spCountAnyTableRows(@PassedTableName as NVarchar(255)) AS -- 计算任何非系统表中的行数,SAFELY 开始 将@ActualTableName 声明为 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)

结束

【讨论】:

以上是关于在sql语句中,怎样将参数做为表名传递到查询语句中的主要内容,如果未能解决你的问题,请参考以下文章

怎样用SQL语句查询一个数据库中的所有表?

SQL语句怎样返回查询结果

在SQLServer中的查询语句

iBatis的SqlMap中,我写的这条动态SQL语句,将表名当做参数动态传递,报“表名无效”错误。

Power BI 中的 M(Power Query),将参数从列表传递到 SQL 语句

python的pymysql中以表名作为变量传递进查询语句中