Notifications
Clear all

Facilitar escrita de codigo

30 Posts
4 Usuários
0 Reactions
2,888 Visualizações
(@mprudencio)
Posts: 2749
Famed Member
Topic starter
 

Bom dia nem sei se o titulo é o mais adequado, mas como estou me aventurando no mundo do VBA, me surgiu um questionamento

Tenho dois codigos que fazem basicamente a mesma coisa.

Ambos identificam um determinado valor em uma celula e copia a linha inteira e cola em outra aba, realizam outras tarefas mas o basico é isso

Primeiro codigo

Sub Baixa()

Application.ScreenUpdating = False

Sheets("Contas a Pagar").Select

Range("H7").Select

Do While ActiveCell <> ""

If ActiveCell.Value = "PAGO" Then

      Intersect(Selection.EntireRow, _
               Range("A:G")).Select
                     Selection.Copy
      
             Sheets("Pagos").Select
    
   Range("A1048576").End(xlUp).Select
       ActiveCell.Offset(1, 0).Select
       
            With Selection
            .PasteSpecial Paste:=xlPasteValues
            .PasteSpecial Paste:=xlPasteFormats
            End With
                
        Range("A6").Select
                
            Sheets("Contas a Pagar").Select
    
    ActiveCell.EntireRow.Delete
    
    ActiveCell.Offset(0, 7).Select
    
    Else
    
    ActiveCell.Offset(1, 0).Select

    End If

    Loop
    
    Range("A7").Select
    
    ActiveWorkbook.RefreshAll
    
    MsgBox "Dados Atualizados Com Sucesso", vbOKOnly, "Atualizando Dados..."
           
    Application.ScreenUpdating = True
    
    ActiveWorkbook.Save
    
End Sub

Segundo codigo


Private Sub Cidade()

Dim WL As Worksheet
Dim WR As Worksheet
Dim WG As Worksheet
Dim WM As Worksheet
Dim SNOME As String
Dim ORG As String
Dim CID As String
Dim NOM As String
Dim CARG As String
Dim FUN As String
Dim VIN As String
Dim SIT As String
Dim LOT As String
Dim VEN As Currency
Dim I As Long
Dim J As Long
Dim Linha As Long
Dim Ulinha As Long
Dim Lin As Long
Dim Ulin As Long
Dim WGLin As Long

Set WL = Sheets("Listas")
Set WR = Sheets("Relação Geral")
Set WM = Sheets("Modelo")
    Linha = 6
    Lin = 6
    Ulin = WR.Range("C" & Rows.Count).End(xlUp).Row
    Ulinha = WL.Range("C" & Rows.Count).End(xlUp).Row
    
For I = Linha To Ulinha

    SNOME = WL.Cells(Linha, 3).Value
    WM.Copy After:=Sheets(Sheets.Count)
Set WG = Sheets("Modelo (2)")

For J = Lin To Ulin
    
    ORG = WR.Cells(Lin, 2).Value
    CID = WR.Cells(Lin, 3).Value
    NOM = WR.Cells(Lin, 4).Value
   CARG = WR.Cells(Lin, 5).Value
    FUN = WR.Cells(Lin, 6).Value
    VIN = WR.Cells(Lin, 7).Value
    SIT = WR.Cells(Lin, 8).Value
    LOT = WR.Cells(Lin, 9).Value
    VEN = WR.Cells(Lin, 10).Value

If WR.Cells(Lin, 3).Value = SNOME Then

WGLin = WG.Range("B" & Rows.Count).End(xlUp).Offset(1, 0).Row
WG.Cells(WGLin, 2).Value = ORG
WG.Cells(WGLin, 3).Value = CID
WG.Cells(WGLin, 4).Value = NOM
WG.Cells(WGLin, 5).Value = CARG
WG.Cells(WGLin, 6).Value = FUN
WG.Cells(WGLin, 7).Value = VIN
WG.Cells(WGLin, 8).Value = SIT
WG.Cells(WGLin, 9).Value = LOT
WG.Cells(WGLin, 10).Value = VEN

End If

Lin = Lin + 1

Next

ActiveSheet.Name = SNOME
Linha = Linha + 1
Lin = 6

Next

End Sub


