반응형
블로그 이미지
개발자로서 현장에서 일하면서 새로 접하는 기술들이나 알게된 정보 등을 정리하기 위한 블로그입니다. 운 좋게 미국에서 큰 회사들의 프로젝트에서 컬설턴트로 일하고 있어서 새로운 기술들을 접할 기회가 많이 있습니다. 미국의 IT 프로젝트에서 사용되는 툴들에 대해 많은 분들과 정보를 공유하고 싶습니다.
솔웅

최근에 올라온 글

최근에 달린 댓글

최근에 받은 트랙백

글 보관함

카테고리

엑셀 VLOOKUP 함수 사용하기

2014. 12. 11. 23:35 | Posted by 솔웅


반응형

어제 업무에 사용했던 함수를 하나 정리해 보겠습니다.


실제 회사 데이터를 올릴 수 는 없어서 가상으로 아래의 상황을 설정했습니다.



작년과 올해에 여러곳에서 마라톤을 완주 했습니다.

위와 같이 정리 돼 있는데 첫번째 표에 해당 City의 2014년도의 기록을 넣고 싶습니다.


이럴경우 VLOOKUP을 사용하면 되는데요.


=IFERROR(VLOOKUP(B6,$G$6:$H$11,2,FALSE),"No")


이렇게 사용하면 됩니다.



우선 VLOOKUP 함수부터 보면 첫번째 argument (B6)는 비교할 데이터 입니다.

첫번째 칸에서는 Chunchon 이 되겠죠.


그리고 두번째($G$6:$H$11)는 이 Chunchon 이라는 데이터랑 비교할 범위입니다.

G6:H11 이 범위인데 여기에 $를 사용한 이유는 나중에 다른 셀에도 적용하기 위해서 해당 셀을 더블클릭하거나 Drag 할 때 이 값이 변하지 않도록 하기 위해서 입니다.

첫번째 인자인 B6는 $이 없으니까 Drag 하면 B7-B8-B9... 이런식으로 숫자가 자동 증가하게 됩니다.


그리고 세번째 인자인 2 는 두번째 값을 셀에 넣겠다는 겁니다.

Chunchon 이랑 같은 값이 오면 그 라인의 두번째 값을 취하겠다는 겁니다.

(그런데 오른쪽에는 Chunchon 이 없네요. 이런 경우는 조금 있다가 설명드리겠습니다.)

대신 Delhi를 보면 3:18 이 두번째 값입니다.


마지막에 FALSE는 Chunchon 이랑 정확하게 match 되어야 한다는 조건이고 TRUE 를 사용하면 비슷한 단어가 있으면 match 됐다고 보라는 겁니다.


이렇게 하면 VLOOKUP은 완료 됐는데요.


아까 봤듯이 CHUNCHON이라는 도시가 오른쪽 표에 없습니다.

이런 경우 #N/A 라고 표시가 될 텐데, 이게 보기 싫으면 다른 문자를 표시할 수 있습니다.


그게 IFERROR 함수 입니다.

만약 결과가 ERROR 이면 No 라는 문자를 표시하라는 거죠.


이렇게 하면 아래와 같은 결과가 나옵니다.




작년과 올해 모두 참가한 마라톤 대회는 Roterdam과 Boston  대회이군요.

Roterdam 대회는 1시간을 앞당겼고 Boston 대회는 50여분이 늦춰졌네요.



뭐 이게 있을 법한 일인지는 모르겠지만요.


하여간 VLOOKUP과 IFERROR 를 잘 사용하면 위와 같이 데이터를 가공할 필요가 있을 때 아주 유용하게 사용 하실 수 있습니다.




반응형


반응형

지지난주 토요일 Automation Test 를 이용해서 Production Deployment 작업을 진행해서 일을 훨씬 수월하게 끝마칠 수 있었습니다.


총 300여개가 넘는 job (test case) 들이 있었고 이것을 실행하는 시간은 총 27시간 이었습니다.

그런데 Jenkins 에 20개의 Agents들이 있어서 이 작업을 1시간 반만에 끝 마칠 수 있었죠.


예전에 매뉴얼로 할 때는 저녁 6시가 넘어서까지 일했다고 하던데...

Automation Test 를 이용한 지난번 Production Deployment는 아침 10시에 끝날 수 있었습니다.


엄청난 시간 절약이고 회사 입장에서는 엄청난 비용 절감이죠.


이걸 Client에게 제대로 인식시켜야 하니까 이 내용을 PPT에 담아서 고객에게 보여줘야겠죠.


저는 정말 PPT 작업 못 하는데... 그나마 제가 낫다고 저보고 하라그래서 슬라이드 한 두장정도 만들어서 줬어요.


물론 담당자가 좀 더 수정을 했지만요.


그거 만들면서 시간을 많이 계산해야 했습니다.


분:초 로 돼 있던 시간을 초로 계산하고 또 초를 다시 분:초로 만들고 분을 시:분으로 만들고 하는 것들이요.


그래서 이 내용들을 정리해 두려구요.



1. 분:초 를 초로 변환하기


07:32

 452


7분 32초를 이렇게 초로 계산하려면 아래와 같이 하면 됩니다.


=LEFT(위치,2)*60+MID(위치,4,2)



2. 초를 분:초로 변환하기


303.3384615

 05:03


303.3384615 초를 분:초 로 변환 하려면 아래와 같이 하면 됩니다.


