13

我有一个脚本文件,例如 test.sql。我想使用 :r test.sql 在 sqlcmd 模式下从另一个脚本调用它,比如 caller.sql。这很好用,但我想在 test.sql 中使用脚本变量。当我从 caller.sql 调用 test.sql 时,我可以设置脚本变量,一切都很好。但是,我想为脚本值使用默认值,这样如果调用者没有设置变量,或者如果我直接运行 test.sql(不是来自 caller.sql),那么脚本变量默认为设置值。

我尝试过诸如

begin try
 select '$(grip)'
 select 'grip value was found'
end try
begin catch
 select 'grip value was missing'
end catch

但我只收到以下消息:发生了致命的脚本错误。未定义可变夹点。

我在 test.sql 中需要什么,以便它可以处理调用者是否通过“grip”?我正在使用 MS SQL 2005

4

6 回答 6

6

有一个有限的解决方法(我只在 SS2008R2 上测试过):

简单版本 - 如果你愿意没有:on error exit/生活sqlcmd.exe -b

:on error ignore -- Ensures that sqlcmd.exe will not fail when referencing an undefined scripting variable. Remove this if you want your script to work in SSMS in regular mode, too.
Declare @valueOrDefault as nvarchar(max)= N'$(value)';    
if @valueOrDefault = N'$' + N'(value)' set @valueOrDefault = N'default value'; -- Test if there is a value and, if not, assign a default; note the splitting of the reference string to avoid expansion.

-- use @valueOrDefault from now on

笔记:

  • 由于 T-SQL 变量不能跨批次工作,因此您无法启动另一个批次(使用 GO),因此无法切换到使用:on error exit. 因此,您必须在脚本的其余部分中进行自己的错误处理——这很重要;请参阅SQL Server - 停止或中断 SQL 脚本的执行
  • 如果:on error ignore为了使脚本以常规模式在 SSMS 中工作而删除 ,请确保在使用 sqlcmd.exe 调用该脚本时未指定 -b 选项,因为如果引用的脚本变量不存在。
  • 通过有效地将脚本变量转换为常规 T-SQL 变量,您不能在 T-SQL 需要文字的地方使用该值,例如 CREATE DATABASE 语句中的数据库名称。
  • 如果未定义脚本变量,则会将以下警告打印到 stderr:'variableName' 脚本变量未定义。

ROBUST 版本 - 更麻烦,但支持:on error exit,这是可取的:

-- Store the default value in the context info (session-level storage accessible across batches that holds up to 128 bytes).
declare @binDefaultValue varbinary(128)= CAST(N'default value' AS varbinary(128));
set CONTEXT_INFO @binDefaultValue;
go -- Make the set CONTEXT_INFO statement take effect.

-- If the scripting variable has a value, store ITS value in the context info instead.
:on error ignore -- Temporarily ignore errors so that accessing a non-existent scripting variable doesn't abort the entire script.
    declare @value as nvarchar(max) = N'$(value)'; -- Try to access the scripting variable; thanks to :on error ignore this will only give a warning.
    if @value <> N'$' + N'(value)' -- Test if there is a value; note the splitting of the reference string to avoid expansion.
    begin
    -- We have a scripting-variable value: Store it in the context info (replacing the default value).
        declare @binValue as varbinary(128) = cast(@value as varbinary(128));
        set CONTEXT_INFO @binValue;
    end
go -- End batch here, so we can switch back to :on error exit (requires a new batch).

:on error exit -- New batch: switch back to robust error handling.
-- End the batch here, so that SSMS in *regular* mode - which will fail on the line above - continues processing below.
-- Note that when run by sqlcmd.exe the subsequent batches will inherit :on error exit.
go

-- Retrieve the value or default value from the context info...
declare @valueOrDefault as nvarchar(max) = convert(nvarchar(max), CONTEXT_INFO(), 0);
-- ... and remove trailing null characters. ?? Is there an easier way to do this?
declare @pos as int = 0;
while @pos < LEN(@valueOrDefault)
begin
    set @pos=@pos+1
    if UNICODE(substring(@valueOrDefault, @pos, 1)) = 0  break;
end
if @pos > 0 set @valueOrDefault = left(@valueOrDefault, @pos - 1);

-- @valueOrDefault now contains the scripting-variable value or default value.
print 'Value or default value: [' + @valueOrDefault + ']';

