Sql server 在函数中循环时,错误:超过了最大存储过程、函数、触发器或视图嵌套级别(限制32)

Sql server 在函数中循环时,错误:超过了最大存储过程、函数、触发器或视图嵌套级别(限制32),sql-server,tsql,stored-procedures,Sql Server,Tsql,Stored Procedures,我是SQL Server 2008的新手。非常感谢您的帮助。如果我写的存储过程是一个耗时的因素,请有人帮我写一个更好的方法 Table Name: tblProduct Column Names: Product No, S1_CR, S1_CAT1, S1_CAT2, S1_CAT3, S1_CAT4 Eg Column Values: 1234, Prod1#Prod2#Prod3, 10#200#300, 20#34#400, 40#12#12, 50#23#12 我试图填充一个表,该表

我是SQL Server 2008的新手。非常感谢您的帮助。如果我写的存储过程是一个耗时的因素,请有人帮我写一个更好的方法

Table Name: tblProduct
Column Names: Product No, S1_CR, S1_CAT1, S1_CAT2, S1_CAT3, S1_CAT4
Eg Column Values: 1234, Prod1#Prod2#Prod3, 10#200#300, 20#34#400, 40#12#12, 50#23#12
我试图填充一个表,该表将显示为(当输入为Prod2时)

执行以下存储过程时出现以下错误

错误:超过最大存储过程、函数、触发器或视图嵌套级别(限制32)

存储过程有一个函数(instr),调用该函数可查找字符串中哈希的出现次数,以帮助填充表

存储过程:

Alter Procedure spPopulate
@Code varchar(10),
@Test int,
@Product varchar(10),
@Year varchar(4)
as
Begin 

Declare @Pos int

select @Pos = LEN(SUBSTRING(S1_CR, 0, CHARINDEX(@Code, S1_CR, 0))) - LEN(REPLACE(SUBSTRING(S1_CR, 0, CHARINDEX(@Code, S1_CR, 0)), '#'  , '')) from tblProduct where Product =@Product and Year = @Year

select ProductNo, s1_cr, s1_cat1, S1_CAT2, S1_CAT3, S1_CAT4,

case
when @Test >= 1 then right(LEFT(s1_CAT1, dbo.INSTR(S1_CAT1+ '#', '#', 1, @Pos+1)-1), (dbo.INSTR(S1_CAT1+ '#', '#', 1, @Pos+1)-1)- (dbo.INSTR(S1_CAT1+ '#', '#', 1, @Pos)+1)+1) end as CAT1,

case 
when @Test >= 2 then right(LEFT(s1_CAT2, dbo.INSTR(S1_CAT2+ '#', '#', 1, @Pos+1)-1), (dbo.INSTR(S1_CAT2+ '#', '#', 1, @Pos+1)-1)- (dbo.INSTR(S1_CAT2+ '#', '#', 1, @Pos)+1)+1) end as CAT2,

case 
when @Test >= 3 then right(LEFT(s1_CAT3, dbo.INSTR(S1_CAT3+ '#', '#', 1, @Pos+1)-1), (dbo.INSTR(S1_CAT3+ '#', '#', 1, @Pos+1)-1)- (dbo.INSTR(S1_CAT3+ '#', '#', 1, @Pos)+1)+1) end AS CAT3,

case 
when @Test >= 4 then right(LEFT(s1_CAT4, dbo.INSTR(S1_CAT4+ '#', '#', 1, @Pos+1)-1), (dbo.INSTR(S1_CAT4+ '#', '#', 1, @Pos+1)-1)- (dbo.INSTR(S1_CAT4+ '#', '#', 1, @Pos)+1)+1) end AS CAT4

from Product where Product=@Product and Year = @Year ;

End

Execute spPopulate @Code = 'Prod1', @Product= 'ProductName1', @Year = '2010', @Test = 1
请在下面找到instr的功能

  ALTER FUNCTION [dbo].[INSTR] (@str VARCHAR(8000), @substr VARCHAR(255), @start INT, @occurrence INT)
  RETURNS INT
  AS
  BEGIN
    DECLARE @found INT = @occurrence,
            @pos INT = @start;

    WHILE 1=1 
    BEGIN
        -- Find the next occurrence
        SET @pos = CHARINDEX(@substr, @str, @pos);

        -- Nothing found
        IF @pos IS NULL OR @pos = 0
            RETURN @pos;

        -- The required occurrence found
        IF @found = 1
            BREAK;

        -- Prepare to find another one occurrence
        SET @found = @found - 1;
        SET @pos = @pos + 1;
    END

    RETURN @pos;
  END