=TEXT(TIME(,,위치),"mm:ss")




이렇게 한 다음에 =위치/86400 을 하는 방법도 있네요.

여기를 보시면 자세한 내용을 보실 수 있습니다.



3. 분을 시간:분 으로


1643

 27hrs23mins


1643분을 시간:분으로 표시하려면 아래와 같이 하면 됩니다.


=INT(위치/60)&"hrs"&MOD(위치,60)-MOD(위치,1)&"mins"



보고서 같은 거 작성할 때 필요할 때가 있을 것 같네요.


반응형


반응형

어제 필터링 한 후 Range 내의 Row의 갯수를 구하는 방법을 알아 봤는데요.


레인지.SpecialCells(xlCellTypeVisible).Rows.Count


이게 필터링을 여러 조합으로 하고 난 후 제대로 작동을 안 하는 경우가 있더라구요.


그래서 이 방법 말고 For 문을 돌려서 Row의 갯수를 구했습니다.


   CRRCnt = 0
   
    For Each Cell In 레인지.SpecialCells(xlCellTypeVisible).Cells
            CRRCnt = CRRCnt + 1
            'Debug.Print CRRCnt
    Next


CRRCnt라는 Integer 를 정의하고 난 후 Range의 보이는 Cell들을 For 문을 돕니다.

제 경우는 Range가 한개의 field안에 있어서 이렇게 했는데요.


더 정확하게 하려면 Cell이 아니라 Rows로 for 문을 돌려야겠군요.

이렇게는 확인을 안 해 봤습니다.

이번 작업의 경우엔 저렇게 하면 되기 때문에 그냥 Cells를 For 문을 돌렸습니다.


이렇게 For 문을 돌리면서 CRRCnt를 하나씩 증가시키면 필터링 한 후 레인지 내의 보이는 Row 수를 구할 수 있습니다.


레인지.SpecialCells(xlCellTypeVisible).Rows.Count 가 생각대로 제대로 작동하지 않으면 위와같이 해결하면 됩니다.



반응형


반응형

원래 이 Tip 도 지난 글과 같이 올리려고 했는데 애초 생각과 다르게 이전에 만들어 놨던 다른 component들까지 다 정리하는 바람에 따로 정리하게 됐습니다.


Sub 평균구하기함수()

    Dim firstCell As Range
    Dim lastCell As Range
    Dim lastRow As Double
    Dim rngtest As Range
    Dim 평균 As String

    Call clickLink("Cell주소")

    ''''''' get the first Cell to calculate
    Set firstCell = getBelowCell2("필드이름1")
   
    ''''''' get the last row to calculate
    lastRow = activateLastrow2Cal2("필드이름2")
    
    ''''''' get the last Cell to calculate
    Set lastCell = firstCell.Offset(lastRow - firstCell.Row, 0)
   
    Call filterOption("필드이름3", "1")
    Call filterOption("필드이름4", ">0")
   
    ' Get count of rows
    Set rngtest = Range(firstCell, lastCell)
    rngtest.Select
   
    평균 = WorksheetFunction.Average(rngtest.SpecialCells(xlCellTypeVisible))
   
    Call writeTable5("Sheet 이름", 0, 0, 평균 , "K11")

End Sub



위에 한글로 표기한 것은 해당 엑셀에 맞춰서 적당한 값을 넣어야 하는 겁니다.

변수명이나 필드이름 등은 회사 업무와 관련이 있는 거라서 회사 업무 내용을 공개하지 않기 위해 이름을 변경했습니다.


여기서 살펴볼 것은 아래 라인 입니다.


평균 = WorksheetFunction.Average(rngtest.SpecialCells(xlCellTypeVisible))


나머지는 이전 글에 각 함수 내용을 올려 놓았으니 그 글을 참조하시면 됩니다.

진행 순서도 똑 같구요.

필터링 후 선택된 Range의 평균을 구하는 것은 WorksheetFunction.Average(레인지.SpecialCells(xlCellTypeVisible)) 를 사용하면 됩니다.


결과 값은 String에 담구요.

이걸 Integer나 Double에 담아봤더니 에러가 나더라구요.

Converting을 해서 담던가 아니면 그냥 String 변수에 담아야 합니다.


한가지 더 참조한다면 아래 라인 입니다.


Call filterOption("필드이름4", ">0")


greater than 0 로 필터링을 어떻게 할 까 궁금했는데 그냥 위와 같이 >0 을 전달하면 되는군요.

filterOption() 함수는 이전 글에 있으니 보시고 싶으시면 바로 전 글을 보시면 됩니다.



반응형


반응형

엑셀 프로그래밍 추가 작업을 하고 있는데 몇가지 새로운 로직이 나왔습니다.

필터링 후 row의 갯수를 구하는 것과 필터링 후 특정 필드의 Average를 구하는 것입니다.


우선 필터링 후 row의 갯수를 구하는 것은 아래와 같이 하면 됩니다.


Range.SpecialCells(xlCellTypeVisible).Rows.Count


이걸 제 작업에서는 아래와 같이 활용했습니다.


Sub Sub이름()

    Dim firstCell As Range
    Dim lastCell As Range
    Dim lastRow As Double
    Dim rngtest As Range
    Dim count As Integer

    Call clickLink("링크")

    ''''''' get the first Cell to calculate
    Set firstCell = getBelowCell2("필드이름1")
   
    ''''''' get the last row to calculate
    lastRow = activateLastrow2Cal2("필드이름2")
    
    ''''''' get the last Cell to calculate
    Set lastCell = firstCell.Offset(lastRow - firstCell.Row, 0)
   
    Call filterOption("필드이름3", "1")
   
    ' Get count of rows
    Set rngtest = Range(firstCell, lastCell)
   
    count = rngtest.SpecialCells(xlCellTypeVisible).Rows.Count
   
    Call writeTable4("Sheet Name", 0, 0, count, "G6")

End Sub


특정 Sheet로 가서 필터링 한 후 특정 필드의 row 갯수를 구하는 겁니다.





우선 제가 작업하는 엑셀 파일에는 첫 sheet 에 링크가 있어서 원하는 sheet로 링크를 클릭해서 가도록 돼 있습니다.


Sub clickLink(link As String)
    Call goToSheet("링크가 있는 sheet name")
    Worksheets("링크가 있는 sheet name").Range(link).Hyperlinks(1).Follow NewWindow:=False, AddHistory:=True
    On Error Resume Next
        ActiveSheet.ShowAllData
    On Error GoTo 0
End Sub


이게 clickLink() 함수 인데요.

Link가 있는 Cell 주수를 String 파라미터로 받습니다. 그리고  goToSheet() 로 링크가 있는 sheet name을 파라미터를 전달합니다.


Sub goToSheet(sheetName As String)
    Sheets(sheetName).Select
    Range("B4").Select
End Sub


goToSheet()은 sheetName을 파라미터로 전달 받아서 해당 Sheet로 이동을 하는 함수 입니다.


다시 clickLink()를 보면 goToSheet() 다음에 해당 링크를 클릭합니다.


간단하게 말하면 clickLink()에 링크를 전달하면 그것을 클릭하는 겁니다.


그 다음에 getBelowCell2() 에서 값을 전달받아 firstCell Range에 담는데요.


Public Function getBelowCell2(cellVal As String) As Variant
    Call ActivateCell(cellVal)
    Set getBelowCell2 = ActiveCell.Offset(1, 0)
End Function


getBelowCell2 는 필드 이름(String)을 전달 받아서 바로 그 밑의 Cell을 return 합니다.

두번째는 activateLastrow2Cal2()에서 return 받은 Double형을 lastRow에 담습니다.


Public Function activateLastrow2Cal2(cellVal As String) As Double
    Call getBelowCell(cellVal)
    activateLastrow2Cal2 = ActiveCell.End(xlDown).Row
End Function


activateLastrow2Cal2()는 필드이름을 파라미터로 전달 받아서 그 필드의 가장 마지막 row 번호를 return 합니다.


이 값을 가지고 계산할 필드의 마지막 Row를 계산합니다.


Set lastCell = firstCell.Offset(lastRow - firstCell.Row, 0)


그러면 계산할 필드의 첫번째 Cell과 마지막 Cell을 확보한 겁니다.


그 다음에 필터링을 하는데요.


Sub filterOption(cellVal As String, filterVal As String)
    ActivateCell ("필드이름")
    'ActivateCell.Offset(0, -1).Select

    If ActiveSheet.FilterMode Then

    Else: Rows(ActiveCell.Row).Select
        Selection.AutoFilter
        Selection.AutoFilter
    End If
    
    Call ActivateCell(cellVal)
    ActiveCell.AutoFilter Field:=ActiveCell.Column, Criteria1:=filterVal
End Sub


Sub ActivateCell(cellVal As String)
    Cells.Find(What:=cellVal, After:=ActiveCell, LookIn:=xlFormulas, LookAt:= _
        xlPart, SearchOrder:=xlByRows, SearchDirection:=xlNext, MatchCase:=False _
        , SearchFormat:=False).Activate
End Sub


우선 ActivateCell은 필드 이름을 전달 받아서 해당 필드이름(제목)이 있는 Cell을 활성화 시키는 겁니다.

filterOption()은 필터링할 필드 이름과 필터링할 값을 파라미터로 받아서 필터링을 하는 겁니다.

그 전에 IF 문에 로직을 만들었는데 그건 제가 작업하는 엑셀 파일에서 이렇게 해야 되서 넣은 겁니다.


이러면 계산할 필드의 첫번째와 마지막 Cell을 구했고 조건에 맞게 필터링도 했습니다.


    Set rngtest = Range(firstCell, lastCell)
   
    count = rngtest.SpecialCells(xlCellTypeVisible).Rows.Count
   
    Call writeTable4("Sheet Name", 0, 0, count, "G6")


그 다음에 나오는 소스코드 입니다.

우선 첫번째 cell 에서 마지막 cell 까지를 rngtest라는 Range에 담고 이 range의 row 수를 count 에 담습니다.

writeTable4()는 전달하는 Sheet Name 의 해당 위치(위에서는 G6) 에 count 값을 write 하려고 만든 겁니다.


Sub writeTable4(sheetName As String, firstNum As Integer, secondNum As Integer, resultVal As Integer, writeCell As String)
    Dim writeVal1 As Range
    Sheets(sheetName).Select
    Set writeVal1 = Range(writeCell).Offset(firstNum, secondNum)
    writeVal1.Value = resultVal
    Range("D6").Select
End Sub