笔记:

  • 以上方法在从 sqlcmd.exe 调用时和在常规模式下的 SSMS 中都有效 - 假设您在脚本中没有使用其他 SQLCMD 命令。遗憾的是,SQLCMD模式下的 SSMS 总是拒绝运行引用不存在的脚本变量的脚本。
  • 需要使用SET CONTEXT_INFO,因为值需要跨批次边界传递,而 T-SQL 变量无法做到这一点。需要多个批次才能切换回强大的错误处理。
  • 上面的代码只支持单个脚本变量,并且由于使用了SET CONTEXT_INFO,它的长度被限制为 128 字节 = 64 个 Unicode 字符;不过,也可以使用其他变通方法,例如临时表。
  • 通过有效地将脚本变量转换为常规 T-SQL 变量,您不能在 T-SQL 需要文字的地方使用该值,例如CREATE DATABASE语句中的数据库名称。
  • 如果未定义脚本变量,则会将以下警告打印到 stderr:'variableName' 脚本变量未定义。
于 2011-12-13T06:38:06.587 回答
2

也许这三个选项之一:

  • 通过命令行选项v
  • 通过:SETVAR本章后面介绍的命令
  • 通过在运行之前定义环境变量SQLCMD

使用v选项

在您的脚本中:SELECT $(foo) FROM $(bar)
用法:C:>SQLCMD i c:\someScript.sql -v foo="CustomerName" bar="Customer"

使用setvar

:setvar foo CustomerName
:setvar bar Customer

来自
http://www.sqlcmd.org/sqlcmd-scripting-variables/

于 2010-09-19T19:03:58.027 回答
0

如果您从 Windows 命令提示符运行 SQLCMD,请在 SQLCMD 语句的末尾添加“2> nul”。这将消除输出开头的 SQLCMD 抱怨。

示例:sqlcmd -S DatabaseServer -E -d MyDatabase -i MyScript -v Variable1=Value1 2> nul

于 2012-06-13T17:07:03.613 回答
0

检索变量并分配给变量,如果它通过 SET 语句,则将其分配。catch 将处理默认值。这适用于我在 SQL Server 2005 上。我注意到这总是说当我在 SQL Management Studio 中运行它时它失败了,即使我认为它有效。当我使用 sqlcmd 通过命令行运行它时,它可以正常工作而不会出现任何错误消息。

BEGIN TRY 
    DECLARE @bogusVar VARCHAR(64);
    SET @bogusVar = '' + $(envVar);
    PRINT 'Using values passed from sqlcmd';
END TRY
BEGIN CATCH
    PRINT 'Using default values for script'
    :setvar envVar 'DefaultValue'
END CATCH;
于 2011-05-03T20:49:45.097 回答
0

对我来说远程实用的结果是将系统环境变量设置为默认值,然后在需要的地方使用 SQLCMD -v 覆盖该值。

于 2012-11-27T00:08:40.507 回答
0

设置上下文是一个新颖而有趣的想法。当我阅读您在解析 VarBinary 时遇到的问题时,我认为临时表可能会满足这一需求。在处理了一点之后(然后记住你的评论,即 SSMS 总是错误 :( 当脚本变量未定义时)我得到了这个版本。我通常不喜欢名称-值解决方案,但性能并不是真的和问题在这里和它以定义和测试每个可选参数所需的样板为代价提供了很大的灵活性。我使用 SSMS 17 在 SQL2008R2 上进行了测试。

Set NoCount On;
If Object_Id('tempdb..#hold', 'U') Is Not Null Drop Table #hold;
Go
Create Table #hold(theKey NVARCHAR(128) Not Null, theValue NVARCHAR(128) Not Null);
Insert #hold(theKey, theValue) Values (N'Type', N'Full');
Select theValue From #hold Where theKey = N'Type'

:on error ignore
    Declare @theValue   NVARCHAR(128) = N'$(BType)';
    If @theValue <> N'$' + N'(BType)'   -- a value was passed in
        Update #hold Set theValue = @theValue Where theKey = N'Type';
Go
:on error exit

Select theValue From #hold Where theKey = N'Type'
Return;
-- sqlcmd -S myServer -E -i thePath\theScript.sql -v BType = "diff"
于 2018-11-09T20:01:06.687 回答