[엑셀 VBA] 예약 DB - 7. MySQL 클래스와 사용법

작성자
dra****
작성일
2022-11-09 16:38
조회
514

클래스를 만들고 사용하다 보니,
Connection String만 바꿔주면 MySQL, 엑세서, MS SQL, Oracle 등등
다 사용할 수 있기 때문에 범용으로 사용이 가능합니다.

하나의 함수 함수 하나씩 설명하고, 전체 소스는 맨 마지각에 두겠습니다.

  1. DB 정보와 커넥션 스트링 만들기
Private strConn As String
Private conn As ADODB.Connection
 
Private ServerAddress As String
Private myDataBase As String
Private myUserName As String
Private myPassword As String
Private port As String
 
Private Sub Class_Initialize()
End Sub
 
Private Sub Class_Terminate()
    Call disconnect_mysql
End Sub
 
Private Function connecto_to_mysql() As Boolean
 
'    ServerAddress = "localhost"
'    myDataBase = "table1"
'    myUserName = "root"
'    myPassword = "0000"
'    port = "3306"
 
'    strConn = "Driver={MySQL ODBC 8.0 Unicode Driver};Server=" & ServerAddress & ";" & _
'              "Port= " & port & ";" & _
'              "Database=" & myDataBase & ";" & _
'              "Uid=" & myUserName & ";" & _
'              "Pwd=" & myPassword & ";" & _
'              "Connect Timeout=100;"
 
    mdGlobal.DBPath = ThisWorkbook.path & "\DB\tmp.accdb"
 
    If clsUtils Is Nothing Then Set clsUtils = New clsUtils
    If clsUtils.fileExists(DBPath) = False Then
        MsgBox "에러: 엑세스 파일을 찾을 수 없습니다.", vbCritical + vbOKOnly, "엑세스 파일"
    End If
 
    strConn = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & DBPath & ";Persist Security Info=False;"
 
    On Error GoTo Error_Section
 
        Set conn = New ADODB.Connection
        conn.Open strConn
 
    connecto_to_mysql = True
 
   Exit Function
 
Error_Section:
    mdMessage.err_MSG "clsMySQL: connecto_to_mysql", Err, False
    Call disconnect_mysql
    connecto_to_mysql = False
End Function
 
Public Function disconnect_mysql() As Boolean
    On Error GoTo Error_Section
        conn.Close
        Set conn = Nothing
        disconnect_mysql = True
 
    Exit Function
 
Error_Section:
    mdMessage.err_MSG "clsMySQL: disconnect_mysql", Err, False
    disconnect_mysql = False
End Function

위에 주석 처리한 부분은 제 로칼 MySQL 서버의 주소이고,

DBPath 변수는 각종 Global 변수를 모아 놓은 Module에 있습니다.

mdGlobal.DBPath = ThisWorkbook.path & "\DB\tmp.accdb"

여기에 엑세스 파일이 있고, 최종 strConn 에 정보를 담고 Connection 인스턴스를

conn이라는 변수명으로 생성해 둡니다. 이 conn 객체를 통해서 엑세스이든, MySQL이든

DB 서버나 파일하고 통신을 할 수 있습니다.

 

2. SQL문으로 실행하기

' SQL문으로 실행 : INSERT, UPDATE, DELETE
Public Function mysql_ExecSQL_strSQL(strSQL As String) As Boolean
    Dim aff As Long
 
    On Error GoTo Error_Section
        Call conn.Execute(strSQL, aff)
 
    mysql_ExecSQL_strSQL = IIf(aff > 0, True, False)
 
    Exit Function
 
Error_Section:
    mdMessage.err_MSG "clsMySQL: mysql_ExecSQL_strSQL", Err, False
    mysql_ExecSQL_strSQL = False
End Function

SQL문을 인자로 넘겨수면 conn 개체를 통해 서버에 SQL문을 전송하고,

서버측에서 영향을 받은(affected) 레코드 갯수를 반환해서 돌려 줍니다.

위에서 aff 변수에 담긴 값이 0보다 크면 실행이 되었다는 의미이고,

0이면 어떤 이유에서던 실행이 안된 것입니다. 그 이유를 알아야 하기 때문에

Error_Section: 란에 에러 내용을 출려하는 부분을 만들어 놓았습니다.

 

3. 에러 메시시 출력

Public Sub err_MSG(funcName As String, Err As Variant, Optional message As String = "", Optional debugOnly As Boolean = True)
    Dim tmp As String
    tmp = "-------- " & funcName & " ----------" & vbNewLine
    tmp = tmp & Err.Number & ": " & Err.Description & vbNewLine
    tmp = tmp & "time: " & VBA.Now & vbNewLine
    tmp = tmp & "message: " & message & vbNewLine
    tmp = tmp & "---------------------- END ---------------------"
 
    Debug.Print tmp
 
    If debugOnly = False Then
        MsgBox tmp, vbCritical + vbOKOnly, "WARNING!!!!"
    End If
End Sub

