【问题标题】:SQL Server: is it possible to use the select @var=field from table syntax with a variable database?SQL Server:是否可以将 select @var=field from table 语法与变量数据库一起使用?
【发布时间】:2020-03-15 00:53:21
【问题描述】:

当我使用下面的脚本时,一切正常:

 create table MyTable (MyField varchar(5))

 insert into Mytable values ('XXX')

 declare @MyVar varchar(5)

 select @MyVar = MyField 
 from dbo.MyTable

 print @MyVar

但我想做的是这样的:

 create table MyTable (MyField varchar(5))

 insert into Mytable values ('XXX')

 declare @MyVar varchar(5)

 declare @DataBase varchar(10) = 'DBMyBase'

 select @MyVar = MyField 
 from @DataBase.dbo.MyTable

 print @MyVar

我试过没有成功:

 create table MyTable (MyField varchar(5))

 insert into Mytable values ('XXX')

 declare @MyVar varchar(5)
 declare @DataBase varchar(10) = 'DBMyBase'
 declare @cmd varchar(max)

 set @cmd = 'select @MyVar = MyField from ' + @DataBase + '.dbo.MyTable'

 exec(@cmd)

 print @MyVar

我也尝试将整个脚本放在@cmd 中,但无法在脚本部分之外捕获@MyVar 值...

对不起,我不知道如何用英文解释,但如果你能理解我,谢谢你的帮助!

【问题讨论】:

  • 谢谢大家的帮助,我不知道 sp_executesql...

标签: sql-server tsql


【解决方案1】:

这里不需要动态SQL。

EXEC @some_variable(不带括号)需要一个可以是三部分名称的@module_name_var parameter

因此您可以使用静态 SQL 字符串和 sp_executesql 的三部分名称来设置数据库上下文。

DECLARE @DataBase VARCHAR(10) = 'DBMyBase'
DECLARE @qualified_sp_executesql NVARCHAR(150) = QUOTENAME(@DataBase) + '.sys.sp_executesql'

EXEC @qualified_sp_executesql
  N'select @MyVar = MyField  from dbo.MyTable',
  N'@MyVar nvarchar(60) out',
  @MyVar OUT;

SELECT @MyVar 

【讨论】:

  • 我喜欢这种方法,但我不明白你怎么能说这不是动态 sql。它绝对是动态sql。您正在调用 EXEC 和 sp_executesql。然而,它是非常安全的动态 sql。
  • SQL 语句是静态的而不是动态的(在运行时建立)
  • 我一定错过了什么。这不是在运行时建立的吗?您在变量中有数据库名称,并将其附加到字符串的其余部分。
  • 执行的 SQL 语句是静态的,因此我的 POV 中没有动态 SQL。它始终是 select @MyVar = MyField from dbo.MyTable 模块名称是动态定义的,但这不是动态 SQL IMO。至关重要的是,它只会被解释为放入其中的模块名称,因此不能用于执行任意代码
  • 谢谢。以防万一其他人不清楚@qualified_sp_executesql是否以某种方式设置为DROP TABLE Student;,那么最糟糕的情况是错误Could not find stored procedure 'DROP TABLE Student;'.
【解决方案2】:

当您在设计时不知道整个语句时,您必须像这样使用动态 SQL:

  DECLARE @DataBase NVARCHAR(80) = N'Adventureworks'
  DECLARE @QtdDataBase sysname = QUOTENAME (@DataBase);
  DECLARE @MyVar NVARCHAR(60);
  DECLARE @AddressID INT = 9;
  IF EXISTS (SELECT 1 FROM sys.databases d WHERE d.name= @DataBase)
  begin
    DECLARE 
      @sql NVARCHAR(4000) = 'select @MyVar=AddressLine1 from ' 
          + @QtdDataBase 
          + '.SalesLT.Address where AddressID=@AddressID'
    EXEC sys.sp_executesql 
      @sql, 
      N'@AddressID int, @MyVar nvarchar(60) out', 
      @AddressID, 
      @MyVar OUT;
  end
  SELECT @MyVar [@MyVar], @sql [@sql], @QtdDataBase [@QtdDataBase]

过程 sys.sp_executesql 接受带有形式参数描述的第二个参数和用于为它们提供值并接收输出结果的下一个参数

【讨论】:

  • @Database 仍然需要安全注入;使用QUOTENAME。否则这只是一个巨大的注入问题。
  • 问题不在于安全性。是关于方法的,是吗?整个动态sql方法不好。我同意。所以,评论没问题。我的更正在这里。
  • 仅仅因为 OP 在其代码中存在严重的安全问题并不意味着您应该将其留在那里。这样的态度就是为什么 SQL 注入现在仍然是一个问题,在一个应该知道得更好的行业......如果一个朋友让你在他们度假的时候过来给他们的植物浇水,你注意到他们的正面当你到达那里时,门没有上锁并且敞开着,你离开时会保持解锁吗?不,你不会。那么,为什么要在这里打开门并解锁呢?
  • 在这里使用quotename不仅仅是防止sql注入(但这非常重要)。如果您的数据库名称中有一个空格(或其他一些愚蠢的字符)并且您不使用 quotename 这将失败。
  • EXEC 接受一个变量,该变量可以是要执行的对象名称,这可以是三部分名称,因此 dbname.sys.sp_executesql
猜你喜欢
  • 1970-01-01
  • 2021-09-10
  • 1970-01-01
  • 1970-01-01
  • 2011-04-06
  • 2014-12-02
  • 1970-01-01
  • 1970-01-01
  • 2014-03-24
相关资源
最近更新 更多