【问题标题】:SQL Server dynamic queriesSQL Server 动态查询
【发布时间】:2010-09-20 19:09:53
【问题描述】:

我有 15 个存储过程,它们从公用表返回数据,然后将该表与特定表连接以检索库存。

例子:

Common: tblCommon
Specific: tblSpecific

有没有办法可以将名称“tblSpecific”作为变量传递到单个存储过程中,如下所示?

SELECT ....
FROM tblCommon c
INNER JOIN @TABLE s on c.primaryKey = s.foreignKey

【问题讨论】:

    标签: sql sql-server tsql dynamic


    【解决方案1】:

    执行此操作的方法是使用通过 sp_executesql() 存储过程运行的动态生成的 SQL。

    通常,您将所需的表名传递给主过程,构建您要执行的 SQL 的 ncharvar 字符串,然后将其传递给 sp_executesql。

    The curse and blessing of Dynamic SQL 是我见过的描述所有进出的最佳页面。

    最大的问题之一是,如果您使用动态 SQL,那么调用您的存储过程的用户不仅必须对该过程具有执行权限,而且还必须具有访问基础表的权限。我提供的链接还描述了如何解决该问题。

    【讨论】:

      【解决方案2】:

      是的,您可以动态生成一条 SQL 语句然后执行它。

      例如,

      DECLARE @specificTableName nvarchar(50)
      DECLARE @specificColumnName nvarchar(50)
      
      SET @specificTableName = 'tblSpecific'
      SET @specificColumnName = 'colSpecific'
      
      DECLARE @sql nvarchar(4000)
      
      set @sql = 'SELECT ... FROM tblCommon c INNER JOIN ' +
      @specificTableName + ' s ON c.PrimaryKey = s.' + @specificColumnName
      
      
      exec (@sql)
      

      【讨论】:

        【解决方案3】:

        将您的查询公式化/操作为字符串,然后调用EXECUTE(@SQLStatement)

        【讨论】:

          【解决方案4】:

          动态 SQL 很危险。您从不希望将传递的值直接替换为 SQL 字符串。幸运的是,听起来你已经知道了。

          不幸的是,在这种情况下,您发现了不能使用 SQL 参数作为表名的问题。那么该怎么办?您不想在动态生成的 SQL 中使用传递的值,但不能以正常安全的方式将其放入查询中。

          答案是查找表。创建一个“表”表,其中包含每个特定表的名称。它应该看起来像这样:

          CREATE TABLE [tables] (table_name sysname)
          

          然后您可以编写如下所示的查询:

          SELECT @tblSpecific = table_name FROM [tables] WHERE table_name = @tblSpecific
          

          现在您只需检查@tblSpecific 是否为NULL。如果不是,那么在动态 SQL 语句中使用是安全的(动态 SQL 最终是您的 only 选项:即使是用户定义的函数也让您在某种程度上这样做)。

          哦,还有一件事——我为查找表选择的名称和类型并非偶然。 SQL 标准已经有一个这样的表(好吧,无论如何都是一个视图)。只需使用INFORMATION_SCHEMA.Tables。

          【讨论】:

          • 说永远不会是反动的。你永远不想这样做 IF 值是从用户屏幕收集的......但如果你为该变量创建域它是安全的......就像他为每个表编写一个查询一样安全将它们放在案例陈述中。有些人没有节制意识。
          【解决方案5】:

          另一种选择,如果数据量不是太大,您可能需要考虑一个用户定义的函数,它可以返回一个您可以用来连接的表变量。

          SELECT ....
          FROM tblCommon c
          INNER JOIN dbo.SomeFuntionThatReturnsData(@someparam) s on c.primaryKey = s.foreignKey
          

          【讨论】:

          • 谢谢,这可能是我即将得到的。
          • 由于您不能使用表名作为参数,因此您仍然需要在某些时候在单独的语句中显式写出所有 SQL。
          • 是的,但我的意思是该函数可能能够替换 15 个 procs 并用于返回要加入的公共表变量。
          【解决方案6】:

          我会将它们分别保存为不同的存储过程。

          我喜欢尽可能保持存储过程简洁明了。它们很难一眼就能理解,因为无论如何表达式都太长了,添加一堆与声明性代码片段混合的过程代码只会让它变得更加困难。

          您最终会得到一个包含 15 次调用的更复杂的带有参数的存储过程的列表,或者您最终会得到一个等效的更简单存储过程的列表。如果你的参数是一个表名,它就不会是那种高效执行的参数化 sp。至于表驱动的方法,它仍然是效率较低且更危险的动态存储过程。表条目很可能被错误输入,除了在表中,任何表名错误都将更不明显。耦合度上升了,粘性下降了(都朝着错误的方向发展)。

          【讨论】:

          • 阿门......人们迷失在不重复代码的想法中。 “如果我把它放在一个地方,那就太酷了。”不利的一面是,当需要进行更改时,您需要在几个地方进行修复,但很有可能这 15 张桌子中的一张看起来会有所不同,并且无论如何都需要出现。
          • 这是声明性代码的良好编码风格不等同于过程代码的良好编码风格的众多方式之一。两者之间没有太多可转移的技能。 IOW,出色的程序技能并不意味着您将在 SQL 方面有多好。
          猜你喜欢
          • 2017-01-11
          • 2012-05-11
          相关资源
          最近更新 更多