이 함수는 클래스가 아닌 다른 모듈에 있는 에러 출력용 함수 입니다.

개발 단계에서는 어떤 에러가 나는지 구체적으로 파악해야 대처할 수 있기 때문에,

함수 이름, 에러 넘버, 에러 내용 등등 출력을 하게 합니다.

 

4. conn 개체를 이용한 SELECT 데이터 가져오기

' SQL문으로 실행 : SELECT 시 rs.recordcount=-1로 반환되는 단점이 있다. affectedRow 속성을 같이 반환한 방법을 생각해야 할 듯
Public Function mysql_ExecSQL_Recordset(ByVal strSQL As String) As Variant
    Dim aff As Long
    Dim rs As New ADODB.Recordset
 
    On Error GoTo Error_Section
        Set rs = conn.Execute(strSQL, aff)
 
    If rs Is Nothing Then
        Set mysql_ExecSQL_Recordset = Nothing
    Else
        Set mysql_ExecSQL_Recordset = rs
    End If
 
    Exit Function
 
Error_Section:
    mdMessage.err_MSG "clsMySQL: mysql_ExecSQL_Recordset", Err, False
    mysql_ExecSQL_Recordset = False
End Function

SELECT 문은 update, delete, insert 문과는 달리 반환되어 오는 것이

선택된 레코드들이기 때문에 이를 담아둘 Recordset 객체가 필요합니다.

체크를 해서 Nothing이 아니면 recordset 객체를 반환해서 호출 함수가 넘겨 받아

시트에 일괄적으로 레코드들을 넘겨 줍니다.

 

저는 MySQL 인스턴스를 생성하기 위해 mdGlobal 이라는 모듈안에

아래의 함수를 만들어 편하게 사용하고 있습니다.

Public MySQL As clsMySQL
 
Public Sub setMySQL(Optional opt As Boolean = True)
    If opt = True Then
        If MySQL Is Nothing Then Set MySQL = New clsMySQL
    Else
        If Not MySQL Is Nothing Then Set MySQL = Nothing
    End If
End Sub

 

4.1. SELECT로 불러온 레코드를 시트에 뿌리기

Public Function refresh_table(tbName As String, Optional orderBy As Boolean = False) As Boolean
    Dim strSQL As String
    Dim dbTable As String
    dbTable = getDBTableName(tbName)
 
    If VBA.Len(dbTable) = 0 Then Exit Function
 
    If orderBy = False Then
        strSQL = "SELECT * FROM " & dbTable & ";"
    Else
        strSQL = "SELECT * FROM " & dbTable & " ORDER BY ODR;"
    End If
 
    Dim lobj As ListObject
    Set lobj = mdGlobal.getListObject("SCHEMA", tbName)
 
    refresh_table = mdDB.fetchData(lobj, strSQL)
End Function

테이블 이름과 시트상의 표를 가져와서 SQL문을 생성하고,

이를 아래의 함수에 넘겨 줍니다. 이렇게 하는 이유는

엑셀파일에 불러와야할 표가 5개가 있는데,

매번 ListObject 객체 생성해서 넘겨주고 하는 일이 번거롭고,

때로는 철자 실수도 하고 해서 자동으로 하게 해 놓았습니다.

'데이터를 DB에서 가져와서 해당 시트의 테이블에 뿌리기
Public Function fetchData(lobj As ListObject, strSQL As String) As Boolean
 
    Call mdGlobal.setMySQL(True)
 
    Dim rs As ADODB.Recordset
 
    On Error GoTo Error_Section
 
        Set rs = MySQL.mysql_SELECT(strSQL)
        'Set rs = mySQL.mysql_ExecSQL_Recordset(strSQL)
 
        If Not rs Is Nothing Then
 
            Application.EnableEvents = False
            Application.ScreenUpdating = False
 
            If lobj.ListRows.Count > 0 Then lobj.DataBodyRange.delete
 
            lobj.ListRows.Add AlwaysInsert:=True
            lobj.DataBodyRange.Cells(1, 1).CopyFromRecordset rs
        End If
 
    Set lobj = Nothing
    rs.Close
    Set rs = Nothing
    Call mdGlobal.setMySQL(False)
 
 
    Application.EnableEvents = True
    Application.ScreenUpdating = True
 
    fetchData = True
 
    Exit Function
 
Error_Section:
    mdMessage.err_MSG "mdDB: fetchData", Err, False
 
    Set lobj = Nothing
    Set rs = Nothing
    Call mdGlobal.setMySQL(False)
    Application.EnableEvents = True
    Application.ScreenUpdating = True
 
    fetchData = False
End Function

Call mdGlobal.setMySQL(True) : MySQL Instance 생성

Set rs = mySQL.mysql_ExecSQL_Recordset(strSQL) : 해당 recordset을 반환 받음

위에 코드 보시면, 2개가 있는데, 아무거나 쓰셔도 됩니다.

 

If lobj.ListRows.Count > 0 Then lobj.DataBodyRange.delete : 표에 데이터가 있으면 삭제

lobj.ListRows.Add AlwaysInsert:=True : 표에 행을 추가모드로 변경

 

lobj.DataBodyRange.Cells(1, 1).CopyFromRecordset rs :

이 부분 알아 내는데 3개월 정도 걸렸습니다. 여기서 .Cells(1,1)을 안 주면,

날짜와 숫자의 값들이 모두 이상하게 변합니다. 날짜 자리에 숫자가 나오거나,

숫자 자리에 날짜가 나오고, 지 멋대로 나업니다. (엑셀의 최대 단점, 날짜/숫자 자동 변경)

이걸 일일히 보정해주는 함수도 만들었는다. .Cells(1,1)을 주고 나면 그런 현상이 사라집니다.

 

아래는 사용한 메모리 자원들 해제. Local 변수는 안해줘도 함수 종료 되면서 해제 되지만,

MySQL 글로벌 변수는 안해주면, 문제가 생길 수 있습니다.

이 엑세스 파일을 열어 놓고, 닫지 않으면, 여러명의 사용자가 동시에 접속했을 때,

CRUD를 사용할 수 없게 될 수도 있습니다. cursorType 같은 설정을 제대로 않하면.

Set lobj = Nothing

rs.Close Set rs = Nothing

Call mdGlobal.setMySQL(False)

 

4.2 Recordset을 이용한 SELECT 데이터 가져오기

Public Function mysql_SELECT(strSQL As String) As Variant
 
    Dim rs As New ADODB.Recordset
'   rs.CursorLocation = adUseClient
'   rs.ActiveConnection = Conn
'   rs.Source = strSQL
    rs.Open strSQL, conn, CursorTypeEnum.adOpenDynamic, LockTypeEnum.adLockOptimistic 'adOpenDynamic = 2, adLockOptimistic= 3
'    rs.Open strSQL, conn
 
'    Debug.Print rs.RecordCount
    On Error GoTo Error_Section
        If rs Is Nothing Then
            Set mysql_SELECT = Nothing
        Else
            Set mysql_SELECT = rs
        End If
    Exit Function
 
Error_Section:
    mdMessage.err_MSG "clsMySQL: mysql_SELECT", Err, False
    mysql_SELECT = False
End Function

 

5. INSERT문에서 방금 삽입한 레코드 일련번호 가져오기

Insert의 경우 SQL문을 실행하면 되는데,

삽입한 일련 번호를 가져와야 하는 경우가 있습니다.

Public Function mysql_INSERT_strSQL(strSQL As String) As Long
   Dim aff As Byte, SER As Long
   On Error GoTo Error_Section
      Call conn.Execute(strSQL, aff)
 
      If aff > 0 Then
        SER = conn.Execute("SELECT @@Identity")(0)
        mysql_INSERT_strSQL = SER
     Else
        mysql_INSERT_strSQL = -1
     End If
   Exit Function
 
Error_Section:
    mdMessage.err_MSG "clsMySQL: mysql_INSERT_strSQL", Err, False
    mysql_INSERT_strSQL = False
End Function

conn.Execute("SELECT @@Identity")(0) : 이 코드를 실행하면 마지막에 삽입한 레코드의 일련번호를 반환해 줍니다.

 

5.1 데이터가 많아 SQL문을 만들기 부담 스런상황의 INSERT
제가 가장 즐겨 쓰는 방법입니다. SQL문을 만드는 것이 아니라,
필드명과 값을 배열로 반들어 인자로 넘겨 줍니다.

Public Function mysql_INSERT_Array(ByVal tbName As String, arrFields, arrValues) As Long
 
    On Error GoTo Error_Section
        'rs.Open tbName, Conn, adOpenDynamic, adLockOptimistic, adCmdTable
        Dim rs As New ADODB.Recordset
        rs.ActiveConnection = conn
        rs.CursorType = adOpenDynamic
        rs.LockType = adLockOptimistic
        rs.Open tbName, , , , adCmdTable
 
        rs.AddNew arrFields, arrValues
        rs.update
 
        Dim SER: SER = conn.Execute("SELECT @@Identity")(0)
        mysql_INSERT_Array = IIf(SER > 0, SER, 0)
 
    Exit Function
 
Error_Section:
    mdMessage.err_MSG "clsMySQL: mysql_INSERT_Array", Err, False
    mysql_INSERT_Array = 0
End Function

 

rs.Open tbName, , , , adCmdTable : DB의 테이블 이름을 인자로 넘기로 Table로 작업하겠다는 옵션을 줍니다.

rs.AddNew arrFields, arrValues : 이러면 해당 테이블의 맨 마지막으로 커서를 이동하고 레코드를 넣습니다. 취소할 것을 대비해서 가상으로 넣은 것이고 마지막에 반드시 rs.update 를 해 주어야 비로서 들어갑니다.

 

5.2 시트의 Range 범위를 입력하여 하는 방법
예전에 즐겨 썼는데... 코드가 지저분해지고,
Range가 변하면 매번 수정을 해야 하는 단점이 있습니다.
그래서 찾아낸 것이 ListObject를 쓰는 것이고 지금은 사용 안합니다.

