[엑셀 VBA] 파워쿼리 자동화 4. CRUD
[엑셀 VBA] 파워쿼리 자동화 4. CRUD
이번에 포스팅하는 내용은, 조회, 수정, 삭제, 추가와 관련된 내용입니다.
1. 자료 조회
WorrbookQuery에 있는 SQL 부분을 수정합니다.
아래 그림에서 보면 조회 조건은, 조회기간, 직책별 검색을 하는 부분입니다.
거의 모든 테이블에 거는 조건이 비슷하기에 SQL을 자동으로 만들어 주는 함수를 하나 만들었습니다.
1.1 getSQLByExpense 함수
'SQL문 만들어 반환하기 Public Function getSQLByExpense(prefix As String, tbName As String) As String Dim SDATE As Date: SDATE = shEnquiry.Range(prefix & "_SDATE").Value Dim EDATE As Date: EDATE = shEnquiry.Range(prefix & "_EDATE").Value Dim db_DATE_field As String: db_DATE_field = "DATE_" & prefix Dim strSQL As String Dim POSITION1 As String: POSITION1 = shEnquiry.Range(prefix & "_POSITION1").Value If UCase(POSITION1) = "ALL" Then strSQL = "SELECT * FROM `" & tbName & "` WHERE " & db_DATE_field & ">='" & SDATE & "' AND " & db_DATE_field & "<='" & EDATE & "' ORDER BY POSITION1, EID, " & db_DATE_field & ";" Else strSQL = "SELECT * FROM `" & tbName & "` WHERE POSITION1='" & POSITION1 & "' AND (" & db_DATE_field & ">='" & SDATE & "' AND " & db_DATE_field & "<='" & EDATE & "') ORDER BY POSITION1, EID, " & db_DATE_field & ";" End If 'Debug.Print strSQL getSQLByExpense = strSQL End Function
1.2 refresh 함수 : 이 함수가 조회를 하게 하는 함수 입니다.
' CA 데이터 갱신하기 Public Sub CA_Refresh() Call mdGlobal.setWorksheets Dim strFormula As String Dim strSQL As String: strSQL = ThisWorkbook.Worksheets("ENQUIRY").getSQLByExpense("CA", "ys_tbCA") svName = GetCustomPropertyValue("server") dbName = GetCustomPropertyValue("database") strFormula = "let Source = MySQL.Database(""" & svName & """, """ & dbName & """, [ReturnSingleDatabase=true, Query=""" & strSQL & """]), #""Transform"" = Table.TransformColumnTypes(Source,{{""DATE_CA"", type date}}) in #""Transform""" ThisWorkbook.Queries("qryCA").formula = strFormula On Error GoTo Error Application.EnableEvents = False ws.ENQUIRY.ListObjects("tableCA").TableObject.Refresh Call toolsSheet.fillRowNumber(ws.ENQUIRY.Cells(10, "B"), "D10") Application.EnableEvents = True Exit Sub Error: MsgBox Err.Description End Sub
strSQL은 getSQLByExpense() 함수를 이용해서 가져옵니다.
strFormula에 M Script언어를 대입하는데, 여기에 DB 정보 및 SQL만 넘겨 줍니다.
그리고 표를 Refresh하면 마법처럼 원하는 자료를 가지고 옵니다.
2. 수정
아래 그림을 보면 따라다니는 버튼을 두 개 만들어 두었습니다.
하나는 update이고, 하나는 delete 버튼입니다.
업데이트를 할 때, WHERE id='id'를 주어야 하기에 'id' column 값을 가져 오는 부분과
나머지 필드를 모두 가져오는 루틴이 있어야 합니다.
제일 먼저 선택한 행의 Row Number를 알아야 하기에 그것을 찾아 오는 함수 하나를 만들었습니다.
2.1 getListObjectRowNumber()
'선택한 셀이 ListObject이면 선택한 셀의 행 번호 가져오기 Public Function getListObjectRowNumber() As Long If TypeName(Selection.ListObject) = "ListObject" Then getListObjectRowNumber = Selection.row - Selection.ListObject.Range.row Exit Function End If getListObjectRowNumber = -1 End Function
그리고 난 다음 나머지 필드명과 값을 구해옵니다.
이전에 포스팅한 RangeToArray() 함수를 참조하면 됩니다.
2.2 update()함수
' CA UPDATE Public Sub CA_Update() mdGlobal.set_query_data Dim row_num: row_num = ThisWorkbook.Worksheets("ENQUIRY").getListObjectRowNumber Dim id: id = lobj.tableCA.DataBodyRange(row_num, 1).Value If id = "" Or Not IsNumeric(id) Then MsgBox "There is no 'id' or not a valid 'id' for updating.": Exit Sub End If Dim arrFields(): arrFields = toolsSheet.RangeToArray(lobj.tableCA.HeaderRowRange) Dim arrValues(): arrValues = toolsSheet.RangeToArray(lobj.tableCA.DataBodyRange.Rows(row_num)) Call mdGlobal.setMySQL(True) If MySQL.Update_Recordset(db.CA, id, arrFields, arrValues) = True Then MsgBox "Updated ..." Call mdGlobal.setMySQL(False) End Sub
참고로 저는 테이블이름이나 고정되어 있는 값들의 변수들 오타를 줄이기 위해서
Type이나 Enum을 즐겨 씁니다. 그럼 오타를 낼 확률이 매우 적습니다.
' 표 객체 Type pListObjects tableCA As ListObject tableBEN As ListObject tableCOMMUTE As ListObject tableOT As ListObject tableEMP As ListObject tableCASHIER As ListObject tableDINING As ListObject tableDISHWASHER As ListObject tableMANAGER As ListObject tableBENEFITS As ListObject tableSalary As ListObject tableSalary_BEN As ListObject tableSalary_CA As ListObject End Type Public lobj As pListObjects이
이런식으로 사용하면 타이핑 할일도 없고, 매우 편합니다.
3. 삭제
삭제는 아이디 값만 있으면 되기에 매우 쉽습니다. 삭제 전에 확인 메시지 한 번 띄워 주고 갑니다.
' CA DELETE Public Sub CA_Delete() If MsgBox("Sure to DELETE?", vbYesNoCancel, "DELETE") <> vbYes Then Exit Sub mdGlobal.setAll Dim row_num: row_num = ThisWorkbook.Worksheets("ENQUIRY").getListObjectRowNumber Dim id: id = lobj.tableCA.DataBodyRange(row_num, 1).Value If id = "" Or Not IsNumeric(id) Then MsgBox "There is no 'id' or not a valid 'id' for updating.": Exit Sub End If Call mdGlobal.setMySQL(True) If MySQL.Delete_Using_Conn(id, db.CA) = True Then lobj.tableCA.ListRows(row_num).Delete Call fillRowNumber(Cells(10, "B"), "D10") MsgBox "Deleted ..." End If Call mdGlobal.setMySQL(False) End Sub
4. 데이터 추가
추가를 하기 위해서 시트에 바로 입력하기 보다는 폼 양식을 선택했습니다.
이유는 직원 EID나 이름 같은 곳에서 오타를 내는 실수를 방지 하기 위함이기도 합니다.
폼에 있는 컨트롤의 이름을 필드명과 같게 하면 매우 쉽게 각각의 값들을 가져 올 수 있습니다.
4.1 MySQL에 삽입하고, 시트에도 내용이 추가되도록 해서 수정과 삭제를 가능하게 합니다.
'MySQL에 삽압후 Sheet에도 삽입 Private Sub btnInsertCA_Click() With Me If valueCheck(.cboPos, "Select POSITION, please") = False Then Exit Sub If valueCheck(.cboEmpName, "Select Employee's Name, please") = False Then Exit Sub If valueCheck(.txtDateCA, "Select CA DATE, please") = False Then Exit Sub If valueCheck(.cboManager, "Select Who approved this CA, please") = False Then Exit Sub Dim EID As String: EID = .cboEmpName.list(.cboEmpName.ListIndex, 0) Dim FIRST_NAME As String: FIRST_NAME = .cboEmpName.Value Dim POSITION1 As String: POSITION1 = .cboPos.Value Dim DATE_CA As Date: DATE_CA = .txtDateCA.Value Dim CA_FOR As String: CA_FOR = "CA" Dim AMOUNT As Single: AMOUNT = .txtAmountCA.Value Dim APPROVED As String: APPROVED = .cboManager.Value Dim INPUTER As String: INPUTER = GetCustomPropertyValue("admin") End With Dim arrFields(), arrValues() Dim id mdGlobal.set_query_data arrFields = Array("EID", "FIRST_NAME", "POSITION1", "DATE_CA", "CA_FOR", "AMOUNT", "APPROVED", "INPUTER") arrValues = Array(EID, FIRST_NAME, POSITION1, DATE_CA, CA_FOR, AMOUNT, APPROVED, INPUTER) Call mdGlobal.setMySQL(True) id = MySQL.Insert_Array(db.CA, arrFields, arrValues) Debug.Print id If IsNumeric(id) Then Dim listRow As listRow Set listRow = lobj.tableCA.ListRows.Add(AlwaysInsert:=True) ReDim arrValues(1 To 10) arrValues = Array(id, EID, FIRST_NAME, POSITION1, DATE_CA, CA_FOR, AMOUNT, APPROVED, INPUTER, VBA.Now) listRow.Range.Value = arrValues mdGlobal.setWorksheets Call toolsSheet.fillRowNumber(ws.ENQUIRY.Cells(10, "B"), "D10") End If Call mdGlobal.setMySQL(False) End Sub
5. 데이터의 가공
예전의 마소 FORMS를 이용해서 만들었던 출퇴근부를 웹과 PWA로 핸드폰으로 이식을 했습니다.
디자인을 너무 못해서 디자인하는게 제일 어렵더군요.... ㅠㅠ
여기서 핸드폰으로 출퇴근을 등록합니다. 직책 선택하면 자동으로 해당 직책의 이름들이 뜨고,
선택해서 출근인지 퇴근인지만 선택만 하면 됩니다. 이렇게 하면 이 자료가 MySQL 데이터로 들어가고, 이를 엑셀에서 조건에 맞게 조회를 하는 것입니다.
직책, 이름, 일자별로 정렬하고 수정 삭제를 합니다.
이렇게 얻은 데이터를 가지고 가공해서 최종 원하는 급여 명세서를 만듭니다.
이 표를 만들때, 제가 잘 몰랐던 표의 막강한 기능들을 알게 되었으며, 행열 추가 삭제시 자동으로 다른 표까지 조정을 다 해주며, SQL에서 해서 가져와야 할 각각의 계산식은 그냥 날로 먹을 수 있습니다. 저는 행추가 삽입 삭제 등등의 기능이 VBA로 했을 때 이렇게 강력한 줄은 몰랐습니다. 예전에는 수동으로 했기에 그리 대단한 줄 몰랐었는데.
이제 모든 데이터는 임대한 DB server에 있고 엑셀은 데이터를 가져와서 조회, 수정, 삭제, 추가의 기능만 하는 단말기가 되었습니다. 파일이 유실되어도 전혀 상관없습니다. 그냥 이 xlsm 파일만 있으면 언제 어디서나 가능하기 때문입니다. 물론 웹과 핸드폰에서도 이 모든 기능을 구현하도록 해 놓았습니다.
이렇게 해서 예전에 FORMS를 이용해서 만들었던 부분들을 새로이 구성하고 에러 처리를 확실하게 내 놓으니 사용하시는 분들이 멘붕에 빠지지 않고 잘 사용하고 계십니다.
지금은 새로운 프로젝트를 하는데, 공부해야 할 내용이 너무 많아, 당분간은 다시 reference 업로드로 가려 합니다.
번호 | 제목 | 작성자 | 작성일 | 추천 | 조회 |
[📚교재 출간 안내] 「진짜쓰는 실무엑셀」 , 드디어 출간되었습니다! (218)
![]() ![]() |
![]() ![]() |
2022.02.03 | 501 | 232224 | |
공지사항 |
[VBA] 구하라 그러면 주어질 것이다. - VBA 마스터 E-Book (영문판)
![]()
dra****
|
2022.07.19
|
추천 13
|
조회 1250
|
![]() |
2022.07.19 | 13 | 1250 |
61490 |
New [VBA] DATABASE 서버와 엑셀과의 데이터를 주고 받을 때 주의 할 점
(5)
![]() |
![]() |
2023.09.21 | - | 46 |
61336 |
New 작업시간을 줄여주는 단축키 설정법
(1)
![]() ![]() |
![]() ![]() |
2023.09.18 | 1 | 83 |
60205 |
sumif 다른 파일 연결시 value 경고 나오는 부분 해결방법
(3)
![]() |
![]() |
2023.08.23 | 2 | 213 |
58475 |
표수식을 드래깅으로 채울때와 CTRL+R로 채울때의 차이점
![]() ![]() |
![]() |
2023.07.11 | 2 | 372 |
45662 |
선택된 범위 정렬 단축키
(29)
![]() |
![]() |
2022.12.15 | 6 | 928 |
44293 |
[엑셀 VBA] 예약 DB - 9. 시트의 바우처&견적서 양식에 데이터 뿌리기
(17)
![]() |
![]() |
2022.11.11 | 4 | 1375 |
![]()
blue_0313
|
2023.03.21
|
추천 0
|
조회 310
|
blue_0313 | 2023.03.21 | 0 | 310 | |
44227 |
[엑셀 VBA] 예약 DB - 8. Listbox 와 폼 컨트롤 연동하고 DB로 보내기
(1)
![]() |
![]() |
2022.11.09 | 4 | 1019 |
44219 |
[엑셀 VBA] 예약 DB - 7. MySQL 클래스와 사용법
(4)
![]() |
![]() |
2022.11.09 | 2 | 923 |
43438 |
[엑셀 VBA] 예약 DB - 6. 폼 콘트롤과 DB 필드명 일치시키기
![]() |
![]() |
2022.10.21 | 2 | 597 |
43292 |
[엑셀 VBA] 예약 DB - 5. makeSQL() 함수 - INSERT, UPDATE문 자동 생성
(7)
![]() |
![]() |
2022.10.18 | 3 | 962 |
43091 |
[엑셀 VBA] 예약 DB - 4. 표 가지고 놀기
(20)
![]() |
![]() |
2022.10.13 | 1 | 1113 |
43017 |
[엑셀 VBA] 예약 DB -3. 표(테이블, ListObject)을 사용해야 하는 이유1
(15)
![]() |
![]() |
2022.10.11 | 2 | 1131 |
42998 |
[잡설] 엑셀이 웹으로 가야하는 이유2
(15)
![]() |
![]() |
2022.10.10 | 5 | 1273 |
![]()
tin
|
2023.01.12
|
추천 0
|
조회 438
|
tin | 2023.01.12 | 0 | 438 | |
42982 |
[엑셀 VBA] 예약 DB - 2. 다시 웹으로 - Access에서 MySQL로 데이터 이식
![]() |
![]() |
2022.10.10 | 2 | 732 |
40590 |
[엑셀 VBA] 예약 DB - 1. 엑세스, SQL, Recordset, ListObject 의 상관 관계
(10)
![]() |
![]() |
2022.07.29 | 3 | 1066 |
![]()
tin
|
2023.01.12
|
추천 0
|
조회 390
|
tin | 2023.01.12 | 0 | 390 | |
40511 |
[잡설] 엑셀의 꽃 파워 쿼리를 버리다.
(5)
![]() |
![]() |
2022.07.29 | 5 | 1725 |
40321 |
[경험담..해결했습니다.] &H80004005(-2147467259)시스템오류
(5)
![]() |
![]() |
2022.07.25 | 2 | 1241 |
40074 |
M365 업데이트 이후 발생하는 H80004005 (-2147467259) 자동화 오류 해결방법
(1)
![]() ![]() |
![]() ![]() |
2022.07.23 | 1 | 825 |
39608 |
엑셀 그래프에 관한 유튜브 채널정보 입니다.
(17)
![]() |
![]() |
2022.07.15 | - | 689 |
36473 |
[엑셀VBA] 시트 이름을 변수로 설정하고 싶을때 간단한 방법
(3)
![]() |
![]() |
2022.06.27 | 2 | 2478 |
@dra**** 님 잘 보았습니다. 개인적인 사견으로,... REST API를 구현한다면 더 편리할 것 같습니다. ~ ^^
excel ==> web server ==> db 순으로 처리를 한다면 보안 및 userful한 환경에서도 보다 더 괜찮을 것 같습니다.
excel에서 adodb를 사용하여 connection 한다면 분명 db server id와 pw 유출 위험도 있구요...
@dra**** 님 이런 엄청난 자료들이...감사합니다!
@dra**** 님 감사합니다,
파워쿼리가 너무 편해서.... Excel -> DB Server로 직접 소통하니 코딩 양이 많이 줄었어요.
그리고 보안이 그리 크게 문제 되는 곳이 아니어서 로그인하게 하고 VBA 막아 노니 어느정도 커버가 되네요. 내부 직원이 맘 먹고 털으려면 뭘 못하겠습니까...정보를 빼가려 하지 않는다는 전제 하에...
웹은 웹대로 똑같이 구현해 놓았기에, 엑셀->Web->DB 이렇게 가려면 굳이 엑셀로 만들 이유가 없을 듯 하기도 하고요. 엑셀로는 입출력이 웹보다는 많이 편하기에 ㅎㅎㅎ
좋은 의견 고맙습니다.
보안이 중요한 프로젝트때에는 그리 구현을 해야 하는데...
그럼 포스팅 내용이 Server response 관련된 내용으로 가야 하는데...ㅠㅠ
ToString.... 코더의 냄새가 물씬 나는 좋은 아이디입니다. ^^
@dra**** 님 감사합니다
@dra**** 님 자료들이 많네요