【问题标题】:sql scripting variable default valuesql脚本变量默认值
【发布时间】:2010-09-19 18:50:43
【问题描述】:

我有一个脚本文件,例如测试.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

【问题讨论】:

    标签: sql-server sql-server-2005 sqlcmd


    【解决方案1】:

    有一个有限的解决方法(我只在 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 - stop or break execution of a SQL script
    • 如果您删除 :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 调用时和在 regular 模式下的 SSMS 中都有效 - 假设您在脚本中没有使用其他 SQLCMD 命令。遗憾的是,SQLCMD 模式下的 SSMS 始终拒绝运行引用不存在的脚本变量的脚本。
    • SET CONTEXT_INFO 的使用是必需的,因为值需要跨批次边界传递,而 T-SQL 变量无法做到这一点。需要多个批次才能切换回强大的错误处理。
    • 上面的代码只支持一个单个脚本变量,并且由于使用SET CONTEXT_INFO,它的长度被限制为128字节= 64个Unicode字符;不过,也可以使用其他解决方法,例如临时表。
    • 通过有效地将脚本变量转换为常规 T-SQL 变量,您不能在 T-SQL 需要文字的地方使用该值,例如 CREATE DATABASE 语句中的数据库名称。
    • 如果未定义脚本变量,则会将以下警告打印到 stderr: 'variableName' 脚本变量未定义。

    【讨论】:

    • 我认为临时表提供了一种更灵活、更简单的方法。见下文
    【解决方案2】:

    也许是这三个选项之一:

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

    使用v 选项

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

    使用setvar

    :setvar foo CustomerName
    :setvar bar Customer
    

    发件人
    http://www.sqlcmd.org/sqlcmd-scripting-variables/

    【讨论】:

    • 感谢您的建议,但这些似乎都没有解决对默认值(在 test.sql 中)的需求,当 caller.sql 调用时,该默认值可以被另一个值覆盖(我不能让它工作)。 :setvar 选项看起来不错,但 test.sql 中的 setvar 优先于 caller.sql 中设置的任何内容
    【解决方案3】:

    检索变量并分配给一个变量,如果它通过 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;
    

    【讨论】:

      【解决方案4】:

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

      【讨论】:

        【解决方案5】:

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

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

        【讨论】:

        • 我已经在许多来源中阅读过这个,但它对我不起作用。有什么想法吗?
        【解决方案6】:

        设置上下文是一个新颖而有趣的想法。当我阅读您在解析 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"
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2011-08-27
          • 2012-06-25
          • 1970-01-01
          • 1970-01-01
          • 2014-03-19
          • 2011-10-14
          • 2011-09-22
          • 2014-12-15
          相关资源
          最近更新 更多