Dependendo da quantidade de linhas o segundo codigo é de longe mais rapido no processamento que o segundo, e embora o segundo seja "mais facil de escrever" ja que é menos sucetivel a erros porque o proprio editor facilita a escrita, ele é muito mais trabalhoso ja que o numero de variaveis é muito maior e consequentemente mais detalhado.

Minha pergunta é da pra escrever o segundo codigo com menos linhas e sem a necessidade de declarar tantas variaveis.

Estou vendo a coisa do ponto de vista de quem esta escrevendo o codigo.

Acho o primeiro mais rapido para escrever pelo fato de ser menor.

Aguardo sugestões.

Marcelo Prudencio
Microsoft Excel Brasil no Facebook

"Começar já é a metade do caminho."
Autor Desconhecido

Simplifica que simples fica.
Nicole Tomazella.

"O Simples é Sempre Melhor Que o Complicado"
Jorge Paulo Lemann.

 
Postado : 26/01/2016 9:10 am
Fernando Fernandes
(@fernandofernandes)
Posts: 43750
Illustrious Member
 

Há uma pequena diferença entre as duas rotinas.
A primeira segue linha a linha da planilha "Contas a Pagar" ,se uma determinada célula da linha atender uma condição, copia essa linha com seus valores e formatos, para a ultima da planilha "Pago" e exclui a linha da planilha inicial.
A Segunda percorre linha a linha a planilha "Listas" para cada linha dessa planilha e criada uma copia da planilha "Modelo" para o fim das planilhas,
então entende-se que essa planilha tem sua "modelagem" padronizada. Apos percorre linha a linha a planilha "Relação Geral" e copia os valores para a planilha criada e a renomeia

Então sem maiores analises creio que a segunda rotina poderia ser:

Private Sub Cidade()
Dim WL As Worksheet
Dim WR As Worksheet
Dim WG As Worksheet
Dim WM As Worksheet
Dim Linha As Long
Dim Lin As Long
Dim WGLin As Long

Set WL = Sheets("Listas")
Set WR = Sheets("Relação Geral")
Set WM = Sheets("Modelo")
    
For Linha = 6 To WL.Range("C" & Rows.Count).End(xlUp).Row 'Ulinha
    WM.Copy After:=Sheets(Sheets.Count)
    Set WG = Sheets("Modelo (2)")
    For Lin = 6 To WR.Range("C" & Rows.Count).End(xlUp).Row 'Ulin
        If WR.Cells(Lin, 3).Value = WL.Cells(Linha, 3).Value Then
            WGLin = WG.Range("B" & Rows.Count).End(xlUp).Offset(1, 0).Row
            WG.Cells(WGLin, 2).Value = WR.Cells(Lin, 2).Value 'ORG
            WG.Cells(WGLin, 3).Value = WR.Cells(Lin, 3).Value 'CID
            WG.Cells(WGLin, 4).Value = WR.Cells(Lin, 4).Value 'NOM
            WG.Cells(WGLin, 5).Value = WR.Cells(Lin, 5).Value 'CARG
            WG.Cells(WGLin, 6).Value = WR.Cells(Lin, 6).Value 'FUN
            WG.Cells(WGLin, 7).Value = WR.Cells(Lin, 7).Value 'VIN
            WG.Cells(WGLin, 8).Value = WR.Cells(Lin, 8).Value 'SIT
            WG.Cells(WGLin, 9).Value = WR.Cells(Lin, 9).Value 'LOT
            WG.Cells(WGLin, 10).Value = WR.Cells(Lin, 10).Value 'VEN
        End If
    Next
    ActiveSheet.Name = WR.Cells(Lin, 3).Value 'SNOME
Next
End Sub

Como não testei pode haver alguma inconsistência; porem vale ressaltar que cada rotina desenvolvida segue as preferencias pessoais do programador.

Existem mil maneiras de preparar Neston. Invente a sua!
http://www.youtube.com/ExpressoExcel

 
Postado : 26/01/2016 2:35 pm
(@mprudencio)
Posts: 2749
Famed Member
Topic starter
 

Reinaldo vc nao entendeu meu questionamento, o que eu disse e que me preocupa na segunda rotina é a quantidade de variaveis que se declara para escrever um codigo como esse.

SE ele for escrito do modo do primeiro usando select por exemplo ele perde em performance mas o programador ganha muito tempo, veja que pra preencher 9 colunas