그 내용을 보면 전달받은 sheet name을 가진 sheet를 활성화 시키고 전달받은 위치 ("G6") 에 전달받은 값을 write 하는 겁니다.

writeVal1에 offset()을 사용한 것은 조건에 따라서 write할 Cell의 위치("G6") 를 바꾸기 위해서 입니다.


count = rngtest.SpecialCells(xlCellTypeVisible).Rows.Count


이 부분을 정리하려고 했는데 이전에 만들어 놨던 여러 Component들까지 다 살펴 봤네요.



반응형


반응형

지난번 VBA 디버깅 툴 사용법 다루면서 Debug.print 문도 배웠는데요.

Immediate Window 에 출력되는 양이 한정돼 있더라구요.

그래서 제가 원하는 전체 로그를 볼 수 있는 방법을 알아봤더니.. 자체적으로 Immediate Window의 출력량을 늘리는 법은 못찾았고 대신에 로그를 파일로 저장하면 되겠더라구요.

그래서 로그를 파일로 저장하는 예제 몇개 정리해 둘까 합니다.



Dim s As String
Dim n As Integer

n = FreeFile()
Open "C:\test.txt" For Output As #n

s = "Hello, world!"
Debug.Print s ' write to immediate
Print #n, s ' write to file

Close #n

Open .... For Aoutput As #n 이라는 구문이 있군요.

해당 텍스트 파일을 #n으로 세팅해 놓고 Print #n, 출력할 문자열 ' write to file 을 하고 마지막에 #n을 Close 해 줍니다.

(아직 실습을 안 해 봐서 정확히 이게 맞는건지는 확실하지 않습니다.

이렇게 Input과 Output 사용법이 약간 다른가 봅니다.

Sub ReadFile()

'again, we need this strange thing to exist so that ...

Dim fso As New FileSystemObject

'the file we're going to read from

Dim ts As TextStream

'... we can open a text file with reference to it

Set ts = fso.OpenTextFile("C:\Wise Owl\info.txt", ForReading)

'keep reading in lines till no more

Dim ThisLine As String

Dim i As Integer

i = 0

Do Until ts.AtEndOfStream

ThisLine = ts.ReadLine

i = i + 1

Debug.Print "Line " & i, ThisLine

Loop

'close down the file

ts.Close

End Sub


이건 약간 다른 방법 같은데... 이번 주말에 전부 한번 실제로 돌려 봐야겠네요.


반응형

[VBA] 디버깅 툴 알아보기

2014. 3. 3. 04:35 | Posted by 솔웅


반응형

Debugging VBA Code



Introduction

프로그래밍에서 디버깅을 하는 것은 가장 중요한 부분중의 하나입니다. VBA 디버깅 툴에 대해 잘 알면 디버깅을 좀 더 쉽고 생산적으로 할 수 있습니디다. 이 글에서는 VBA의 built in 디버깅 툴에서 여러분이 어플리케이션을 테스트하거나 디버깅하는데 도움이 되는 몇가지를 다룰 겁니다.



SectionBreak


Stepping Through Code


코드를 디버깅하기 위해 우선 해야 하는 것은 한번에 한라인의 코드를 살필 수 있어야 하는 겁니다. 코드를 쭉 훑어 보려면 살펴 볼 코드의 첫번째 줄에 커서를 위치시킵니다. 그리고 F8을 누르거나 Debut 메뉴를 누릅니다. 다음에 실행될 코드가 검은 글씨에 노란 배경색으로 표시될 겁니다.  이 노란 배경이 쳐진 줄은 아직 실행이 안 된 겁니다. 바로 다음에 실행 될 라인 입니다.


그 코드가 다른 procedure를 call 한다면 F8을 누르면 호출된 procedure로 들어갈 겁니다. 호출된 procedure로 들어가고 싶지 않으면 SHIFT F8을 하면 됩니다. SHIFT F8을 누르면 호출된 procedure로 들어가지 않고 그냥 실행만 합니다. 그리고 그 다음번 줄에서 디버깅을 위해 정지할 겁니다. 이미 그 procedure로 들어왔다면 CTRL F8를 누르시면 모두 실행이 될 겁니다. 그리고 어느때던지 F5를 누르거나 Run menu에서 Continue를 누르면 이후의 모든 과정이 실행될 겁니다. (pause 구문이 있으면 당연히 서겠죠)


step-by-step 모드일 때 Immediate window에서 변수 값을 변경할 수 있습니다.




Break Points And The Stop Command


Breakpoint는 디버깅 모드에서 살펴보기 위해 실행을 잠깐 멈추도록 하는 표시입니다. 이 표시를 하려면 F9을 누르거나 코드 왼쪽 부분을 클릭하면 됩니다. 그리고 Debug menu에서 Toggle Breakpoint를 선택하는 방법도 있습니다. breakpoint로 설정 되면 흰색글씨에 벽돌색 배경화면으로 표시됩니다. 그리고 코드를 실행시키면 이 라인에서 멈추어 검은 글씨에 노란 배경화면이 돼 있을 겁니다. 아까 말했듯이 노란색 부분은 아직 실행이 되지 않은 부분 입니다.


breakpoint에서 멈췄을 때 Immediate window 에서 변수 값을 바꿀 수 있다. 변수의 내용을 보려면 ?를 입력하고 변수 이름을 넣고 엔터키를 치면 된다. 변수이름 = 새로운 값 형식으로 Immediate window에 넣고 엔터키를 치면 변수 값을 변경할 수 있다.


Immediate window가 보이지 않으면 (대개 VBA 에디터의 밑에 부분에 있다. CTRL G를 누르거나 View menu에서 Immediate Window를 선택하면 된다.


breakpoint를 없애려면 커서를 해당 줄에 위치시킨 후 F9을 누르세요. Debug 메뉴에서 Clear All Breakpoint를 선택하거나 CTRL SHIFT F9을 누르면 모든 Breakpoint를 없앨 수 있습니다.


VBA에서는 Stop 명령어도 제공합니다. 해당 라인에서 프로그램의 실행을 멈추고 break 상태로 만들 수 있습니다.


디버깅이 끝나면 모든 breakpoint들을 clear 하는 것을 잊지 마세요. (디버그 메뉴에서 Clear All Breakpoint를 누르거나 CTRL SHIFT F9을 누르기) 그리고 Stop statement도 없애거나 주석처리해 주시구요.


breakpoint를 사용했던 아니면 디버깅 모드에서 한줄 한줄 넘어가면서 왔던 어떤 줄에 멈추어 있다면 다음 줄을 수정해서 실행시킬 수 있습니다. 그리고 현재의 라인이 있는 section을 재 실행 할 수도 있구요. 실행을 원하는 라인에 오른쪽 마우스를 클릭하시고 실행 하고 Run menu에서 Next Statement를 선택할 수도 있습니다. 그러면 선택된 라인에서 재 실행할 겁니다.


SectionBreak


The Debug Command


VBA는 두개의 파라미터를 가진 Debug 객체를 제공합니다. Print와 Assert을 사용해서 변수값을 표시할 수 있고 프로그램의 흐름을 콘트롤 할 수 있습니다. Debug.Print를 사용하면 Immediate window에서 출력 결과를 확인하실 수 있습니다. 이것은 프로그램의 실행에는 아무런 영향을 미치지 않습니다. Immediate window에 글자를 출력하고 난 후 다음 부분이 계속 실행됩니다. 변수 이름과 원하는 글자를 사용해서 Immediate 창에 원하는 글자를 출력하실 수 있습니다.

   

 Debug.Print "The value of variable X is: " & X

   


한번에 여러개의 변수를 출력할 수도 있습니다. 콤마로 변수를 구분해 주시면 됩니다.

   

Debug.Print X, Y, Z

   

Debug.Assert는 conditional breakpoint로서 Assert문장이 False 일 경우 실행을 멈춥니다.

  

  Debug.Assert Var >= 0

   

이 경우에는 변수가 0보다 작을 경우 멈추게 되겠죠. 이 Assert 메소드는 C 언어에서 가져온 겁니다. 그래서 C에서 사용하는 방법을 아시면 그대로 사용하시면 됩니다.


디버깅이 끝나시면 이 Debut.Print나 Debug.Assert 구문을 없애시거나 주석처리하는 것을 잊지 마세요.


SectionBreak



The Locals Window



Locals windows는 현재 진행되는 부분의 모든 변수들의 값을 볼 수 있도록 해 줍니다. 이 Local window를 보시려면 View 메뉴에서 Locals Window를 선택하세요. Immediate window에서 원하는 변수를 출력하거나 변경하실 수도 있습니다. 변수는 1개의 라인에 표시됩니다. Range 같이 여러 값이 들어 있으면 트리구조로 그 값을 보여줍니다. (Collpsible)



SectionBreak



The Watch Window


Watch window 는 모든 Watches를 보여줍니다. View menu에서 Watch Window를 선택하시면 보실 수 있습니다. Expression이 True일 때 혹은 변수가 그 값이 변경되거나 할 때 실행을 pause 할 수 있도록 해 주는 instruction입니다. 변수에 Watch를 생성하시려면 Watch window를 열고 Watch window에서 right-click을 하신 후 Add Watch를 선택하세요. Add Watch dialog에서는 Expression text box에 Watch를 원하시는 변수 이름을 넣으세요. 그 다음에 Break When Value Changes를 선택하시면 됩니다. 그러면 실행시 변수 값이 변경되는 라인에서 멈추게 될 겁니다. 실행이 멈추면 그 변수의 값이 변경돼 있는 상태인 겁니다.


Watch 를 remove 하려면 Watch window에서 right-click을 하시고 팝업메뉴에서 Delete Watch를 선택하세요. Watch를 변경하시려면 Edit Watch를 선택하시면 됩니다.



SectionBreak


The Call Stack


Call Stack은 VBA가 관리하는 data structure 입니다. 어떤 procedure가 어떤 다른 procedure를 call 했는지를 추적해 주죠. 만약 AAA procedure가 BBB를 call 하고 이 BBB는 CCC를 Call 한다면 Call Stack window는 가장 최근의 procedure서부터 리스트에 표시를 해 줄 겁니다. 실행된 procedure들의 chain은 현재까지 오는 동안의 진행과정을 보여 줄 겁니다. View ment에서 Call Stack 을 선택하면 이 Call Stack window를 보실 수 있습니다. 현재까지 오는 동안 어떤 procedure들이 실행됐는지 추적할 때 아주 유용하죠. programmatic 하게 이 call stack을 볼 수 있는 방법은 없습니다.


반응형


반응형

영하 25도

막 출근했는데요. 주차장에 차 세워놓고 건물로 들어오는데 공기에 노출된 살들이 너무 아프더라구요.

영하 25도.. 정말 추운 날씨예요.

어제는 영하 30도까지 내려갔었던거 같은데.....

빨리 봄이 왔으면 좋겠습니다.


어제 오후에 인도에서 새로온 매니저가 엑셀일을 하다가 매뉴얼로 하기엔 너무 벅찬 일이라 저에게 프로그래밍을 부탁하더라구요.

요구 조건은 이겁니다.


모든 Sheet 에 있는 특정 컬럼에 있는 값들과 특정 Sheet에 있는 특정 컬럼에 있는 값들을 비교하라.

특정 Sheet의 특정 컬럼에 있는 값들은 몇개의 패턴으로 문자열들이 결합돼 있으니 이것을 split 한 다음에 비교해야 한다.


이걸 10시까지 작업해서 보내줬는데요.

이 작업하면서 만들어논 컴포넌트들이나 로직들을 다음에도 유용하게 사용할 수 있을 것 같아서 여기에 정리해 놓습니다.


Sub AllSheets()
    Dim ws As Worksheet
    For Each ws In Worksheets
        Call checkFieldName(ws)
    Next ws
End Sub


이 Sub는 엑셀 파일내의 모든 Sheet에 대해 For Loop를 도는 겁니다.





Loop안에는 해당 Sheet를 checkFeildName() 함수로 전달합니다.


Sub checkFieldName(ws As Worksheet)
    Dim rngCell As Range
    Dim allCell As Range

    For Each rngCell In Worksheets(ws.Name).UsedRange
        If rngCell.Value = "필드 제목 1" Or rngCell.Value = "필드 제목 2" Then
            Set allCell = Range(rngCell.Offset(1, 0), rngCell.End(xlDown))
            Call CheckReportName(ws, allCell)
        End If
    Next

End Sub


이 Sub는 전달받은 Worksheet 내에 값들이 있는 모든 Cell들에 대해 For Loop문을 돕니다.

이 Loop 안에서는 해당 Cell 의 값이 '필드 제목 1' 이거나 '필드 제목 2' 일 경우 그 Field 제목 바로 밑에서 부터 해당 필드의 값이 있는 맨 마지막 Cell을 allCell이라는 Range에 담습니다.

그리고 Worksheet과 allCell Range를 CheckReportName() 에 전달합니다.


Sub CheckReportName(ws As Worksheet, allCell As Range)
    Dim checkName As String

    For Each Cell In Sheets(ws.Name).Range(allCell.Address).Cells
        checkName = checkExist(Cell.Value)
       
        If (UCase(checkName) = "Y") Then
            Cell.Interior.Color = RGB(36, 36, 255)
        End If
       
        If (UCase(checkName) = "Y1") Then
            Cell.Interior.Color = RGB(255, 255, 143)
        End If
    Next Cell
End Sub


이 Sub에서는 전달받은 Worksheet 내의 전달받은 Range에 대해 Loop를 돕니다.

이 Loop 안에서는 해당 Cell의 값을 checkExist() 에 전달해서 결과를 return 받습니다.

이 return 받은 결과가 Y 이면 해당 셀을 파랗게 그리고 return 받은 결과가 Y1이면 해당 셀을 노랗게 만듭니다.


Public Function checkExist(cellVal As String) As String

        Dim tmpArr1 As Variant
        Dim tmpArr2 As Variant
        Dim testStr1 As String
        Dim testStr2 As String
        Dim omitText As String
        Dim checkEx As String
       
        checkEx = "N"
   
    For Each Cell In Sheets("특정 Sheet").Range("범위").Cells
   
        If InStr(1, Cell.Value, "첫번째") > 0 Then
            If InStr(1, Cell.Value, "my.Reports") > 0 Then
                omitText = Mid(Cell.Value, 13, Len(Cell.Value))
            Else
                omitText = Cell.Value
            End If
           
                tmpArr2 = Split(omitText, "첫번째")
                testStr1 = tmpArr2(0)
                testStr2 = tmpArr2(1)
               
                If InStr(UCase(testStr1), UCase(cellVal)) <> 0 Or UCase(cellVal) = UCase(testStr1) Then
                    checkEx = "Y"
                End If
               
                If InStr(UCase(cellVal), UCase(testStr1)) <> 0 Or UCase(cellVal) = UCase(testStr1) Then
                    checkEx = "Y1"
                End If
               
                If InStr(UCase(testStr2), UCase(cellVal)) <> 0 Or UCase(cellVal) = UCase(testStr2) Then
                    checkEx = "Y"
                End If
               
                If InStr(UCase(cellVal), UCase(testStr2)) <> 0 Or UCase(cellVal) = UCase(testStr2) Then
                    checkEx = "Y1"
                End If
        Else
                If InStr(UCase(Cell.Value), UCase(cellVal)) <> 0 Or UCase(cellVal) = UCase(Cell.Value) Then
                    checkEx = "Y"
                End If
               
                If InStr(UCase(cellVal), UCase(Cell.Value)) <> 0 Or UCase(cellVal) = UCase(Cell.Value) Then
                    checkEx = "Y1"
                End If
        End If
   
    Next Cell
   
   
    checkExist = checkEx
End Function


이 함수는 특정 sheet의 특정 Range에 대해 Loop를 돕니다.


이 특정 sheet의 이름과 특정 Range는 하드 코딩을 했습니다.


시간이 나면 이 부분도 프로그래밍으로 값을 받아 올 수 있도록 Refactoring 을 할 수 있겠네요.

이 Loop에서는 받은 값을 각 Cell의 값과 비교해서 checkEx(디폴트값 N)라는 String 값을 세팅합니다.


처음에 이 특정 Sheet의 특정 컬럼에 있는 값들은 특정 패턴으로 문자열이 결합돼 있어서 이를 Split 해야 한다고 했습니다.


그 Rule이 Loop 안의 If문 안에 있습니다.


우선 맨 처음 If문에서는 해당 Cell의 값이 '첫번째' 라는 문자열을 포함하는지 여부를 체크합니다.


포함하고 있으면 이 문자열의 첫번째 13글자를 제외한 값을 omitText에 담고 그렇지 않은 경우에는 문자 전체를 omitText String 변수에 담습니다.

그리고 나서 이 '첫번째' 라는 문자열을 기준으로 각 문자열들을 Split 해서 tempArr2라는 배열에 담습니다.


이 배열의 첫번째 값은 testStr1에 두번째 값은 testStr2 라는 String 변수에 담습니다.

이 두 String 을 전달받은 String 과 비교합니다.


우선 전달받은 String 에 testStr1이 포함돼 있거나 두 문자열이 같은 경우에는 checkEx의 값을 Y로 바꿉니다.


반대로 testStr1에 전달받은 문자열을 포함하고 있는 경우에는 checkEx의 값을 Y1으로 바꿉니다.


이 작업을 testStr2에도 마찬가지로 해 줍니다.


여기까지가 해당 Cell 값에 '첫번째'라는 문자열을 가지고 있는 경우이고요 이 '첫번째'라는 문자열을 가지고 있지 않은 경우에는 곧바로 해당 Cell의 값고 전달받은 값을 비교해서 Y와 Y1이라는 값을 checkEx에 세팅을 해 줍니다.

여기까지 작업을 해서 그 결과를 매니저한테 보냈는데요.


주말에 시간 나면 이 프로그램을 다시 보고 Refactoring 좀 해 봐야겠습니다.


더 간단하게 처리할 수 있는 방법은 없는지.....


반응형

[VBA] Userform 만들기

2014. 2. 21. 23:28 | Posted by 솔웅


반응형

Userform


이 글에서는 Excel VBA Userform을 어떻게 생성하는지를 보여드립니다.
여기서는 아래와 같은 userform을 만들어 보겠습니다.

..




Add the Controls

Userform에 control들을 추가하려면 아래와 같이 하세요.

1. Visual Basic Editor를 연다. Project Explorer가 보이지 않으면 View-Project Explorer를 클릭한다.
2. Insert-Userform을 클릭한다. Toolbox가 자동으로 뜨지 않으면 View-Toolbox를 클릭한다.

여기까지 하면 아래와 같은 화면을 보실 수 있습니다.



3. 아래 테이블에 있는 control들을 추가 한다. 이 작업이 끝나면 위에서 본 것과 같은 Userform 모양이 될 것이다. 예를 들어 Toolbox에 있는 TextBox 를 클릭하면 text box control이 생성 될 것이다. 혹은 Userform에 text box를 드래그 할 수도 있다. Car를 할 경우 라디오버튼을 만들기 전에 frame 부터 만드는 것을 잊지 마세요.


4. 아래 테이블에 맞게 이름과 caption들을 바꾸자. Names는 Excel VBA code에서 사용될 것이다. Caption은 화면에 표시되는 글자를 말한다. control의 이름들을 바꾸어 보는 것도 좋은 연습이 될 것이다. 이렇게 바꾸면 코딩을 할 때 훨씬 편하게 할 수 있다. control의 name과 caption을 바꾸려면 View를 클릭하고 Properties 윈도우에서 각 control들을 클릭하면 된다.

Control Name Caption
Userform DinnerPlannerUserForm Dinner Planner
Text Box NameTextBox  
Text Box PhoneTextBox  
List Box CityListBox  
Combo Box DinnerComboBox  
Check Box DateCheckBox1 June 13th
Check Box DateCheckBox2 June 20th
Check Box DateCheckBox3 June 27th
Frame CarFrame Car
Option Button CarOptionButton1 Yes
Option Button CarOptionButton2 No
Text Box MoneyTextBox  
Spin Button MoneySpinButton  
Command Button OKButton OK
Command Button ClearButton Clear
Command Button CancelButton Cancel
7 Labels No need to change Name:, Phone Number:, etc.



Note: combo box는 유저가 아이템을 선택할 수 있는 drop down 리스트 입니다. 한개의 옵션만 선택될 수 있습니다.

Show the Userform

Userform을 보이려면 worksheet에 command button을 만들고 아래 코드를 실행하세요.

Private Sub CommandButton1_Click()
    DinnerPlannerUserForm.Show
End Sub



이제 Sub UserForm_Initialize를 생성할 겁니다. 이 Userform에 대해 Show 메소드를 사용할 때 이 sub 도 자동적으로 실행될 겁니다.

1. Visual Basic Editor를 연다.
2. Project Explorer에서 DinnerPlannerUserForm을 마우스 오른쪽 클릭을 하고 View Code를 선택한다.
3. left drop-down list에서 Userform을 선택한다. right drop-down list 에서 Initialize를 선택한다.
4. 아래 코드를 복사해 넣는다.

Private Sub UserForm_Initialize()

'Empty NameTextBox
NameTextBox.Value = ""

'Empty PhoneTextBox
PhoneTextBox.Value = ""

'Empty CityListBox
CityListBox.Clear

'Fill CityListBox
With CityListBox
    .AddItem "San Fransisco"
    .AddItem "Oakland"
    .AddItem "Richmond"
End With

'Empty DinnerComboBox
DinnerComboBox.Clear

'Fill DinnerComboBox
With DinnerComboBox
    .AddItem "Italian"
    .AddItem "Chinese"
    .AddItem "Frites and Meat"
End With

'Uncheck DataCheckBoxes

DateCheckBox1.Value = False
DateCheckBox2.Value = False
DateCheckBox3.Value = False

'Set no car as default
CarOptionButton2.Value = True

'Empty MoneyTextBox
MoneyTextBox.Value = ""

'Set Focus on NameTextBox
NameTextBox.SetFocus

End Sub



텍스트 박스들은 비어 있을 겁니다. 리스트 박스하고 콤보박스는 채워져 있을 거구요. 체크박스들은 체크되어있지 않을 겁니다.


Assign the Macros

이제 Userform의 첫번째 파트를 만들었습니다. 이제 보기 좋아졌죠? 하지만 Userform 내의 어떤 버튼을 클릭하던지 아직 어떤 일이 일어나지는 않습니다.

1. Visual Basic Editor를 연다.
2. Project Explorer 에서 DinnerPlannerUserForm을 더블클릭한다.
3. Money spin button을 더블 클릭한다.
4. 아래 코드를 추가한다.

Private Sub MoneySpinButton_Change()

MoneyTextBox.Text = MoneySpinButton.Value

End Sub



이 코드는 spin button을 이용할 때 text box가 update 되도록 하는 부분입니다.

5. OK 버튼을 더블클릭한다.
6. 아래의 코드를 추가한다.

Private Sub OKButton_Click()

Dim emptyRow As Long

'Make Sheet1 active
Sheet1.Activate

'Determine emptyRow
emptyRow = WorksheetFunction.CountA(Range("A:A")) + 1

'Transfer information
Cells(emptyRow, 1).Value = NameTextBox.Value
Cells(emptyRow, 2).Value = PhoneTextBox.Value
Cells(emptyRow, 3).Value = CityListBox.Value
Cells(emptyRow, 4).Value = DinnerComboBox.Value

If DateCheckBox1.Value = True Then Cells(emptyRow, 5).Value = DateCheckBox1.Caption

If DateCheckBox2.Value = True Then Cells(emptyRow, 5).Value = Cells(emptyRow, 5).Value & " " & DateCheckBox2.Caption

If DateCheckBox3.Value = True Then Cells(emptyRow, 5).Value = Cells(emptyRow, 5).Value & " " & DateCheckBox3.Caption

If CarOptionButton1.Value = True Then
    Cells(emptyRow, 6).Value = "Yes"
Else
    Cells(emptyRow, 6).Value = "No"
End If

Cells(emptyRow, 7).Value = MoneyTextBox.Value

End Sub



우선 Sheet1을 Activate 하고 emptyRow 를 찾습니다. emptyRow 변수는 첫번째 빈 row입니다 그리고 기록이 될 때마다 하나씩 증가하게 됩니다. 그리고 나서 하는 일이 Userform에 있는 데이터를 emptyRow의 특정 컬럼에 옮겨 놓는 작업입니다.

7. Clear button을 더블클릭한다.
8. 아래의 코드를 추가한다.

Private Sub ClearButton_Click()

Call UserForm_Initialize

End Sub



이 라인은 Clear button이 클릭 됐을 때 Sub UserForm_Initialize를 call 하게 됩니다.

9. Cancle Button을 더블클릭한다.
10. 아래 코드를 추가한다.

Private Sub CancelButton_Click()

Unload Me

End Sub



이 코드는 Cancel button을 클릭했을 때 Userform을 닫도록 합니다.


Test the Userform

Visual Basic Editor를 나갑니다. 아래 처럼 Column 제목을 달고 form 에 내용을 입력한 다음에 OK 버튼을 눌러보세요.

Result:


원문을 보려면 여기를 클릭하세요.



반응형

[VBA] Developer 메뉴 보이게 하기

2014. 2. 21. 07:03 | Posted by 솔웅


반응형

엑셀 쉬트에 버튼을 만들고 그 버튼을 클릭하면 어떤 작업이 이뤄지도록 하려면 Developer 메뉴를 이용해야 합니다.

그런데 이 Developer 메뉴는 처음에 메뉴에 나타나지 않습니다.


이 Developer 메뉴를 나타나게 하려면 아래와 같이 하면 됩니다.


1. File 로 가서 Options를 선택한다.


2. Customer Ribbon을 선택하고 우측의 Developer 체크박스를 체크한 후 OK 버튼을 누른다.

3. 그러면 화면과 같이 Developer 메뉴가 나타나고 아래 그림 처럼 엑셀 쉬트에 버튼을 만들 수 있게 됩니다.



반응형
이전 1 ··· 3 4 5 6 7 8 9 10 다음