메뉴
해결 M365윈도우11

파워쿼리,VBA 질문입니다

박
9월 24일 조회 710
프로그램 버전에따라 CSV파일 머릿글이 달라서 하나하고 똑같이 두번째 만드는데

무슨이유에서인지 이전엔 파일안에있던게 다 로드 됬었는데 안되네요..

샘플 파일기준으로 파일 취합하려면 어떻게 해야할까요

프로그램에서 뽑히는 데이터를 그대로 넣어두는거라 개별로 머릿글수정을 하긴 번거로울꺼같은데

버전 2개를 합쳐서 하나로 만들방법도 있을까요

추가로



Private Sub Worksheet_Change(ByVal Target As Range)

Application.ScreenUpdating = False
Application.EnableEvents = False

If Not Intersect(Target, Range("E4")) Is Nothing Then
'값이 변경된 셀이 "OO"셀 이면

OutLine

Application.ScreenUpdating = True
Application.EnableEvents = True

End Sub

 

이렇게 했는데 자동실행이 안되는 이유와 해결방안도 궁금합니다

C4셀에 함수식이 적혀있어서 파워쿼리 새로고침하면 해당영역표가 만들어지고

VBA는 바뀌는 영역을 테두리 전체삭제한뒤 커런트리젼으로 다시 테두리 만드는거에요

댓글 7

수
수메리안 채택된 답변 9월 24일
안녕하세요~
1. 첫번째 파일의 수식에 오류가 있어 다음과 같이 수정했으니 살펴보시구요~
=CHOOSECOLS(SORT(LET(r,UNIQUE(CSV_TBL[T/N]),
f,LAMBDA(v,SORT(FILTER(CSV_TBL[[P/G]:[생크 길이]],v=CSV_TBL[T/N]),7)),
DROP(REDUCE("",r,LAMBDA(a,b,VSTACK(a,f(b)))),1)),5,1),1,6,2,3,4,5,7,8,9)

2. vba 에서
OutLine

은 시트속성에 이미 있기 때문에 속성 에러가 계속 납니다. 이렇게 수정해 보세요~
시트에는,
Private Sub Worksheet_Calculate()
    Static LastValue As Variant
    Dim CurrentValue As Variant

    CurrentValue = Range("E4").Value
  
    If Not IsEqual(LastValue, CurrentValue) Then
    
        LastValue = CurrentValue
        Call ApplyGrid(Me)
    End If
End Sub

Private Function IsEqual(Val1 As Variant, Val2 As Variant) As Boolean
    If IsEmpty(Val1) And IsEmpty(Val2) Then
        IsEqual = True
    ElseIf IsEmpty(Val1) Or IsEmpty(Val2) Then
        IsEqual = False
    Else
        IsEqual = (Val1 = Val2)
    End If
End Function

모듈에는,
Public Sub ApplyGrid(ws As Worksheet)
    Dim rng As Range
    
    Application.ScreenUpdating = False
    Application.EnableEvents = False
    
    On Error GoTo ErrorHandler
    
    ws.UsedRange.Borders.LineStyle = xlNone
    
    Set rng = ws.Range("C3").CurrentRegion
    
    With rng.Borders
        .LineStyle = xlContinuous
        .Weight = xlThin
        .ColorIndex = xlAutomatic
    End With
    
ErrorHandler:
   
    Application.ScreenUpdating = True
    Application.EnableEvents = True
    
    If Err.Number <> 0 Then
        MsgBox "오류가 발생했습니다: " & Err.Description, vbCritical
    End If
End Sub

Public Sub ApplyGridDelayed()
    Dim ws As Worksheet
    Set ws = ActiveSheet
    Call ApplyGrid(ws)
End Sub

박
박예린 작성자 9월 24일
@수메리안 님 너무 도움이 되었습니다.
추가로 제가 지금 VBA기초 챌린지 챕터3까지만 들은 VBA완전 쌩초보라 GPT도움받아 해석은 했는데 안배운 내용이 너무 많아서요
저는 아직 Sub로 매크로 이름 만들고 거기서 dim 다해두고 하는걸로 배웠는데
만들어주신건    public sub이고 applygrid에선 dim rng만 있고 그걸 applygriddelay가 데려가고 에러 핸들러도 시트가 아니라 모듈내용에 있고 그런 작성방식이 실무에서 필요한건가요? 
특별한 이유가 있는건지 궁금합니다.
 
마지막으로 이렇게 저렇게 조작하다보니 E4셀값이 변하지 않는경우가 있더라구요
챗GPT가 RANGEVALUE기억해서 하는걸로 해보라고 했다가 난리가 났어요
어떻게해야할까요...
수
수메리안 9월 24일
1. 테스트용으로 넣었던 applygriddelay 프로시저는 이미 불필요하므로 삭제하셔도 됩니다.
워크시트에서 배열수식 딜레이테스트로, E4 셀 값 변경 시 약간의 지연을 두고 실행되도록
If Not Intersect(Target, Range("E4")) Is Nothing Then
    Application.OnTime Now + TimeValue("00:00:01"), ...
