3

I wrote this SQL in a stored procedure but not working,

declare @tableName varchar(max) = 'TblTest'
declare @col1Name varchar(max) = 'VALUE1'
declare @col2Name varchar(max) = 'VALUE2'
declare @value1 varchar(max)
declare @value2 varchar(200)

execute('Select TOP 1 @value1='+@col1Name+', @value2='+@col2Name+' From '+ @tableName +' Where ID = 61')

select @value1

execute('Select TOP 1 @value1=VALUE1, @value2=VALUE2 From TblTest Where ID = 61')

This SQL throws this error:

Must declare the scalar variable "@value1".

I am generating the SQL dynamically and I want to get value in a variable. What should I do?

4

5 回答 5

22

您从动态语句中收到DECLARE错误的原因是动态语句是在单独的批次中处理的,这归结为范围问题。虽然 SQL Server 中可用范围可能有更正式的定义,但我发现通常记住以下三个就足够了,从最高可用性到最低可用性排序:

全球

服务器范围内可用的对象,例如使用双井号/井号创建的临时表 ( ##GLOBALTABLE,但是您喜欢调用 # )。对全局对象要非常小心,就像对待任何应用程序、SQL Server 或其他方式一样;通常最好完全避免这些类型的事情。我实质上是说要牢记这个范围,以提醒您不要参与其中。

IF ( OBJECT_ID( 'tempdb.dbo.##GlobalTable' ) IS NULL )
BEGIN
    CREATE TABLE ##GlobalTable
    (
        Val             BIT
    );

    INSERT INTO ##GlobalTable ( Val )
    VALUES ( 1 );
END;
GO

-- This table may now be accessed by any connection in any database,
-- assuming the caller has sufficient privileges to do so, of course.

会话

引用锁定到特定 spid 的对象。在我的脑海中,我能想到的唯一类型的会话对象是一个普通的临时表,定义为#Table。处于会话范围内本质上意味着在批处理(由终止GO)完成后,对该对象的引用将继续成功解析。 这些技术上可以通过其他会话访问,但是以编程方式进行这样的操作将是一项壮举,因为它们在 tempdb 中获得了某种随机名称,并且无论如何访问它们有点让人头疼。

-- Start of session;
-- Start of batch;
IF ( OBJECT_ID( 'tempdb.dbo.#t_Test' ) IS NULL )
BEGIN
    CREATE TABLE #t_Test
    (
        Val     BIT
    );

    INSERT INTO #t_Test ( Val )
    VALUES ( 1 );
END;
GO 
-- End of batch;

-- Start of batch;
SELECT  *
FROM    #t_Test;
GO
-- End of batch;

打开一个新会话(与单独 spid 的连接),上面的第二批将失败,因为该会话将无法解析#t_Test对象名称。

批次

普通变量,例如 your@value1@value2,仅适用于声明它们的批次。与#Temp表不同,一旦您的查询块命中 a GO,这些变量就停止对会话可用。这是产生错误的范围级别。

-- Start of session;
-- Start of batch;
DECLARE @test   BIT = 1;

PRINT @test;
GO
-- End of batch;

-- Start of batch;
PRINT @Test;  -- Msg 137, Level 15, State 2, Line 2
              -- Must declare the scalar variable "@Test".
GO
-- End of batch;

好吧,那又怎样?

您的动态语句在这里发生的情况是该EXECUTE()命令有效地作为一个单独的批处理进行评估,而不会破坏您从中执行它的批处理。 EXECUTE()一切都很好,但是自从引入 以来sp_executesql(),我只在最简单的情况下使用前者(明确地,当我的语句中根本没有“动态”元素时,主要是为了“欺骗”否则不适应 DDLCREATE语句在其他批次的中间运行)。 @AaronBertrand上面的答案类似,并且在性能上与以下类似,在评估动态语句时利用优化器的功能,但我认为扩展@param, 好,参数可能是值得的。

IF NOT EXISTS ( SELECT  1
                FROM    sys.objects
                WHERE   name = 'TblTest'
                    AND type = 'U' )
BEGIN
    --DROP TABLE dbo.TblTest;
    CREATE TABLE dbo.TblTest
    (
        ID      INTEGER,
        VALUE1  VARCHAR( 1 ),
        VALUE2  VARCHAR( 1 )
    );

    INSERT INTO dbo.TblTest ( ID, VALUE1, VALUE2 )
    VALUES ( 61, 'A', 'B' );
END;

SET NOCOUNT ON;

DECLARE @SQL    NVARCHAR( MAX ),
        @PRM    NVARCHAR( MAX ),
        @value1 VARCHAR( MAX ),
        @value2 VARCHAR( 200 ),
        @Table  VARCHAR( 32 ),
        @ID     INTEGER;

    SET @Table = 'TblTest';
    SET @ID = 61;

    SET @PRM = '
        @_ID        INTEGER,
        @_value1    VARCHAR( MAX ) OUT,
        @_value2    VARCHAR( 200 ) OUT';
    SET @SQL = '
        SELECT  @_value1 = VALUE1,
                @_value2 = VALUE2
        FROM    dbo.[' + REPLACE( @Table, '''', '' ) + ']
        WHERE   ID = @_ID;';

EXECUTE dbo.sp_executesql @statement = @SQL, @param = @PRM,
            @_ID = @ID, @_value1 = @value1 OUT, @_value2 = @value2 OUT;

PRINT @value1 + ' ' + @value2;

SET NOCOUNT OFF;
于 2014-01-26T18:23:28.983 回答
7
Declare @v1 varchar(max), @v2 varchar(200);

Declare @sql nvarchar(max);

Set @sql = N'SELECT @v1 = value1, @v2 = value2
FROM dbo.TblTest -- always use schema
WHERE ID = 61;';

EXEC sp_executesql @sql,
  N'@v1 varchar(max) output, @v2 varchar(200) output',
  @v1 output, @v2 output;

您还应该将输入(例如 61 来自何处)作为正确的参数传递(但您将无法以这种方式传递表和列名)。

于 2014-01-26T13:46:10.187 回答
0

-- 创建或更改程序

ALTER PROCEDURE (

@age INT,
@salary INT OUTPUT)

开始

SELECT @salary = (SELECT SALARY FROM new_testing where AGE = @age  ORDER BY AGE OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY);

结尾

-----------------声明输出变量----------------------------- ----

声明@test INT

---------------------然后执行查询------------------------- --------

执行 25 ,@salary = @test OUTPUT

打印@test

-------------------无需过程即可获得相同的输出-------------- ----------------- SELECT * FROM new_testing where AGE = 25 ORDER BY AGE OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY

于 2020-01-13T08:10:04.767 回答
0

这是一个简单的例子:

Create or alter PROCEDURE getPersonCountByLastName (
@lastName varchar(20),
@count int OUTPUT
)
As
Begin
select @count = count(personSid) from Person where lastName like @lastName
End;

一批执行以下语句(全选)

1. Declare @count int
2. Exec getPersonCountByLastName kumar, @count output
3. Select @count

当我尝试单独执行语句 1、2、3 时,我遇到了同样的错误。但是当一次执行它们时,它工作得很好。

原因是SQL 在不同的会话中执行declare、exec 语句。

开放进一步修正。

于 2017-04-13T06:51:20.087 回答
0

如果您不一次运行所有语句,SQL Server 中也会出现这种情况。如果您要突出显示一组语句并执行以下操作:

DECLARE @LoopVar INT
SET @LoopVar = (SELECT COUNT(*) FROM SomeTable)

然后尝试突出显示另一组语句,例如:

PRINT 'LoopVar is: ' + CONVERT(NVARCHAR(255), @LoopVar)

您将收到此错误。

于 2017-05-31T20:05:27.620 回答