简体   繁体   English

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

[英]“Must declare the table variable ”@name“” in stored procedure

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

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. 您需要做的是使@PropsIDs表成为非表变量,并使之成为临时表或物理表。 OR you need to wrap everything in dynamic SQL and execute it. 或者,您需要将所有内容包装在动态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. 或者如前所述-将@ProdIDs更改为临时表。 (The route you're approaching in the CREATE #ProdIds, but then you need to use #ProdIDs instead of @ProdIDs everywhere in the sproc). (您正在CREATE #ProdIds中接近的路由,但是随后在存储过程中的所有位置都需要使用#ProdIDs而不是@ProdIDs)。

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: @NotNeededWPRNsNULL时,它可以工作,因为串联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. 我还要说的是,如果您使用的是SQL Server 2008或更高版本, 考虑使用表值参数而不是带分隔符的字符串列表。 This is much safer and more efficient, and validates the input, if I were to pass 1); DROP TABLE dbo.Prioperties; -- 如果我通过1); DROP TABLE dbo.Prioperties; --这将更安全,更有效,并验证输入1); DROP TABLE dbo.Prioperties; -- 1); DROP TABLE dbo.Prioperties; -- 1); DROP TABLE dbo.Prioperties; -- as @NotNeededWPRNs , you could find yourself without a properties table. 1); DROP TABLE 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? 在一个不相关的音符,你应该避免使用敏感的文化日期格式在可能情况下, 06/28/2013显然应该是6月28日在此情况下,但对于06/07/2013 ,没有设置DATEFORMAT ,或语言怎么办你知道这是7月6日还是6月7日吗? 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. 最好使用的格式是yyyyMMdd ,它永远不会模棱两可,即使在某些设置中,即使ISO标准格式yyyy-MM-dd都可以解释为yyyy-dd-MM

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不可用。

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 : 像下面这样更改您的proc的主体:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM