none
Que erro e esse ( Trigger) RRS feed

  • Pergunta

  • Prezados fiz essa Trigger para poder fazer um insert (Update) num campo de uma tabela, a trigger foi criado sem erro, porem quando eu faço o lançamento na tabela me retorna uma mensagem de erro.

    TRIGGER

    set ANSI_NULLS ON
    set QUOTED_IDENTIFIER ON
    go
    ALTER  TRIGGER [RESULTADO] ON [dbo].[MTRFCOMPL]
    FOR INSERT
    AS
    DECLARE @IDPRJ INT
    DECLARE @IDTRF INT
    DECLARE CURSORIDPRJ CURSOR FOR
    SELECT  isnull(convert(decimal(10,3), T),0)  * isnull(convert(decimal(10,3), Y01),0)
    * isnull(convert(decimal(10,3), Y02),0) AS [RESULTADO],
       Y01,Y02,Y03,Y04,RES FROM MTRFCOMPL
    OPEN CURSORIDPRJ
    FETCH NEXT FROM CURSORIDPRJ INTO @IDPRJ,@IDTRF
    WHILE @@FETCH_STATUS = 0
    BEGIN
    UPDATE MTRFCOMPL SET RES =
      isnull(convert(decimal(10,3), T),0)  * isnull(convert(decimal(10,3), Y01),0)* isnull(convert(decimal(10,3), Y02),0)
    FROM MTRFCOMPL
    WHERE IDPRJ = @IDPRJ AND IDTRF = @IDTRF
    FETCH NEXT FROM CURSORIDPRJ INTO @IDPRJ
    END
    CLOSE CURSORIDPRJ
    DEALLOCATE CURSORIDPRJ


    mensagem de erro
    General SQL error.
    Cursorfetch: The number of variables declared in the INTO list must match that of selected columns.

    Com isso o sistema nao me permite salver. agradeço a ajuda de todos vcs.

    terça-feira, 23 de março de 2010 20:45

Respostas

  • Olá,

    Você está melhorando, mas ainda falta um pouco mais a ser feito. Quando a idéia é trabalhar com linhas, você não deve fazer a declaração de variáveis, pois, isso irá levar a problemas (o artigo inclusive fala sobre isso). Retire a declaração de variáveis e a cláusula WHERE e inclua um JOIN com a INSERTED. Assim todas as linhas são contempladas sem a necessidade de cursor. Ex:

    UPDATE MTRF SET CAMPOLIVREVALOR1 = 
         isnull(convert(decimal(10,3), MT.Y01),0) *
           isnull(convert(decimal(10,3), MT.Y02),0)
          FROM MTRF 
    INNER JOIN MTRFCOMPL AS MT ON MT.CODCOLIGADA = MTRF.CODCOLIGADA AND MT.IDPRJ = MTRF.IDPRJ AND MT.IDTRF = MTRF.IDTRF
    INNER JOIN INSERTED AS I ON MT.ID = I.ID AND MT.IDPRJ = I.IDPRJ AND MT.IDTRF = I.IDTRF

    Se você necessita trocar, então a trigger não deve ser somente para eventos de INSERT, mas também para eventos de UPDATE.

    [ ]s,

    Gustavo Maia Aguiar
    http://gustavomaiaaguiar.spaces.live.com

    Como descobrir a data do último acesso a uma tabela ?
    http://gustavomaiaaguiar.spaces.live.com/blog/cns!F4F5C630410B9865!964.entry


    Classifique as respostas. O seu feedback é imprescindível
    • Marcado como Resposta Ronnie Von terça-feira, 27 de abril de 2010 19:11
    quinta-feira, 25 de março de 2010 16:57