End If

와 같은 코드가 들어가 있었는데, 이는 배열 수식 완료를 기다린 뒤 applygriddelay를 동작시키기 위해서 입니다. 워크시트에서 삭제했으니, 모듈에서도 삭제하시면 됩니다.
2. Sub와 Public Sub는 기능상 차이가 없습니다. 기본 접근 지정자는 Public이며,
단지 명시적으로 표시하느냐의 차이일 뿐입니다.
관례적으로는 외부 모듈에서 호출될 가능성이 있는 경우 Public Sub를 쓰고, 해당 모듈/시트 내부에서만 쓰이는 경우라면 Private Sub로 제한하는 것도 좋습니다.
2. 에러핸들러를 모듈에 두는 이유는 
Call ApplyGrid(Me)

실행 중 오류가 발생할 경우, 그 오류를 모듈에서 처리하는 것이 가장 적절합니다.
그래서 시트 모듈은 E4 셀 값의 변경을 감지하는 역할만 하고,
실제 테두리 적용 및 예외 처리는 모듈에서 담당하도록 구조를 나눈 것입니다.
3. 말씀하신 “E4 셀 값이 변하지 않는 경우”가 구체적으로 어떤 상황인지 알려주시면,
해당 케이스를 직접 테스트해 보겠습니다. 가능하시다면 raw 데이터를 함께 올려주시면 더 정확하게 확인할 수 있습니다.
박
박예린 작성자 9월 25일
@수메리안 님 기존에 1.CSV로 계속 덮어쓰기해서 =셀번호,자동채우기 해서 썻어서 로데이터가 없어여
테스트해보실때 아랫쪽데이터가 아니라 윗쪽데이터를 지워보시면 알수있어요
E4셀이 공구경이라 같은 공구경이 제일작을경우엔 E4셀이 변하지 않고, 테두리범위는 변하고 하게되더라구여 
수
수메리안 9월 25일
@박예린 님 1. toolsheet(v2018)_250922 (1)
이 파일에서,
CSV 파일에서 데이터를 복사해서 계속 붙여넣기 하면서 "공구확인용" 시트에 있는 테이블을 생성하는 작업 내용으로 이해하는 게 맞는가요?
2. 이때, E열 공구명의 E4값이 똑같은 새로운 값을 CSV 시트에 넣었을 경우, 테두리가 변해야 하는데 알맞게 변하지 않는다는 문제인가요?
즉, 기존에 5행 테두리였는데, 새로 붙인 데이터의 행이 3행일 경우,
E4값이 같으면, 데이터가 2행이 빈 상태로 있는데, 이 부분도 테두리가 둘러진 상태로 있는 문제가 발생한다는 말씀인가요?
수
수메리안 9월 25일
@수메리안 님 실시간으로 "CSV" 시트의 내용이 바뀔 때마다, "공구확인용" 시트에 테두리가 초기화 되었다가, 다시 적용이 되도록 하는데, 특히 E4가 변할 때, 변하지 않을 때 등을 감지해서 적용하도록 수정했습니다~
특히 모듈에서 시트명을 상수로 설정해, 원하는 시트명으로 바꿀 수 있도록 했습니다~

Option Explicit

' ==================== 시트 정의 ====================
Public Const WS_MAIN As String = "공구확인용"
Public Const WS_CSV As String = "CSV"

Public Sub ApplyGrid(Optional ByRef L_DR As String = "")
    '// ---------------- 기본/상태 저장 ----------------
    Dim ws As Worksheet: Set ws = Worksheets(WS_MAIN)
    Dim sht As Worksheet
    Dim prvCalc As XlCalculation
    On Error GoTo EH
    
    With Application
        .ScreenUpdating = False
        .EnableEvents = False
        prvCalc = .Calculation
        .Calculation = xlCalculationManual
    End With
    
    '// ---------------- 실제 그릴 범위 결정 ----------------
    Dim d_RNG As Range
    Dim n_DR As String
    Dim hasSpill As Boolean: hasSpill = False
    
    On Error Resume Next
    hasSpill = ws.Range("C3").hasSpill
    On Error GoTo 0
    
    If hasSpill Then
        '// 1) 스필이 있으면 스필범위로
        Set d_RNG = ws.Range("C3").CurrentRegion
    Else
        '// 2) 스필이 없으면 예비: CSV의 eRow 기반
        Set sht = ThisWorkbook.Worksheets(WS_CSV)
        Dim eRow As Long: eRow = sht.Cells(sht.Rows.Count, "B").End(xlUp).Row + 2
        Dim cCol As Long: cCol = ws.Cells(3, ws.Columns.Count).End(xlToLeft).Column
        If eRow < 3 Then eRow = 3
        Set d_RNG = ws.Range(ws.Cells(3, 3), ws.Cells(eRow, cCol))
    End If
    
    n_DR = d_RNG.Address
    
    '// ---------------- 기존 테두리 지우기 ----------------
    Call C_PB(ws, L_DR)
    
    '// ---------------- 데이터 없으면 종료 ----------------
    If WorksheetFunction.CountA(d_RNG) = 0 Then
        L_DR = ""  '// 빈 범위이므로 초기화
        GoTo QH
    End If
    
    '// ---------------- 격자 그리기 ----------------
    Call D_GB(d_RNG)
    
    '// 현재 그린 범위를 저장
    L_DR = n_DR
    