Isso aqui

 ORG = WR.Cells(Lin, 2).Value
    CID = WR.Cells(Lin, 3).Value
    NOM = WR.Cells(Lin, 4).Value
   CARG = WR.Cells(Lin, 5).Value
    FUN = WR.Cells(Lin, 6).Value
    VIN = WR.Cells(Lin, 7).Value
    SIT = WR.Cells(Lin, 8).Value
    LOT = WR.Cells(Lin, 9).Value
    VEN = WR.Cells(Lin, 10).Value

vc troca por isso

Intersect(Selection.EntireRow, _
               Range("B:J")).Select
                     

Em resumo a minha pergunta é como escrever o segundo codigo sem esse monte de variaveis isso transforma a programação em um trabalho muito braçal

Minha pergunta esta relacionada a seguinte situação se vc tiver uma tabela muito grande (em colunas)

Imagine 30 colunas declarar uma a uma ta um trabalhão, e que se pode resolver com uma linha de instrução conforme o exemplo acima.

Mal comparando (é so um exemplo ruim).

=A1+A2+A3+A4+A5+A6+A7+A8+A9+A10, faz a mesma coisa que =SOMA(A1:A10), embora em vba usar a segunda forma tenha uma performance menor ja que o excel fica o tempo todo pulando entre abas, mas do ponto de vista do programador é muito mais produtivo.

Esse é o meu questionamento.

Marcelo Prudencio
Microsoft Excel Brasil no Facebook

"Começar já é a metade do caminho."
Autor Desconhecido

Simplifica que simples fica.
Nicole Tomazella.

"O Simples é Sempre Melhor Que o Complicado"
Jorge Paulo Lemann.

 
Postado : 26/01/2016 3:46 pm
Fernando Fernandes
(@fernandofernandes)
Posts: 43750
Illustrious Member
 

Boa noite,

Declarar variáveis é uma boa prática de programação.
Uma maneira de reduzir a quantidade seria declarar o conjunto das variáveis como um vetor e fazer um laço para percorrê-lo.
Caso o objetivo seja o desempenho e a redução do código, uma opção é utilizar os recursos do Excel.
Exemplo:

Sub CopiaPagos()
    Dim Linhas As Long
    Dim Qt As Long
    Dim Primeiro As Long
    
    Sheets.Add
    ActiveSheet.Name = "TEMP"
    Sheets("Contas a Pagar").Cells.Copy Sheets("TEMP").[A1]
    Sheets("Contas a Pagar").Range("A1:J1").Copy Sheets("Pagos").[A1]
    Application.Goto Reference:=Sheets("TEMP").[H2]
    Linhas = ActiveCell.CurrentRegion.Rows.Count
    With ActiveSheet
        .Sort.SortFields.Clear
        .Sort.SortFields.Add Key:=Range("H2")
        .Sort.SetRange Range("A2:J" & Linhas)
        .Sort.Apply
    End With
    Qt = WorksheetFunction.CountIf(Sheets("TEMP").Range("H2:H" & Linhas), "PAGO")
    Primeiro = WorksheetFunction.Match("PAGO", Sheets("TEMP").Range("H2:H" & Linhas), 0)
    Range("A" & Primeiro + 1 & ":J" & Qt + Primeiro + 1).Copy Sheets("Pagos").[A2]
    Application.DisplayAlerts = False
    Sheets("TEMP").Delete
    Application.DisplayAlerts = True
End Sub

O código acima cria uma planilha temporária ("TEMP"), classifica os dados e copia o intervalo da coluna H onde consta a palavra "PAGO".
Caso queira reduzir ainda mais o código, é possível utilizar o filtro avançado:

Sub FiltroAv()
    Dim Linhas As Long
    
    Linhas = Sheets("Contas a Pagar").[A1].CurrentRegion.Rows.Count
    Sheets("Pagos").Select
    Range("A1:J2").Select
    Sheets("Contas a Pagar").Range("A1:J" & Linhas).AdvancedFilter Action:= _
        xlFilterCopy, CriteriaRange:=Sheets("Critérios").Range("A1:J2"), CopyToRange _
        :=Range("A1:J1"), Unique:=False
End Sub