Todas as Respostas

  • Você está declarando o cursor com as 6 colunas  [RESULTADO],Y01,Y02,Y03,Y04,RES .

    Entretanto na hora de atribuir esses valores as variáveis, você não está especificando o mesmo número de variáveis e colunas do cursor.

    FETCH NEXT FROM CURSORIDPRJ INTO @IDPRJ,@IDTRF -- só 2 variáveis

    e

    FETCH NEXT FROM CURSORIDPRJ INTO @IDPRJ -- só 1 variável



    Fabrício França Lima | MCP, MCTS, MCITP | Visite meu site: http://fabriciodba.spaces.live.com/
    terça-feira, 23 de março de 2010 23:25
  • Fabricio  bom dia, fiz a alteração conforme vc tinha bem observado, porem o mensagem de erro continua aparecendo

    set ANSI_NULLS ON
    set QUOTED_IDENTIFIER ON
    go
    ALTER  TRIGGER [RESULTADO] ON [dbo].[MTRFCOMPL]
    FOR INSERT
    AS
    DECLARE @IDPRJ INT
    DECLARE @IDTRF INT
    DECLARE CURSORIDPRJ CURSOR FOR
    SELECT  isnull(convert(decimal(10,3), T),0)  * isnull(convert(decimal(10,3), Y01),0)
    * isnull(convert(decimal(10,3), Y02),0) AS [RESULTADO]
       FROM MTRFCOMPL
    OPEN CURSORIDPRJ
    FETCH NEXT FROM CURSORIDPRJ INTO @IDPRJ,@IDTRF
    WHILE @@FETCH_STATUS = 0
    BEGIN
    UPDATE MTRFCOMPL SET RES =
      isnull(convert(decimal(10,3), T),0)  * isnull(convert(decimal(10,3), Y01),0)* isnull(convert(decimal(10,3), Y02),0)
    FROM MTRFCOMPL
    WHERE IDPRJ = @IDPRJ AND IDTRF = @IDTRF
    FETCH NEXT FROM CURSORIDPRJ INTO @IDPRJ,@IDTRF
    END
    CLOSE CURSORIDPRJ
    DEALLOCATE CURSORIDPRJ

     

    mensagem de erro

    mensagem de erro
    General SQL error.
    Cursorfetch: The number of variables declared in the INTO list must match that of selected columns.

     

    obrigado pela ajuda

     

    quarta-feira, 24 de março de 2010 12:55
  • Ronnie,

    Agora você está montando o cursor com 1 coluna  [RESULTADO] e está tentando atribuir valores a 2 variáveis @IDPRJ,@IDTRF .

    Tira uma dessas e verifica se o erro continua.



    Fabrício França Lima | MCP, MCTS, MCITP | Visite meu site: http://fabriciodba.spaces.live.com/ Novo Post: Criando um CheckList Automático do Banco de Dados
    quarta-feira, 24 de março de 2010 13:07
  • Ronni von,

    a consulta do cursor está retornando apenas um resultado e o fetch precisa de 2. O select do cursor só tem o valor abaixo:

    isnull(convert(decimal(10,3), T),0)  * isnull(convert(decimal(10,3), Y01),0)
    * isnull(convert(decimal(10,3), Y02),0) AS [RESULTADO]


    Se a resposta resolveu sua questão ou problema, classifique-a para manter a qualidade do forum e a confiabilidade dos participantes.

    Alex M. Bastos
    http://bastosalex.spaces.live.com
    quarta-feira, 24 de março de 2010 13:08
  • Prezados, Alex, e Fabricio, mudei essa trigger conforme as explicaçoes de vcs, quando eu incluo o lançamento nao me aparece mais o erro, porem o campo que deveria receber o calculo que e RES, nao esta recebendo,

    ele so recebe a informação quando eu vou na trigger e executo o Update ai ele atualiza,

     

    set ANSI_NULLS ON
    set QUOTED_IDENTIFIER ON
    go
    ALTER  TRIGGER [RESULTADO] ON [dbo].[MTRFCOMPL]
    FOR INSERT
    AS
    DECLARE @IDPRJ INT
    DECLARE @IDTRF INT
    DECLARE CURSORIDPRJ CURSOR FOR
    SELECT IDPRJ,isnull(convert(decimal(10,3), T),0)  * isnull(convert(decimal(10,3), Y01),0)
    * isnull(convert(decimal(10,3), Y02),0) AS [RESULTADO]
       FROM MTRFCOMPL
    OPEN CURSORIDPRJ
    FETCH NEXT FROM CURSORIDPRJ INTO @IDPRJ,@IDTRF
    WHILE @@FETCH_STATUS = 0
    BEGIN
    UPDATE MTRFCOMPL SET RES =
      isnull(convert(decimal(10,3), T),0)  * isnull(convert(decimal(10,3), Y01),0)* isnull(convert(decimal(10,3), Y02),0)
    FROM MTRFCOMPL

    WHERE IDPRJ = @IDPRJ AND IDTRF = @IDTRF
    FETCH NEXT FROM CURSORIDPRJ INTO @IDPRJ,@IDTRF
    END
    CLOSE CURSORIDPRJ
    DEALLOCATE CURSORIDPRJ

    Nao era para ser automatico, atualizar no momento que mando salvar.

    Obrigado a todos

    quarta-feira, 24 de março de 2010 14:16
  • Ronnie Von,

    a variável @IDTRF do cursor é int, mas o select do cursor retorna data type decimal para esta variável. Isto está correto?

    Para ver se a trigger está sendo disparada, eu criaria uma tabela de log e faria um INSERT nela quando o trigger fosse disparado colocando os valores que está manuseando no trigger.

    Além disso, o trigger só é disparado quando dá o commit da transação que o disparou. Depois do insert vc dá commit ou rollback?

    Coloque aqui o resultado do teste do log para podermos continuar lhe ajudando.


    Se a resposta resolveu sua questão ou problema, classifique-a para manter a qualidade do forum e a confiabilidade dos participantes.

    Alex M. Bastos
    http://bastosalex.spaces.live.com
    quinta-feira, 25 de março de 2010 12:16
  • Bom Dia,

    Acredito que ao invés de tentarmos descobrir porque o cursor não está funcionando seja muito mais efetivo postar uma solução mais performática, pois, nesse caso o CURSOR é completamente desnecessário além do que há possíveis erros de lógica. A leitura do código me parece a seguinte:

    Para cada registro em MTRFCOMPL
     Atualize a tabela MTRFCOMPL onde a coluna IDPRJ e IDTRF seja igual ao do registo capturado
    Fim

    Não entendi porque varrer a tabela MTRFCOMPL e atualizá-la dentro de uma trigger. Isso será redudante e muito pouco performático, pois, em outras palavras, para cada INSERT, a tabela inteira será varrida e atualizada (não há filtros no cursor). E se fosse essa a lógica, bastaria um simples comando de UPDATE. Além de mais eficiente, representa muito menos código o que reflete menor dificuldade de manutenção. Sugiro a leitura do artigo abaixo:

    Piores Práticas – Elaborar triggers preparadas para linhas e não para conjuntos
    http://gustavomaiaaguiar.spaces.live.com/blog/cns!F4F5C630410B9865!937.entry

    [ ]s,

    Gustavo Maia Aguiar
    http://gustavomaiaaguiar.spaces.live.com

    Como descobrir a data do último acesso a uma tabela ?
    http://gustavomaiaaguiar.spaces.live.com/blog/cns!F4F5C630410B9865!964.entry


    Classifique as respostas. O seu feedback é imprescindível
    • Sugerido como Resposta Alex M. Bastos sexta-feira, 26 de março de 2010 17:53
    quinta-feira, 25 de março de 2010 12:32
  • OLa Pessoal, tentando explicar melhor essa trigger, tenho na tabela MTRFCOMPL os seguinte campos T,Y01,Y02, O resultado desse calculo deveria ser inserido na Coluna UPDATE MTRFCOMPL SET RES,  

    a minha intencao com essa trigger e fazer isso,

    Obrigado a todos

     

    quinta-feira, 25 de março de 2010 12:54
  • Bom Dia,

    De fato o cursor não é necessário nesse caso. Veja o exemplo abaixo:

    -- Cria a tabela
    CREATE TABLE T (ID INT, C1 INT, C2 INT, C3 INT)
    GO
    -- Cria a trigger
    CREATE TRIGGER TR ON T
    FOR INSERT
    AS
    -- O uso da INSERTED irá impedir que linhas que já estejam atualizadas sejam atualizadas de novo
    -- Atualizar as linhas de novo é ineficiente
    UPDATE T SET T.C3 = T.C1 + T.C2
    FROM T
    INNER JOIN INSERTED AS I ON T.ID = I.ID
    GO
    -- Insere um registro
    INSERT INTO T (ID, C1, C2) VALUES (1, 2, 2)
    -- Verifica o retorno (o cálculo foi feito)
    SELECT ID, C1, C2, C3 FROM T
    -- Insere dois registros
    INSERT INTO T (ID, C1, C2) SELECT 2, 3, 4 UNION ALL SELECT 3, 5, 6
    -- Verifica o retorno (o cálculo foi feito)
    SELECT ID, C1, C2, C3 FROM T

    Analisando melhor,  nem a trigger talvez seja de fato necessária.

    -- Dropa a coluna com o cálculo
    ALTER TABLE T DROP COLUMN C3
    -- Refaz a coluna para uma coluna calculada
    ALTER TABLE T ADD C3 As C1 + C2
    -- Verifica o cálculo
    SELECT ID, C1, C2, C3 FROM T

    E analisando melhor ainda, nem a coluna calculada é de fato necessária.

    CREATE VIEW V AS
    SELECT ID, C1, C2, C1 + C2 As C3 FROM T
    GO
    SELECT ID, C1, C2, C3 FROM V 

    [ ]s,

    Gustavo Maia Aguiar
    http://gustavomaiaaguiar.spaces.live.com

    Como descobrir a data do último acesso a uma tabela ?
    http://gustavomaiaaguiar.spaces.live.com/blog/cns!F4F5C630410B9865!964.entry


    Classifique as respostas. O seu feedback é imprescindível
    • Sugerido como Resposta Alex M. Bastos sexta-feira, 26 de março de 2010 17:53
    quinta-feira, 25 de março de 2010 13:16
  • Ola Pessoal Desculpa  a minha Ignorancia, mas esta complicado, Gustavo olhando no seu Blog, no topico Elaborar triggers preparadas para linhas e não para conjuntos,

    fiz uma aqui baseado na seu exemplo, e tb nao funcionou,

     

    Veja como ficou

    -- "Prepara" a trigger para operar múltiplas linhas
    ALTER TRIGGER [RES] ON [dbo].[MTRFCOMPL]
    FOR INSERT
    AS
    BEGIN
        -- Declara as variáveis para atualização
          DECLARE @IDPRJ INT, @IDTRF INT

        -- Declara um cursor para "varrer" a INSERTED
        DECLARE RESULT CURSOR
        FAST_FORWARD
        FOR SELECT
           isnull(convert(decimal(10,3), Y01),0) *
           isnull(convert(decimal(10,3), Y02),0)
        FROM MTRFCOMPL WHERE IDPRJ = '192'

       /* ProdutoID, Quantidade, DataVenda FROM INSERTED*/
        -- É necessário ordenar para evitar que uma venda mais antiga atualize a data erroneamente
        ORDER BY IDPRJ, IDTRF    

        -- Abre o cursor
        OPEN RESULT

        -- Lê o primeiro registro
        FETCH NEXT FROM MTRFCOMPL INTO @IDPRJ, @IDTRF

        -- Varre os demais registros
        WHILE @@FETCH_STATUS = 0
        BEGIN
            -- Atualiza o estoque
       UPDATE  MTRFCOMPL SET   RES  =
            isnull(convert(decimal(10,3), Y01),0) *
           isnull(convert(decimal(10,3), Y02),0)
            FROM MTRFCOMPL
                WHERE  @IDPRJ = IDPRJ AND @IDTRF = IDTRF

            -- Passa para o próximo registro
              FETCH NEXT FROM MTRFCOMPL INTO @IDPRJ, @IDTRF
        END

        -- Fecha o cursor
        CLOSE RESULT

        -- Desaloca o cursor
        DEALLOCATE RESULT
    END

    obrigado a todos

    quinta-feira, 25 de março de 2010 13:49
  • Bom Dia,

    A idéia do artigo é justamente evitar cursores (veja que ao final tem soluções com um único update).
    Qual é a chave primária dessa tabela ? Você chegou a ver os exemplos que coloquei nesse post para evitar o cursor ?

    [ ]s,

    Gustavo Maia Aguiar
    http://gustavomaiaaguiar.spaces.live.com

    Como descobrir a data do último acesso a uma tabela ?
    http://gustavomaiaaguiar.spaces.live.com/blog/cns!F4F5C630410B9865!964.entry


    Classifique as respostas. O seu feedback é imprescindível
    quinta-feira, 25 de março de 2010 14:09
  • Ola Gustavo, As chaves primarias Sao os ID, IDPRJ, IDTRF, Vendo alguns exemplo  no blog, consegui fazer um aqui sam usar Cursor, veja como ficou

    ALTER TRIGGER [RESULTADO] ON [dbo].[MTRFCOMPL]
    FOR INSERT
    AS
    BEGIN
     DECLARE @IDPRJ INT , @IDTRF INT
        SET @IDPRJ = (SELECT IDPRJ FROM INSERTED)
        SET @IDTRF = (SELECT IDTRF FROM INSERTED)
        -- Atualiza a tabela de produtos
     UPDATE MTRF SET CAMPOLIVREVALOR1 =
         isnull(convert(decimal(10,3), MT.Y01),0) *
           isnull(convert(decimal(10,3), MT.Y02),0)
          FROM MTRF
    INNER JOIN MTRFCOMPL AS MT ON MT.CODCOLIGADA = MTRF.CODCOLIGADA AND MT.IDPRJ = MTRF.IDPRJ AND MT.IDTRF = MTRF.IDTRF
    WHERE MTRF.IDPRJ = @IDPRJ AND MTRF.IDTRF = @IDTRF
    END

    -----------

    Ela faz o calculos corretos, Tipo Coloca na coluna Y01 10 * Y02 10 me retorna no campo CAMPOLIVREVALOR1  = 100.

    porem essas informaçoes da coluna Y01 e Y02 eu posso esta trocando a qualquer momento porem mesmo eu trocando ele mantem no campo CAMPOLIVREVALOR1  100.

    esse CAMPOLIVREVALOR1  deveria aceitar receber o resultado.

    fui clero

    obrigado

    quinta-feira, 25 de março de 2010 14:59
  • Olá,

    Você está melhorando, mas ainda falta um pouco mais a ser feito. Quando a idéia é trabalhar com linhas, você não deve fazer a declaração de variáveis, pois, isso irá levar a problemas (o artigo inclusive fala sobre isso). Retire a declaração de variáveis e a cláusula WHERE e inclua um JOIN com a INSERTED. Assim todas as linhas são contempladas sem a necessidade de cursor. Ex:

    UPDATE MTRF SET CAMPOLIVREVALOR1 = 
         isnull(convert(decimal(10,3), MT.Y01),0) *
           isnull(convert(decimal(10,3), MT.Y02),0)
          FROM MTRF 
    INNER JOIN MTRFCOMPL AS MT ON MT.CODCOLIGADA = MTRF.CODCOLIGADA AND MT.IDPRJ = MTRF.IDPRJ AND MT.IDTRF = MTRF.IDTRF
    INNER JOIN INSERTED AS I ON MT.ID = I.ID AND MT.IDPRJ = I.IDPRJ AND MT.IDTRF = I.IDTRF

    Se você necessita trocar, então a trigger não deve ser somente para eventos de INSERT, mas também para eventos de UPDATE.

    [ ]s,

    Gustavo Maia Aguiar
    http://gustavomaiaaguiar.spaces.live.com

    Como descobrir a data do último acesso a uma tabela ?
    http://gustavomaiaaguiar.spaces.live.com/blog/cns!F4F5C630410B9865!964.entry


    Classifique as respostas. O seu feedback é imprescindível
    • Marcado como Resposta Ronnie Von terça-feira, 27 de abril de 2010 19:11
    quinta-feira, 25 de março de 2010 16:57

  • Obrigado Gustavo, quero aprender uns 50% do  que vcs sabem, um dia eu chego la, com fe em Deus,

    Gustavo vou precisar de trocar essas informaçoes tb,vou tentar fazer a alteração que vc sugeriu.

     

    quinta-feira, 25 de março de 2010 20:53