QH:
    With Application
        .ScreenUpdating = True
        .EnableEvents = True
        .Calculation = prvCalc
    End With
    Exit Sub
    
EH:
    With Application
        .ScreenUpdating = True
        .EnableEvents = True
        .Calculation = prvCalc
    End With
    
    MsgBox "오류가 발생했습니다: " & Err.Description, vbCritical
    
End Sub

' ==================== 이전 테두리 지우기 (최적화된 버전) ====================
Private Sub C_PB(ws As Worksheet, L_Rng As String)
    On Error Resume Next
    
    If L_Rng = "" Then Exit Sub  ' 이전 범위가 없으면 종료
    
    Dim eRng As Range
    Set eRng = ws.Range(L_Rng)
    
    '// 이전 테두리 완전 제거
    With eRng.Borders
        .Item(xlEdgeLeft).LineStyle = xlNone
        .Item(xlEdgeTop).LineStyle = xlNone
        .Item(xlEdgeRight).LineStyle = xlNone
        .Item(xlEdgeBottom).LineStyle = xlNone
        .Item(xlInsideVertical).LineStyle = xlNone
        .Item(xlInsideHorizontal).LineStyle = xlNone
    End With
    
    On Error GoTo 0
End Sub

' ==================== 범위 포함 여부 확인 ====================
Private Function IS_rc(iRng As Range, oRng As Range) As Boolean
    On Error Resume Next
    IS_rc = False
    
    If iRng.Row >= oRng.Row And _
       iRng.Column >= oRng.Column And _
       (iRng.Row + iRng.Rows.Count - 1) <= (oRng.Row + oRng.Rows.Count - 1) And _
       (iRng.Column + iRng.Columns.Count - 1) <= (oRng.Column + oRng.Columns.Count - 1) Then
        IS_rc = True
    End If
    
    On Error GoTo 0
End Function

' ==================== 격자 테두리 그리기 ====================
Private Sub D_GB(tRng As Range)
    On Error Resume Next
    
    With tRng.Borders
        ' 외곽 테두리
        With .Item(xlEdgeLeft)
            .LineStyle = xlContinuous
            .Weight = xlThin
            .ColorIndex = xlAutomatic
        End With
        With .Item(xlEdgeTop)
            .LineStyle = xlContinuous
            .Weight = xlThin
            .ColorIndex = xlAutomatic
        End With
        With .Item(xlEdgeRight)
            .LineStyle = xlContinuous
            .Weight = xlThin
            .ColorIndex = xlAutomatic
        End With
        With .Item(xlEdgeBottom)
            .LineStyle = xlContinuous
            .Weight = xlThin
            .ColorIndex = xlAutomatic
        End With
        
        ' 내부 테두리
        With .Item(xlInsideVertical)
            .LineStyle = xlContinuous
            .Weight = xlThin
            .ColorIndex = xlAutomatic
        End With
        With .Item(xlInsideHorizontal)
            .LineStyle = xlContinuous
            .Weight = xlThin
            .ColorIndex = xlAutomatic
        End With
    End With
    
    On Error GoTo 0
End Sub

' ==================== 전체 테두리 초기화 (필요시 사용) ====================
Public Sub cls_B()
    Dim ws As Worksheet: Set ws = Worksheets(WS_MAIN)
    
    With Application
        .ScreenUpdating = False
        .EnableEvents = False
    End With
    
    ' C3부터 사용된 영역 끝까지의 모든 테두리 제거
    Dim eRow As Long: eRow = ws.Cells(ws.Rows.Count, "C").End(xlUp).Row
    Dim eCol As Long: eCol = ws.Cells(3, ws.Columns.Count).End(xlToLeft).Column
    
    If eRow < 3 Then eRow = 3
    If eCol < 3 Then eCol = 12  ' L열
    
    ws.Range(ws.Cells(3, 3), ws.Cells(eRow + 10, eCol)).Borders.LineStyle = xlNone
    
    With Application
        .ScreenUpdating = True
        .EnableEvents = True
    End With
    
End Sub

 
박
박예린 작성자 9월 25일
@수메리안 님 감사합니다! 열심히 공부해보겠습니다!!

질문답변 게시판의 최근 글

스크랩 완료