O código acima faz a mesma coisa que o anterior, porém utilizando o filtro avançado e sem criar a planilha temporária.
Testei numa planilha com 100.000 linhas e a execução foi bem rápida, menos de 2 segundos.
Quanto aos códigos apresentados na primeira mensagem, o que reduz o desempenho do primeiro é a seleção dos intervalos.
A sugestão é fazer um código que copie sem selecionar.

Abraço

Existem mil maneiras de preparar Neston. Invente a sua!
http://www.youtube.com/ExpressoExcel

 
Postado : 26/01/2016 6:11 pm
EdsonBR
(@edsonbr)
Posts: 1057
Noble Member
 

Olá, Marcelo, tudo bem?

Em meu entendimento, o primeiro código só é mais apropriado se for usado um número limitado de vezes, usando rapidamente o gravador de macros, por exemplo, para uma situação específica.

Se for um procedimento mais rotineiro ou se o código tiver tendência a crescer ou tiver que ser editado, ele estará muito mais sujeito a situações imprevistas e também é mais difícil de ler, documentar e fazer manutenção, além de geralmente ser mais lento, devido ao maior número de chamadas desnecessárias (selects, activates, etc.).

Mesmo sem considerar performance, analisando situações como no loop Do While ActiveCell <> "", o uso de ActiveCell onde dentro do loop tem vários Selects que mudam a célula ativa podem resultar em complicações (loop infinito, por exemplo) pois a cada select/activate a célula ativa muda de endereço, o que não seria recomendado em um código mais complexo.

Quanto ao segundo código, o Reinaldo "enxugou" bastante o código, como vc desejava. Veja que ele eliminou as variáveis intermediárias (e desnecessárias) ORG, CID, NOM, CARG, FUN, VIN, SIT, LOT, VEN.

Mas se ainda assim vc quiser enxugar mais um pouco, poderá atribuir todo o intervalo com uma só expressão, o que fica facilitado principalmente pq em seu caso o intervalo é de colunas adjacentes o que facilita a escrita. Nesse caso, aproveitando o código do Reinaldo, onde tem
WG.Cells(WGLin, 2).Value = WR.Cells(Lin, 2).Value 'ORG
WG.Cells(WGLin, 3).Value = WR.Cells(Lin, 3).Value 'CID
WG.Cells(WGLin, 4).Value = WR.Cells(Lin, 4).Value 'NOM
WG.Cells(WGLin, 5).Value = WR.Cells(Lin, 5).Value 'CARG
WG.Cells(WGLin, 6).Value = WR.Cells(Lin, 6).Value 'FUN
WG.Cells(WGLin, 7).Value = WR.Cells(Lin, 7).Value 'VIN
WG.Cells(WGLin, 8).Value = WR.Cells(Lin, 8).Value 'SIT
WG.Cells(WGLin, 9).Value = WR.Cells(Lin, 9).Value 'LOT
WG.Cells(WGLin, 10).Value = WR.Cells(Lin, 10).Value 'VEN

Pode-se substituir por:

WG.Range(Cells(WGLin,2),Cells(WGLin,10)).Value = WR.Range(Cells(Lin,2),Cells(Lin,10)).Value

Ou seja, isso atribui, de uma só vez todo o conteúdo do intervalo referente às colunas B a J da planilha WG, linha WGLin ao intervalo correspondente na planilha WR, linha "Lin", colunas também B a J. Logicamente nesse caso vc percorreu linha por linha, mas, como vc deve ter imaginado, pode-se pegar um intervalo inteiro de várias linhas e colunas de uma só vez e atribuir a outro intervalo, desde que tenham mesmo tamanho.

Entretanto, como o Reinaldo fez tem a vantagem de ficar mais documentado dentro do código, pois cada linha diz a qual variável se refere, dando maior clareza.

De qualquer forma, eu sempre escolheria o segundo código, pois além de ser mais profissional, tem-se a certeza que foi feito por alguém com algum conhecimento no assunto, não pelo gravador, portanto passa mais confiança.

Obs.: como vc não anexou seu modelo, também não testei e poderá haver erros nas linhas acima.

 
Postado : 26/01/2016 8:57 pm
(@mprudencio)
Posts: 2749
Famed Member
Topic starter
 

JValq obrigado pela postagem mas vc fez uma confusao danada, misturou os dois codigos e escreveu um novo que vou estudar no futuro para conhecer as instruções utilizadas por vc.

