SQL “必须在存储过程中声明表变量“@name”

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/20265023/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-31 23:17:24  来源:igfitidea点击:

"Must declare the table variable "@name"" in stored procedure

sqlsql-server

提问by Tom

I have a procedure which returns the error:

我有一个返回错误的过程:

Must declare the table variable "@PropIDs".

必须声明表变量“@PropIDs”。

But it is followed with the message:

但紧随其后的是消息:

(123 row(s) affected)

(123 行受影响)

The error appears when I execute it with

当我执行它时出现错误

EXEC [dbo].[GetNeededProperties] '1,3,5,7,2,12', '06/28/2013', 'TT'

But works fine when

但工作正常时

EXEC [dbo].[GetNeededProperties] NULL, '06/28/2013', 'TT'

Can any one help me with that? The procedure:

任何人都可以帮助我吗?步骤:

CREATE PROCEDURE [dbo].[GetNeededProperties]
@NotNeededWPRNs nvarchar(max), --string like '1,2,3,4,5'
@LastSynch datetime,
@TechCode varchar(5)
AS
BEGIN

DECLARE @PropIDs TABLE
(ID bigint)
Declare @ProductsSQL nvarchar(max);
SET @ProductsSQL = 'Insert into @PropIDs (ID) 
SELECT [WPRN] FROM [dbo].[Properties] WHERE(WPRN in (' + @NotNeededWPRNs + '))'
exec sp_executesql @ProductsSQL

SELECT  p.WPRN AS ID,
p.Address  AS Address,
p.Address AS Street
  FROM [dbo].[Properties] AS p
WHERE 
   p.WPRN NOT IN( SELECT ID FROM @PropIDs)


I've found kind of solution when declaring table like this:

我在声明这样的表时找到了一种解决方案:

IF OBJECT_ID('#PropIDs', 'U') IS NOT NULL
  DROP TABLE #PropIDs

CREATE  TABLE  #PropIDs

But when execute the procedure from C# (linq sql) it returns an error

但是当从 C# (linq sql) 执行过程时它返回一个错误

回答by Allan S. Hansen

The issue is that you're mixing up dynamic SQL with non-dynamic SQL.

问题是您将动态 SQL 与非动态 SQL 混为一谈。

Firstly - the reason it works when you put NULL into @NotNeededWPRNs is because when that variable is NULL, your @ProductsSQL becomes NULL.

首先 - 当您将 NULL 放入 @NotNeededWPRNs 时它起作用的原因是因为当该变量为 NULL 时,您的 @ProductsSQL 变为 NULL。

WHat you need to do is either make your @PropsIDs table a non-table variable and either a temporary table or a physical table. OR you need to wrap everything in dynamic SQL and execute it.

你需要做的是让你的@PropsIDs 表成为一个非表变量和一个临时表或一个物理表。或者您需要将所有内容包装在动态 SQL 中并执行它。

So the easy way is to do something like this:

所以简单的方法是做这样的事情:

Declare @ProductsSQL nvarchar(max);
    SET @ProductsSQL = '
    DECLARE @PropIDs TABLE
    (ID bigint)
    Insert into @PropIDs (ID) 
    SELECT [WPRN] FROM [dbo].[Properties] WHERE(WPRN in (' + @NotNeededWPRNs + '))

    SELECT  p.WPRN AS ID,
    p.Address  AS Address,
    p.Address AS Street
      FROM [dbo].[Properties] AS p
    WHERE 
       p.WPRN NOT IN( SELECT ID FROM @PropIDs)
    '

and execute that. OR as mentioned - change @ProdIDs to a temporary table. (The route you're approaching in the CREATE #ProdIds, but then you need to use #ProdIDs instead of @ProdIDs everywhere in the sproc).

并执行那个。或者如前所述 - 将@ProdIDs 更改为临时表。(您在 CREATE #ProdIds 中接近的路线,但随后您需要在 sproc 中的任何地方使用 #ProdIDs 而不是 @ProdIDs)。

回答by GarethD

The reason you get this error is that the scope of table variables is limited to a single batch, since sp_executesqlruns in its own batch, it has no knowledge that you have declared it in another batch.

您收到此错误的原因是表变量的范围仅限于单个批次,因为sp_executesql在其自己的批次中运行,因此它不知道您已在另一批次中声明它。

It works when you @NotNeededWPRNsis NULLbecause concatenating NULLyields NULL(unless otherwise set), so you are just executing:

它在您@NotNeededWPRNsNULL因为连接NULL产量NULL(除非另有设置)时起作用,所以您只是在执行:

exec sp_executesql null;

I would also say, if you are using SQL Server 2008 or later pleaseconsider using table valued parametersinstead of a delimited list of strings. This is much safer and more efficient, and validates the input, if I were to pass 1); DROP TABLE dbo.Prioperties; --as @NotNeededWPRNs, you could find yourself without a properties table.

我还要说,如果您使用的是 SQL Server 2008 或更高版本,考虑使用表值参数而不是分隔的字符串列表。这更安全、更有效,并验证输入,如果我传递1); DROP TABLE dbo.Prioperties; --as @NotNeededWPRNs,您可能会发现自己没有属性表。