Public Function MySQL_INSERT_Range(tbName As String, ws As Worksheet, rowStart As Long, rowEnd As Long, colStart As Long, colEnd As Long) As Boolean
   Dim rs As ADODB.Recordset
   Set rs = New ADODB.Recordset
 
   On Error GoTo Error_MySQL_INSERT_Range
 
      Dim strSQL As String
      strSQL = "SELECT * FROM " & tbName & ";"
      rs.Open strSQL, strConn, CursorTypeEnum.adOpenDynamic, LockTypeEnum.adLockOptimistic 'adOpenDynamic = 2, adLockOptimistic= 3
 
      Dim row As Long, col As Long
      Dim fdName As String, val As Variant
 
      For row = rowStart To rowEnd
         rs.AddNew
         For col = colStart To colEnd
            fdName = Cells(1, col).Text
            val = Cells(row, col).value
 
            Debug.Print row & ":" & col & vbTab & fdName & ":" & val
            rs.Fields(fdName) = val
         Next col
         Debug.Print row
      Next row
      rs.update
 
   On Error GoTo 0
   rs.Close
   Set rs = Nothing
   MySQL_INSERT_Range = True
   Exit Function
 
Error_MySQL_INSERT_Range:
   MsgBox Err.Description, vbInformation
   Set rs = Nothing
   MySQL_INSERT_Range = False
End Function

 

6. recordset을 이용한 UPDATE

간단한 update의 경우 SQL문을 쓰면 되지만,
필드와 값이 많으면, 그것을 일일히 SQL문으로 만드는 것도 힘듭니다.
INSERT처럼 필드와 값을 배열로 넘겨 주면 되는데, 한가지 다른 점이 있습니다.
WHERE문에 상당한 부분을 인자로 넘겨 주어야 하는데,

Public Function mysql_UPDATE_Recordset(ByVal tbName As String, ByVal criterion As String, arrFields, arrValues) As Boolean
 
   On Error GoTo Error_Section
      Dim rs As New ADODB.Recordset
      rs.ActiveConnection = conn
      rs.CursorType = adOpenDynamic
      rs.LockType = adLockOptimistic            'adLockPessimistic :다른 사용자가 이 레코드를 변경, 수정하지 못하게 락을 거는
      rs.Open tbName, , , , adCmdTable
 
'     Dim criteria As String: criteria = "Ser = " & key
      Dim criteria As String: criteria = criterion
 
      'rs.MoveLast
      'rs.Find criteria, , adSearchBackward
      rs.Find criteria
      rs.update arrFields, arrValues
 
      rs.Close
      Set rs = Nothing
      mysql_UPDATE_Recordset = True
 
   Exit Function
 
Error_Section:
'    rs.Close
    Set rs = Nothing
    mdMessage.err_MSG "clsMySQL: mysql_UPDATE_Recordset", Err, False
 
    mysql_UPDATE_Recordset = False
End Function

Dim criteria As String: criteria = "Ser = " & key :  "WHERE Ser = key" 이런 문구와 같은 것입니다.

rs.Find criteria : 해당 레코드를 찾아 그 위치로 커서를 이동합니다. 못 찾으면 에러를 발생시킵니다.

rs.update arrFields, arrValues : 배열에 담긴 값을 update해 줍니다.

 

recordset update는 에러가 없으면 결과 값이 성공이고, 있으면 실패가 됩니다.

그래서 반드시 에러 체크 루틴을 만들어 주어야 합니다.

 

7. 데이터의 존재 여부 확인

Function IsDataExist(strSQL As String) As Boolean
    Dim aff As Long
 
    On Error GoTo Error_Section
        Call conn.Execute(strSQL, aff)
 
        IsDataExist = IIf(aff > 0, True, False)
    Exit Function
 
Error_Section:
    mdMessage.err_MSG "clsMySQL: IsDataExist", Err, False
    IsDataExist = False
End Function

가끔 업데이트 하기전이나 필요에 따라 레코드 존재 유무를 간단히 확인하고자 할 때

 

이 이외에 몇 개의 함수가 더 있기는 한데... 별로 중요치는 않아서... ^^

여기의 코드를 이해하신 다면, 제가 1년 정도 학습한 데이터베이스 다루는 기술을 다 습특하셨다고 보셔도 무방합니다.

 

8. clsMySQL 전체 소스

Option Explicit
 
'//   mySQL Connector/ODBC Driver 설치                      //'
'//      https://dev.mysql.com/downloads/file/?id=506041    : ODBC
'//      https://dev.mysql.com/downloads/file/?id=506231    : ADO
'//   strPath : DB가 있는 엑셀 파일이음                     //'
'//   strSQL : SQL문                                        //'
'//   pasteRange : 검색한 레코드를 붙여 넣을 범위           //'
 
Private strConn As String
Private conn As ADODB.Connection
 
Private ServerAddress As String
Private myDataBase As String
Private myUserName As String
Private myPassword As String
Private port As String
 
 
Private Sub Class_Initialize()
    'If connecto_to_mysql = False Then Exit Sub
End Sub
 
Private Sub Class_Terminate()
    Call disconnect_mysql
End Sub
 
Private Function connecto_to_mysql() As Boolean
 
'    strConn = "Driver={MySQL ODBC 8.0 Unicode Driver};Server=" & ServerAddress & ";" & _
'              "Port= " & port & ";" & _
'              "Database=" & myDataBase & ";" & _
'              "Uid=" & myUserName & ";" & _
'              "Pwd=" & myPassword & ";" & _
'              "Connect Timeout=100;"
 
   mdGlobal.DBPath = ThisWorkbook.path & "\DB\tmp.accdb"
 
    If clsUtils Is Nothing Then Set clsUtils = New clsUtils
    If clsUtils.fileExists(DBPath) = False Then
        MsgBox "에러: 엑세스 파일을 찾을 수 없습니다.", vbCritical + vbOKOnly, "엑세스 파일"
    End If
 
    strConn = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & DBPath & ";Persist Security Info=False;"
 
    On Error GoTo Error_Section
        Set conn = New ADODB.Connection
        conn.Open strConn
 
    connecto_to_mysql = True
   Exit Function
 
Error_Section:
    mdMessage.err_MSG "clsMySQL: connecto_to_mysql", Err, False
    Call disconnect_mysql
    connecto_to_mysql = False
End Function
 
Public Function disconnect_mysql() As Boolean
    On Error GoTo Error_Section
        conn.Close
        Set conn = Nothing
        disconnect_mysql = True
 
    Exit Function
 
Error_Section:
    mdMessage.err_MSG "clsMySQL: disconnect_mysql", Err, False
    disconnect_mysql = False
End Function
 
Private Sub closeRcordset(rs As ADODB.Recordset)
    If Not rs Is Nothing Then
        rs.Close
        Set rs = Nothing
    End If
End Sub
 
' --------------------------------------------------------------------------------------
' Data Handling-------------------------------------------------------------------------
 
' SQL문으로 실행 : INSERT, UPDATE, DELETE
Public Function mysql_ExecSQL_strSQL(strSQL As String) As Boolean
    Dim aff As Long
 
    On Error GoTo Error_Section
        Call conn.Execute(strSQL, aff)
 
    mysql_ExecSQL_strSQL = IIf(aff > 0, True, False)
 
    Exit Function
 
Error_Section:
    mdMessage.err_MSG "clsMySQL: mysql_ExecSQL_strSQL", Err, False
    mysql_ExecSQL_strSQL = False
End Function
 
 
' SQL문으로 실행 : SELECT 시 rs.recordcount=-1로 반환되는 단점이 있다. affectedRow 속성을 같이 반환한 방법을 생각해야 할 듯
Public Function mysql_ExecSQL_Recordset(ByVal strSQL As String) As Variant
    Dim aff As Long
    Dim rs As New ADODB.Recordset
 
    On Error GoTo Error_Section
        Set rs = conn.Execute(strSQL, aff)
 
    If rs Is Nothing Then
        Set mysql_ExecSQL_Recordset = Nothing
    Else
        Set mysql_ExecSQL_Recordset = rs
    End If
 
    Exit Function
 
Error_Section:
    mdMessage.err_MSG "clsMySQL: mysql_ExecSQL_Recordset", Err, False
    mysql_ExecSQL_Recordset = False
End Function
 
 
' SELECT ----------------------------------------------------------------------
' SELECT ----------------------------------------------------------------------
 
Public Function mysql_SELECT(strSQL As String) As Variant
 
    Dim rs As New ADODB.Recordset
'   rs.CursorLocation = adUseClient
'   rs.ActiveConnection = Conn
'   rs.Source = strSQL
    rs.Open strSQL, conn, CursorTypeEnum.adOpenDynamic, LockTypeEnum.adLockOptimistic 'adOpenDynamic = 2, adLockOptimistic= 3
'    rs.Open strSQL, conn
 
'    Debug.Print rs.RecordCount
    On Error GoTo Error_Section
        If rs Is Nothing Then
            Set mysql_SELECT = Nothing
        Else
            Set mysql_SELECT = rs
        End If
    Exit Function
 
Error_Section:
    mdMessage.err_MSG "clsMySQL: mysql_SELECT", Err, False
    mysql_SELECT = False
End Function
 
Public Function mysql_INSERT_strSQL(strSQL As String) As Long
   Dim aff As Byte, SER As Long
   On Error GoTo Error_Section
      Call conn.Execute(strSQL, aff)
 
      If aff > 0 Then
        SER = conn.Execute("SELECT @@Identity")(0)
        mysql_INSERT_strSQL = SER
     Else
        mysql_INSERT_strSQL = -1
     End If
   Exit Function
 
Error_Section:
    mdMessage.err_MSG "clsMySQL: mysql_INSERT_strSQL", Err, False
    mysql_INSERT_strSQL = False
End Function
 
Public Function mysql_INSERT_Array(ByVal tbName As String, arrFields, arrValues) As Long
 
    On Error GoTo Error_Section
        'rs.Open tbName, Conn, adOpenDynamic, adLockOptimistic, adCmdTable
        Dim rs As New ADODB.Recordset
        rs.ActiveConnection = conn
        rs.CursorType = adOpenDynamic
        rs.LockType = adLockOptimistic
        rs.Open tbName, , , , adCmdTable
 
        rs.AddNew arrFields, arrValues
        rs.update
 
        Dim SER: SER = conn.Execute("SELECT @@Identity")(0)
        mysql_INSERT_Array = IIf(SER > 0, SER, 0)
 
    Exit Function
 
Error_Section:
    mdMessage.err_MSG "clsMySQL: mysql_INSERT_Array", Err, False
    mysql_INSERT_Array = 0
End Function
 
Public Function MySQL_INSERT_Range(tbName As String, ws As Worksheet, rowStart As Long, rowEnd As Long, colStart As Long, colEnd As Long) As Boolean
   Dim rs As ADODB.Recordset
   Set rs = New ADODB.Recordset
 
   On Error GoTo Error_MySQL_INSERT_Range
 
      Dim strSQL As String
      strSQL = "SELECT * FROM " & tbName & ";"
      rs.Open strSQL, strConn, CursorTypeEnum.adOpenDynamic, LockTypeEnum.adLockOptimistic 'adOpenDynamic = 2, adLockOptimistic= 3
 
      Dim row As Long, col As Long
      Dim fdName As String, val As Variant
 
      For row = rowStart To rowEnd
         rs.AddNew
         For col = colStart To colEnd
            fdName = Cells(1, col).Text
            val = Cells(row, col).value
 
            Debug.Print row & ":" & col & vbTab & fdName & ":" & val
            rs.Fields(fdName) = val
         Next col
         Debug.Print row
      Next row
      rs.update
 
   On Error GoTo 0
   rs.Close
   Set rs = Nothing
   MySQL_INSERT_Range = True
   Exit Function
 
Error_MySQL_INSERT_Range:
   MsgBox Err.Description, vbInformation
   Set rs = Nothing
   MySQL_INSERT_Range = False
End Function
 
 
' UPDATE ----------------------------------------------------------------------
' UPDATE ----------------------------------------------------------------------
 
Public Function mysql_UPDATE_Recordset(ByVal tbName As String, ByVal criterion As String, arrFields, arrValues) As Boolean
 
   On Error GoTo Error_Section
      Dim rs As New ADODB.Recordset
      rs.ActiveConnection = conn
      rs.CursorType = adOpenDynamic
      rs.LockType = adLockOptimistic            'adLockPessimistic :다른 사용자가 이 레코드를 변경, 수정하지 못하게 락을 거는
      rs.Open tbName, , , , adCmdTable
 
'     Dim criteria As String: criteria = "Ser = " & key
      Dim criteria As String: criteria = criterion
 
      'rs.MoveLast
      'rs.Find criteria, , adSearchBackward
      rs.Find criteria
      rs.update arrFields, arrValues
 
      rs.Close
      Set rs = Nothing
      mysql_UPDATE_Recordset = True
 
   Exit Function
 
Error_Section:
'    rs.Close
    Set rs = Nothing
    mdMessage.err_MSG "clsMySQL: mysql_UPDATE_Recordset", Err, False
 
    mysql_UPDATE_Recordset = False
End Function
 
 
' -----------------------------------------------------
' -----------------------------------------------------
Function IsDataExist(strSQL As String) As Boolean
    Dim aff As Long
 
    On Error GoTo Error_Section
        Call conn.Execute(strSQL, aff)
 
        IsDataExist = IIf(aff > 0, True, False)
    Exit Function
 
Error_Section:
    mdMessage.err_MSG "clsMySQL: IsDataExist", Err, False
    IsDataExist = False
End Function
 
' 표 없데이트 또는 삽입 -------------------------------
' -----------------------------------------------------
 
Public Function mysql_ListObject_Update(lobj As ListObject, tbName As String, criterion As String) As Boolean
 
    Dim criteria As String
 
'    Dim arrFields(): arrFields = Array("Ser", "GC", "NET_WE", "NET_WD", "MEM_WE", "MEM_WD", "VIP_WE", "VIP_WD", "OTH_WE", "OTH_WD", "NOTE", "DEADLINE", "LIST_ORDER")
    Dim arrFields()
    Dim arrValues()
    Dim cnt As Long: cnt = lobj.ListRows.Count
    Dim key As Variant
    Dim row As Long
    Dim newSerial As Long
 
    On Error GoTo Error_Section
 
        arrFields = RangeToArray(lobj.HeaderRowRange, True, False)
 
        Dim rs As New ADODB.Recordset
        rs.ActiveConnection = conn
        rs.CursorType = adOpenDynamic
        rs.LockType = adLockOptimistic
        rs.Open tbName, , , , adCmdTable
 
        For row = 1 To cnt
            key = VBA.Trim(lobj.ListRows(row).Range(, 1).value)
            arrValues = RangeToArray(lobj.ListRows(row).Range, True, False)
 
 
            If VBA.Len(key) > 0 Then
                'UPDATE
                criteria = criterion & key
                rs.MoveFirst
                rs.Find criteria
                rs.update arrFields, arrValues
                Debug.Print key & ": UPDATED"
                Debug.Print "-------------------------------------"
            Else
                'INSERT
                rs.AddNew arrFields, arrValues
                rs.update
                newSerial = conn.Execute("SELECT @@Identity")(0)
                Debug.Print newSerial & ": INSERTED"
                Debug.Print "-------------------------------------"
            End If
        Next row
 
    rs.Close
    Set rs = Nothing
    mysql_ListObject_Update = True
    Exit Function
 
Error_Section:
    Set rs = Nothing
    mdMessage.err_MSG "clsMySQL: mysql_ListObject_Update", Err, False
 
    mysql_ListObject_Update = False
End Function
 
 
 
 
Public Function RangeToArray(rng As Range, Optional tpose As Boolean = True, Optional includeIndex = False)
   Dim arr()
   Dim arrResult()
   Dim i As Long, index As Long, endIndex As Long
   arr = IIf(tpose, Application.Transpose(rng.value), rng.value)
 
'   Debug.Print "rng LBound Value Columns: " & LBound(rng.Value, 1) : 헤더 포함 행의 개수
'   Debug.Print "rng UBound Value Columns: " & UBound(rng.Value, 2) : 칼럼의 개수
 
   If includeIndex = False Then
      ReDim arrResult(1 To UBound(arr, 1) - 1)
      index = 2
      endIndex = UBound(arr, 1) - 1
   Else
      ReDim arrResult(1 To UBound(arr, 1))
     index = 1
      endIndex = UBound(arr, 1)
   End If
 
   Dim tmp
   For i = 1 To endIndex
      tmp = VBA.Trim(arr(index, 1))
      If tmp = "" Then tmp = Null
      arrResult(i) = tmp
continue:
      index = index + 1
   Next i
   RangeToArray = arrResult
End Function

 

 

 

 

 

 

 

 

스크랩
공유
회원등급 : 29레벨
포인트 : 4348 EP
전체 3

  • 2022-11-10 17:58

    @dra**** 님 엑셀과 MySQL을 동시에 활용하는 그 순간 풀스택으로 개발자로 성장할 수 있습니다.

    한가지 문제는 엑셀을 프론트로쓰면 보안이 걸린다는 것인데.. 적당히 trade-off 하면 웬만한 사업장에서는 거의 무결하게 사용할 수 있어서 증말 편리합니다 ㅎㅎ


    • 2022-11-10 19:59

      @더블유에이 님 보안 부분은 엑셀 태생의 한계라 어쩔 수 없습니다.

      다만 보안이 크게 문제가 안되는 소규모 사업체는 매우 유용합니다.

      지금 개발해 드린 업체 사장님은 6명의 직원이 동시에 엑셀로 예약 업무를 하고 계시는데,

      만족도가 높아서, 컨설팅 관련 부분도 개발해 달라 하시더군요.

      자료 유출을 한다 해서 큰 문제가 되지도 않을 뿐더러, 매일 같이 DB를 백업 받아 놓으시기 때문에, 혹시 있을 지 모르는 불상사도 대비를 하고 계십니다.

      엑셀 파일이야 개발한 제가 보관 중이니, 다시 보내드리면 되고.

       

      Q&A에 올라오는 글들을 보면, 자동화를 갈구하시는 분들이 너무 많은데,

      어떻게 해야할 지를 모르니 많이 안타까울 뿐입니다.


  • 2023-03-30 14:51

    @dra**** 님 4.1 4번째 줄에

      dbTable = getDBTableName(tbName)

    이건 데이터베이스에 테이블이 있는지 없는지 검사하기 위한 건가요?