EdsonBr entendeu exatamente o que procurava, que é reduzir o tamanho do segundo codigo, de forma pratica. As vezes a planilha é muito grande (numero de colunas), e declarar esse monte de variaveis é pouco produtivo para o programador.

Entao (vou testar) essa redução embora dificulte a manuntenção para uma tabela grande ajuda bastante.

Marcelo Prudencio
Microsoft Excel Brasil no Facebook

"Começar já é a metade do caminho."
Autor Desconhecido

Simplifica que simples fica.
Nicole Tomazella.

"O Simples é Sempre Melhor Que o Complicado"
Jorge Paulo Lemann.

 
Postado : 26/01/2016 9:17 pm
(@mprudencio)
Posts: 2749
Famed Member
Topic starter
 

Edson bom dia como disse testei sua sugestão mas deu erro.

Erro em tempo de execução 1004

O metodo Range do Objeto' _Worksheet' falhou

Alguma ideia??

Marcelo Prudencio
Microsoft Excel Brasil no Facebook

"Começar já é a metade do caminho."
Autor Desconhecido

Simplifica que simples fica.
Nicole Tomazella.

"O Simples é Sempre Melhor Que o Complicado"
Jorge Paulo Lemann.

 
Postado : 27/01/2016 5:11 am
EdsonBR
(@edsonbr)
Posts: 1057
Noble Member
 

Sem ter sua pasta de trabalho para analisar fica difícil depurar. Aparentemente estaria certo.

Mas veja estes dois exemplos, cujo resultado é idêntico, com a diferença que no 2º. os intervalos correspondentes fora atribuídos a variáveis-objeto tipo range. Ambos copiam os valores do intervalo A1:E5 para o intervalo A10:E14 na planilha ativa. Insira alguns valores no intervalo A1:E5 e comprove.

Sub Teste1()
  Dim ws As Worksheet
  Set ws = ActiveSheet
  ws.Range(Cells(10, 1), Cells(14, 5)).Value = _ 
      ws.Range(Cells(1, 1), Cells(5, 5)).Value
End Sub

Sub Teste2()
  Dim ws As Worksheet
  Dim rgOrigem As Range, rgDestino As Range
  Set ws = ActiveSheet
  Set rgOrigem = ws.Range(Cells(1, 1), Cells(5, 5))
  Set rgDestino = ws.Range(Cells(10, 1), Cells(14, 5))
  rgDestino.Value = rgOrigem.Value
End Sub

Logicamente se vc quisesse copiar valores e também fórmulas, usaria ao invés de .Value o .FormulaR1C1.

Qualquer coisa retorne.

 
Postado : 27/01/2016 11:19 am
(@mprudencio)
Posts: 2749
Famed Member
Topic starter
 

Vou testar seu codigo mas o arquivo esta aqui

Marcelo Prudencio
Microsoft Excel Brasil no Facebook

"Começar já é a metade do caminho."
Autor Desconhecido

Simplifica que simples fica.
Nicole Tomazella.

"O Simples é Sempre Melhor Que o Complicado"
Jorge Paulo Lemann.

 
Postado : 27/01/2016 11:28 am
EdsonBR
(@edsonbr)
Posts: 1057
Noble Member
 

Marcelo, desculpe-me, o erro foi meu. Esqueci-me que a propriedade Range exige argumento na forma "A1". Portanto basta acrescentar a propriedade .Address após os Cells(....), ou seja, substitua:

WG.Range(Cells(WGLin, 2), Cells(WGLin, 10)).Value = _
WR.Range(Cells(Lin, 2), Cells(Lin, 10)).Value

por

WG.Range(Cells(WGLin, 2).Address, Cells(WGLin, 10).Address).Value = _
WR.Range(Cells(Lin, 2).Address, Cells(Lin, 10).Address).Value

Rodei em sua planilha e ficou ok.

 
Postado : 27/01/2016 8:43 pm
(@edcronos2)
Posts: 346
Reputable Member
 

pode ser assim tbm

           WG.Range(WG.Cells(WGLin, 2), WG.Cells(WGLin, 10)).Value = _
WR.Range(WR.Cells(Lin, 2), WR.Cells(Lin, 10)).Value

