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

时间:2022-11-18 22:49:30

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)执行过程时,它返回一个错误。

4 个解决方案

#1


11  

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).

和执行。或者如前所述,将@天才更改为临时表。(你在CREATE #天才中接近的路线,但是你需要在sproc中使用#天才而不是@天才。)

#2


4  

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

您得到这个错误的原因是,表变量的范围仅限于单个批处理,因为sp_executesql在它自己的批处理中运行,它不知道您已经在另一个批处理中声明了它。

It works when you @NotNeededWPRNs is NULL because concatenating NULL yields NULL (unless otherwise set), so you are just executing:

当您@NotNeededWPRNs为空时,它是有效的,因为连接NULL值为NULL(除非另有设置),所以您正在执行:

exec sp_executesql null;

I would also say, if you are using SQL Server 2008 or later please consider using table valued parameters instead 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);删除表dbo.Prioperties;@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/2013 is 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-MM in some settings.

在不相关的说明上,你应该避免使用文化敏感的日期格式,在这种情况下,6/28/2013显然应该是6月28日,但是如果没有设置DATEFORMAT或语言,你怎么知道这将会在7月6日或6月7日被阅读呢?使用的最佳格式是yyyyMMdd,它从不含糊,即使是ISO标准格式yyyy-MM-dd也可以在某些设置中被解释为yyyy-dd-MM。

#3


1  

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。

#4


0  

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

#1


11  

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).

和执行。或者如前所述,将@天才更改为临时表。(你在CREATE #天才中接近的路线,但是你需要在sproc中使用#天才而不是@天才。)

#2


4  

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

您得到这个错误的原因是,表变量的范围仅限于单个批处理,因为sp_executesql在它自己的批处理中运行,它不知道您已经在另一个批处理中声明了它。

It works when you @NotNeededWPRNs is NULL because concatenating NULL yields NULL (unless otherwise set), so you are just executing:

当您@NotNeededWPRNs为空时,它是有效的,因为连接NULL值为NULL(除非另有设置),所以您正在执行:

exec sp_executesql null;

I would also say, if you are using SQL Server 2008 or later please consider using table valued parameters instead 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);删除表dbo.Prioperties;@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/2013 is 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-MM in some settings.

在不相关的说明上,你应该避免使用文化敏感的日期格式,在这种情况下,6/28/2013显然应该是6月28日,但是如果没有设置DATEFORMAT或语言,你怎么知道这将会在7月6日或6月7日被阅读呢?使用的最佳格式是yyyyMMdd,它从不含糊,即使是ISO标准格式yyyy-MM-dd也可以在某些设置中被解释为yyyy-dd-MM。

#3


1  

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。

#4


0  

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