전체 74
번호 제목 작성자 작성일 추천 조회
알림
[📚교재 출간 안내] 「진짜쓰는 실무엑셀」 , 드디어 출간되었습니다! (179)
오빠두엑셀 | 2022.02.03 | 추천 469 | 조회 184806
오빠두엑셀 2022.02.03 469 184806
공지사항
[VBA] 구하라 그러면 주어질 것이다. - VBA 마스터 E-Book (영문판) 첨부파일 (13)
dra**** | 2022.07.19 | 추천 10 | 조회 772
dra**** 2022.07.19 10 772
45662
선택된 범위 정렬 단축키 (20)
Denise | 2022.12.15 | 추천 5 | 조회 523
Denise 2022.12.15 5 523
44293
[엑셀 VBA] 예약 DB - 9. 시트의 바우처&견적서 양식에 데이터 뿌리기 (15)
dra**** | 2022.11.11 | 추천 4 | 조회 888
dra**** 2022.11.11 4 888
Re:[엑셀 VBA] 예약 DB - 9. 시트의 바우처&견적서 양식에 데이터 뿌리기
blue_0313 | 2023.03.21 | 추천 0 | 조회 69
blue_0313 2023.03.21 0 69
44227
[엑셀 VBA] 예약 DB - 8. Listbox 와 폼 컨트롤 연동하고 DB로 보내기 (1)
dra**** | 2022.11.09 | 추천 3 | 조회 566
dra**** 2022.11.09 3 566
44219
[엑셀 VBA] 예약 DB - 7. MySQL 클래스와 사용법 (3)
dra**** | 2022.11.09 | 추천 2 | 조회 514
dra**** 2022.11.09 2 514
43438
[엑셀 VBA] 예약 DB - 6. 폼 콘트롤과 DB 필드명 일치시키기
dra**** | 2022.10.21 | 추천 2 | 조회 310
dra**** 2022.10.21 2 310
43292
[엑셀 VBA] 예약 DB - 5. makeSQL() 함수 - INSERT, UPDATE문 자동 생성 (7)
dra**** | 2022.10.18 | 추천 3 | 조회 533
dra**** 2022.10.18 3 533
43091
[엑셀 VBA] 예약 DB - 4. 표 가지고 놀기 (17)
dra**** | 2022.10.13 | 추천 1 | 조회 695
dra**** 2022.10.13 1 695
43017
[엑셀 VBA] 예약 DB -3. 표(테이블, ListObject)을 사용해야 하는 이유1 (15)
dra**** | 2022.10.11 | 추천 2 | 조회 580
dra**** 2022.10.11 2 580
42998
[잡설] 엑셀이 웹으로 가야하는 이유2 (11)
dra**** | 2022.10.10 | 추천 5 | 조회 826
dra**** 2022.10.10 5 826
Re:[잡설] 엑셀이 웹으로 가야하는 이유2
tin | 2023.01.12 | 추천 0 | 조회 161
tin 2023.01.12 0 161
42982
[엑셀 VBA] 예약 DB - 2. 다시 웹으로 - Access에서 MySQL로 데이터 이식
dra**** | 2022.10.10 | 추천 2 | 조회 395
dra**** 2022.10.10 2 395
40590
[엑셀 VBA] 예약 DB - 1. 엑세스, SQL, Recordset, ListObject 의 상관 관계 (9)
dra**** | 2022.07.29 | 추천 3 | 조회 674
dra**** 2022.07.29 3 674
Re:[엑셀 VBA] 예약 DB - 1. 엑세스, SQL, Recordset, ListObject 의 상관 관계
tin | 2023.01.12 | 추천 0 | 조회 107
tin 2023.01.12 0 107
40511
[잡설] 엑셀의 꽃 파워 쿼리를 버리다. (5)
dra**** | 2022.07.29 | 추천 5 | 조회 1152
dra**** 2022.07.29 5 1152
40321
[경험담..해결했습니다.] &H80004005(-2147467259)시스템오류 (4)
티엠프이 | 2022.07.25 | 추천 2 | 조회 732
티엠프이 2022.07.25 2 732
40074
M365 업데이트 이후 발생하는 H80004005 (-2147467259) 자동화 오류 해결방법
오빠두엑셀 | 2022.07.23 | 추천 1 | 조회 511
오빠두엑셀 2022.07.23 1 511
39608
엑셀 그래프에 관한 유튜브 채널정보 입니다. (16)
레몬네이드 | 2022.07.15 | 추천 0 | 조회 395
레몬네이드 2022.07.15 - 395
36473
[엑셀VBA] 시트 이름을 변수로 설정하고 싶을때 간단한 방법 (3)
알파고 | 2022.06.27 | 추천 2 | 조회 1705
알파고 2022.06.27 2 1705
35196
Print_Area 동적 인쇄 영역설정 첨부파일 (7)
sean | 2022.06.21 | 추천 4 | 조회 1800
sean 2022.06.21 4 1800
34607
셀레니움으로 개별공시지가 조회 첨부파일 (5)
나야 | 2022.06.02 | 추천 2 | 조회 452
나야 2022.06.02 2 452
26273
엑셀 2021, M365 가로스크롤 기능 (39)
더블유에이 | 2022.01.23 | 추천 17 | 조회 1295
더블유에이 2022.01.23 17 1295
Re:엑셀 2021, M365 가로스크롤 기능
김학동 | 2022.01.23 | 추천 1 | 조회 353
김학동 2022.01.23 1 353
Re:엑셀 2021, M365 가로스크롤 기능
김동희 | 2022.05.30 | 추천 0 | 조회 200
김동희 2022.05.30 0 200
24189
[엑셀 VBA] Snippet - OCR: 이미지 파일을 텍스트 파일로 변환하기 (28)
dra**** | 2021.12.01 | 추천 8 | 조회 3516
dra**** 2021.12.01 8 3516
Re:[엑셀 VBA] Snippet - OCR: 이미지 파일을 텍스트 파일로 변환하기
tin | 2023.01.12 | 추천 0 | 조회 90
tin 2023.01.12 0 90