Range( WG.Cells(WGLin, 2)
se fosse para uso de aba ativa nem precisava

eu prefiro usar with assim só uso o ponto
.Range( .Cells(WGLin, 2), .Cells(WGLin, 10)).Value

mas para muito dados ou sempre prefiro arrays
mesmo criando varios arrays e varios loops sempre fica mais rapido do que ter que acessar a planilha varias vezes seguidas

 
Postado : 28/01/2016 12:49 am
(@mprudencio)
Posts: 2749
Famed Member
Topic starter
 

Edson obrigado pela dica, realmente funcionou...

Edcronos2 sua dica tambem funcionou, mas eu gostaria que se possivel vc fizesse dois exemplos um com Array e outro utilizando a instrução with.

Ja que o objetivo desse topico é aprendizado essas novas opções seriam interessante.

Aguardo.

Marcelo Prudencio
Microsoft Excel Brasil no Facebook

"Começar já é a metade do caminho."
Autor Desconhecido

Simplifica que simples fica.
Nicole Tomazella.

"O Simples é Sempre Melhor Que o Complicado"
Jorge Paulo Lemann.

 
Postado : 28/01/2016 7:28 am
EdsonBR
(@edsonbr)
Posts: 1057
Noble Member
 

Tem razão, edcronos2, qualificando Cells com o Worksheet correspondente é prática até melhor e dispensa os Address e fica menos sujeito a erros.

Quanto ao uso de Arrays, pode ser também uma boa idéia. Entretanto, neste caso específico, o foco do Marcelo era diminuir o número de variáveis, enxugando o código. Mas de fato pode-se passar uma array inteira a um intervalo em uma só operação, o que é uma grande vantagem em programação.

Saudações aos amigos.

 
Postado : 28/01/2016 7:36 am
(@edcronos2)
Posts: 346
Reputable Member
 

MPrudencio
quando eu falo pouco eu sou quase um gênio :o
quando eu tento explicar :? é briga :? ou maus entendidos :roll:
então é melhor vc procurar outra fonte ;)

para ter uma ideia, se lembra do grupo né
e eu entrei num grupo de física teórica com meu conhecimento nulo por pura curiosidade
fiz umas tantas perguntas, o pessoal é até maneiro e explica bem
mas eu com meu modo de aprender, em 3 dias já fui acusado de tentar destruir pesquisas e estudos que tem anos :o
sei la eu uso minha imaginação critico até assimilar, para saber se estou entendendo a logica eu mudo os parâmetros ou as variaveis, mas parece que em ciencia tbm tem um crentes que não se aceita a critica e se sentem abalados com seus conhecimentos

kkk, já fui acusado novamente de suga cerebro,
eu pego a inteligencia e conhecimento deles e substituo por minha ignorância :shock:
preciso nem entrar numa faculdade, é só entrar nesses grupos e roubar o conhecimento de todo mundo e de quebra ainda me livro da concorrência :mrgreen:

 
Postado : 28/01/2016 8:04 am
(@mprudencio)
Posts: 2749
Famed Member
Topic starter
 

Eu so queria ver como fica isto


            WG.Cells(WGLin, 2).Value = WR.Cells(Lin, 2).Value 'ORG
            WG.Cells(WGLin, 3).Value = WR.Cells(Lin, 3).Value 'CID
            WG.Cells(WGLin, 4).Value = WR.Cells(Lin, 4).Value 'NOM
            WG.Cells(WGLin, 5).Value = WR.Cells(Lin, 5).Value 'CARG
            WG.Cells(WGLin, 6).Value = WR.Cells(Lin, 6).Value 'FUN
            WG.Cells(WGLin, 7).Value = WR.Cells(Lin, 7).Value 'VIN
            WG.Cells(WGLin, 8).Value = WR.Cells(Lin, 8).Value 'SIT
            WG.Cells(WGLin, 9).Value = WR.Cells(Lin, 9).Value 'LOT
            WG.Cells(WGLin, 10).Value = WR.Cells(Lin, 10).Value 'VEN

Dentro de um Array e dentro de um with... Faz ae.. :lol: :D ;)

Marcelo Prudencio
Microsoft Excel Brasil no Facebook

"Começar já é a metade do caminho."
Autor Desconhecido

Simplifica que simples fica.
Nicole Tomazella.

"O Simples é Sempre Melhor Que o Complicado"
Jorge Paulo Lemann.

 
Postado : 28/01/2016 9:40 am
Página 1 / 2