您收到的错误消息与递归调用有关

在您的解决方案中,您没有将“GO”放在过程的“END”和测试执行之间。
很可能是您将测试的执行编译成了过程代码本身。

我将假设此练习的目的是将值从列中分离出来,以便您可以对数据进行标准化

  • 关于实际错误,请阅读@Amir的答案
  • 关于帮助你解决问题,看看我下面的方法,希望你能从中得到一些帮助

  • 字符串处理通常非常慢……特别是如果您编写了InStr()方法,并且如果应用于所有或多行……您通常可以用LIKEIt替换。看起来正确的解决方案是更好的数据结构。使用散列来分隔值,然后尝试使用查询将其拆分,这与关系数据库的用途背道而驰。您将使用更好的数据结构解决递归错误和速度问题。100%同意@Lathejockey81:Normalise@Lathejockey81 sir/mam,您能建议我如何为该表提供更好的数据结构吗。提前感谢您抽出时间。如果你能给我发消息,我可以在一些网站上找到一些好的数据库设计,这对像meWow这样的新手也会很有帮助。。我必须衷心感谢你的努力和所花的时间。我在执行这部分代码时遇到问题,请您帮助我,否则如果“@”index=0和“@”term_count“@”num_terms和“@”reviewing“@”S1_CR--num_terms在查看第一列时将为零。这是我正在获取的错误-Msg 102,级别15,状态1,第70行“@'num_terms”附近的语法不正确。消息156,级别15,状态1,第72行关键字“else”附近语法不正确。再次感谢。@Katheeja-当我粘贴“对不起”时被删除了。。。替换为“如果'@index=0和'@term\u count'@num\u terms和'@reviewing'@S1\u CR',则需要从变量namesWow.中删除前导单引号。”。。谢谢你。。太好了。上帝保佑你。我会一次又一次地研究代码:-)你可能不理解我这一辈子的兴奋。再次感谢您的时间。如何修改代码以获得与Prod2匹配的所有记录。插入到“@源价值(1234)、源价值(1234)、源价值(1234)、Prod1#;; Prod1#;生产3#生产3#生产3#;生产3#;生产3#生产3#生产3 35;生产3 35;生产3 35;生产4、生产4、生产4、1 3555 355 355#生产4、1、1、1、1 3555 5 5 8 3558 3555 8 3555 535; 8)8 3555 8 8 8 8 35;; 8 3555 5 5 5 5 5 5 5 5 8 8 35;; 8、2、2、2、2、2、2、2、2、2、5、4、3、4、3、4、4、2、1、3、4、3、4、2、1、1、2、2、2、2、2、2、2、2、2、3、3、3、3、4、3、4、3、4、3、4、4、3、4、3、4、4、4、3、5、5、5、5、5、5、4、4、4、4、3、5、5、4、5、5、4、5、4、4、3、3、5、3、5、5、5、5、5、4、5、5、4、5、5、4、4、3、3、3、3、3、5、5、3、5、5、5、5、5、5、5、5、5、5、4、5、5、4、5、5、5、4、5、5、5、4、3、3、3、5、3、5、5、5、5、5#8')我希望最终目的是将所有数据从这种疯狂的格式中解放出来。假设这是所有的历史数据,并且您正在取出并放入一个新表,那么,1。从查询末尾的结果集中删除筛选器,2.按照预期将其放入存储过程中,3。对每个prodID执行一次该过程,4.使用prodID/ProdCode将返回的行存储到新表中以获得唯一性。5.然后,您可以随意查询新表中的所有数据,这将很容易。
      ALTER FUNCTION [dbo].[INSTR] (@str VARCHAR(8000), @substr VARCHAR(255), @start INT, @occurrence INT)
      RETURNS INT
      AS
      BEGIN
        DECLARE @found INT = @occurrence,
                @pos INT = @start;
    
        WHILE 1=1 
        BEGIN
            -- Find the next occurrence
            SET @pos = CHARINDEX(@substr, @str, @pos);
    
            -- Nothing found
            IF @pos IS NULL OR @pos = 0
                RETURN @pos;
    
            -- The required occurrence found
            IF @found = 1
                BREAK;
    
            -- Prepare to find another one occurrence
            SET @found = @found - 1;
            SET @pos = @pos + 1;
        END
    
        RETURN @pos;
      END
    
    -- set up our sample datasource
    declare @source as table 
    (
        ProductNo int, 
        S1_CR varchar(100), 
        S1_CAT1 varchar(100), 
        S1_CAT2 varchar(100), 
        S1_CAT3 varchar(100), 
        S1_CAT4 varchar(100)
    )
    
    insert into @source values (1234, 'Prod1#Prod2#Prod3', '10#200#300', '20#34#400', '40#12#12', '50#23#12')
    
    -- set up what will become input parameters to our sp
    declare @prodNo     int = 1234
    declare @code       varchar(10) = 'Prod2'
    --Note that there are not Columns named Product, Year in the sample data but you refer to it in your Query so I assume they exist
    declare @Product    varchar(10) -- is your declaration long enough? calling your sp you use 'ProductName1' which wont fit.
    declare @year       varchar(4) = '2010'
    
    -- this could be a constant if it doesnt change from record to record,
    -- or a parameter if it does
    declare @delimiter  char    = '#'
    
    --declare our variables
    declare @output     table
    (
        ProductNo int, 
        S1_CR   varchar(100), 
        S1_CAT1 varchar(100), 
        S1_CAT2 varchar(100), 
        S1_CAT3 varchar(100), 
        S1_CAT4 varchar(100)
    )
    
    declare @ProductNo  int 
    declare @S1_CR      varchar(100)
    declare @S1_CAT1    varchar(100) 
    declare @S1_CAT2    varchar(100) 
    declare @S1_CAT3    varchar(100) 
    declare @S1_CAT4    varchar(100)
    
    --these ones are used during validation
    declare @index      int = 1     
    declare @term_count int = 0 -- this counts the number of terms in the column we are looking at
    declare @num_terms  int = 0 -- this becomes our fixed comparison value
    declare @reviewing  varchar(100) -- this is the data from the column we are looking at
    
    select 
        @ProductNo = s.ProductNo,
        @S1_CR = s.S1_CR + @delimiter,
        @S1_CAT1 = s.S1_CAT1 + @delimiter,  
        @S1_CAT2 = s.S1_CAT2 + @delimiter,
        @S1_CAT3 = s.S1_CAT3 + @delimiter,
        @S1_CAT4 = s.S1_CAT4 + @delimiter
    from @source s
    where s.ProductNo = @prodNo
    
    set @reviewing = @S1_CR
    
    --validate to ensure there are equal number of concatenated terms in each column
    --you will probably want to do other validation on inputs etc.
    while @index > 0 --and @num_terms > 0
    begin
    
        set @index = CHARINDEX(@delimiter, @reviewing, @index +1)
    
        if @index > 0 
            set @term_count = @term_count + 1
        else if @index = 0 and @term_count  @num_terms and @reviewing  @S1_CR -- num_terms will be zero when reviewing the first column
            set @num_terms = 0
        else 
            begin
    
                select 
                    @num_terms = case @reviewing
                                    when @S1_CR then @term_count
                                    else @num_terms
                                end,
                    @reviewing = case @reviewing
                                    when @S1_CR then @S1_CAT1
                                    when @S1_CAT1 then @S1_CAT2
                                    when @S1_CAT2 then @S1_CAT3
                                    when @S1_CAT3 then @S1_CAT4 
                                    else ''
                                end
    
                if @reviewing = ''
                    set @index = 0
                else
                    set @index = 1
                    set @term_count = 0
            end
    end
    
    --Split out the terms
    while @num_terms > 0
    begin    
        insert into @output values
        (
            @ProductNo,
            left(@S1_CR,CHARINDEX(@delimiter,@S1_CR,1) -1),
            left(@S1_CAT1,CHARINDEX(@delimiter,@S1_CAT1,1) -1),
            left(@S1_CAT2,CHARINDEX(@delimiter,@S1_CAT2,1) -1),
            left(@S1_CAT3,CHARINDEX(@delimiter,@S1_CAT3,1) -1),
            left(@S1_CAT4,CHARINDEX(@delimiter,@S1_CAT4,1) -1)
        )
    
        set @S1_CR = right(@S1_CR,len(@S1_CR) - CHARINDEX(@delimiter,@S1_CR,1))
        set @S1_CAT1 = right(@S1_CAT1,len(@S1_CAT1) - CHARINDEX(@delimiter,@S1_CAT1,1))
        set @S1_CAT2 = right(@S1_CAT2,len(@S1_CAT2) - CHARINDEX(@delimiter,@S1_CAT2,1))
        set @S1_CAT3 = right(@S1_CAT3,len(@S1_CAT3) - CHARINDEX(@delimiter,@S1_CAT3,1))
        set @S1_CAT4 = right(@S1_CAT4,len(@S1_CAT4) - CHARINDEX(@delimiter,@S1_CAT4,1))
    
        set @num_terms = @num_terms - 1
    end
    
    select * from @output where S1_CR = @code