First you would need to create the type (I tend to use a generic name for reusability):

首先,您需要创建类型(为了可重用,我倾向于使用通用名称):

CREATE TYPE dbo.IntegerList TABLE (Value INT);

Then you can add it to your procedure:

然后您可以将其添加到您的程序中:

CREATE PROCEDURE [dbo].[GetNeededProperties]
    @NotNeededWPRNs dbo.IntegerList READONLY,
    @LastSynch DATETIME,
    @TechCode VARCHAR(5)
    AS
    BEGIN

    SELECT  p.WPRN AS ID,
            p.Address  AS Address,
            p.Address AS Street
     FROM   [dbo].[Properties] AS p
    WHERE   p.WPRN NOT IN (SELECT Value FROM @NotNeededWPRNs)

On an unrelated note, you should avoid using culture sensitive date formats where possible, 06/28/2013is clearly supposed to be 28th June in this case, but what about 06/07/2013, without setting DATEFORMAT, or the language how do you know if this will be read as 6th July or 7th June? The best format to use is yyyyMMdd, it is never ambiguous, even the ISO standard format yyyy-MM-dd can be interpreted as yyyy-dd-MMin some settings.

在一个不相关的说明中,您应该尽可能避免使用文化敏感的日期格式,06/28/2013在这种情况下显然应该是 28th June,但是如果06/07/2013没有设置DATEFORMAT,或者语言如何知道这是否会被读取为 6th July 或6 月 7 日?最好使用的格式是yyyyMMdd,它永远不会有歧义,即使是 ISO 标准格式 yyyy-MM-dd 也可以yyyy-dd-MM在某些设置中解释为。

回答by Raj

Change you code to :

将您的代码更改为:

Declare @ProductsSQL nvarchar(max);
SET @ProductsSQL = 'DECLARE @PropIDs TABLE
(ID bigint);
Insert into @PropIDs (ID) 
SELECT [WPRN] FROM [dbo].[Properties] WHERE(WPRN in (' + @NotNeededWPRNs + '))'
exec sp_executesql @ProductsSQL

Table variable declared outside the dynamic SQL will not be available to the dynamic SQL.

在动态 SQL 之外声明的表变量将不可用于动态 SQL。

回答by user3041160

You can avoid using dynamic sql by creating a sql function that use a CTE (I found the code below many years ago on sqlservercentral - Amit Gaur) :

您可以通过创建一个使用 CTE 的 sql 函数来避免使用动态 sql(我多年前在 sqlservercentral - Amit Gaur 上找到了下面的代码):

Change the body of your procs with something like this :

用这样的东西改变你的 procs 的主体:

SELECT  p.WPRN AS ID,
p.Address  AS Address,
p.Address AS Street
  FROM [dbo].[Properties] AS p
WHERE 
   p.WPRN NOT IN ( SELECT item FROM dbo.strToTable(@NotNeededWPRNs, ','))

Below the sql code that transforms a string into a table :

在将字符串转换为表的 sql 代码下方:

CREATE FUNCTION [dbo].[strToTable] 
(
    @array varchar(max),
    @del char(1)
)
RETURNS 
@listTable TABLE 
(
    item int
)
AS
BEGIN

    WITH rep (item,list) AS
    (
        SELECT SUBSTRING(@array,1,CHARINDEX(@del,@array,1) - 1) as item,
        SUBSTRING(@array,CHARINDEX(@del,@array,1) + 1, LEN(@array)) + @del list

        UNION ALL

        SELECT SUBSTRING(list,1,CHARINDEX(@del,list,1) - 1) as item,
        SUBSTRING(list,CHARINDEX(@del,list,1) + 1, LEN(list)) list
        FROM rep
        WHERE LEN(rep.list) > 0
    )
    INSERT INTO @listTable
    SELECT item FROM rep

    RETURN 
END