I want to create a macro that would allow me to update a table with data from another table.
The only thing is that the two tables don't have the same headers and I would like to copy and paste cells only based on the "header value".
The two differents tables are not in the same worksheet.
If that can help:
Input
Output
Here's how to use ADODB.
Sub Test()
Dim Ws As Worksheet
Dim sql As String
Dim vFild()
Dim rngFild As Range, rng As Range
Dim strFild As String
Dim n As Integer
Set Ws = Sheets("Output")
With Ws
Set rngFild = .Range("a1", .Range("a1").End(xlToRight))
End With
For Each rng In rngFild
n = n + 1
ReDim Preserve vFild(1 To n)
vFild(n) = "[" & rng & "]"
Next rng
strFild = Join(vFild, ",")
sql = "select " & strFild & "from [Input$] "
exeSQL Ws, sql
End Sub
Sub exeSQL(Ws As Worksheet, strSQL As String)
Dim Rs As Object
Dim strConn As String
Dim i As Integer
strConn = "Provider=Microsoft.ACE.OLEDB.12.0;" & _
"Data Source=" & ThisWorkbook.FullName & ";" & _
"Extended Properties=Excel 12.0;"
Set Rs = CreateObject("ADODB.Recordset")
Rs.Open strSQL, strConn
If Not Rs.EOF Then
With Ws
.Range("a1").CurrentRegion.Offset(1).ClearContents
'For i = 0 To Rs.Fields.Count - 1
' .Cells(1, i + 1).Value = Rs.Fields(i).Name
'Next
.Range("a" & 2).CopyFromRecordset Rs
End With
End If
Rs.Close
Set Rs = Nothing
End Sub
Related
I've been trying to find a way to add records from excel to my ms access table and after searching and watching videos I managed to have the code below.
However it keeps adding blanks.
The excel data has the same headers with the same names on the access table minus the first column in access that contains its own numbered column
Does anyone know why?
code below
Sub AddRecords()
Dim accessFile As String
Dim accessTable As String
Dim sht As Worksheet
Dim lastRow As Long
Dim lastColumn As Integer
Dim con As Object
Dim rs As Object
Dim sql As String
Dim i As Long
Dim j As Integer
Application.ScreenUpdating = False
accessFile = "C:\Users\...\Novo.accdb"
accessTable = "TBL_Database_AVD"
Set sht = ThisWorkbook.Sheets("dados")
With sht
lastRow = .Cells(.Rows.Count, "A").End(xlUp).Row
lastColumn = .Cells(2, .Columns.Count).End(xlToLeft).Column
End With
Set con = CreateObject("ADODB.connection")
con.Open "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & accessFile
sql = "SELECT * FROM " & accessTable
Set rs = CreateObject("ADODB.Recordset")
rs.CursorType = 1
rs.LockType = 3
rs.Open sql, con
For i = 3 To lastRow
rs.AddNew
For j = 1 To lastColumn
rs(sht.Cells(i, j).Value) = sht.Cells(i, j).Value
Next j
rs.Update
Next i
rs.Close
con.Close
Set rs = Nothing
Set con = Nothing
Application.ScreenUpdating = True
Here is what I am trying to do:
Pick up SQL script from a worksheet(script has comments and queries)
Assign it to a string variable with the value of range of cells(where the scripts are pasted) from the worksheet
Execute the script by passing the string variable to ADODB connection I made before as a recordset
Paste the results of the script executed in the Oracle Database in a new sheet
So far what I have achieved:
Database connection was successful
I am able to assign the range values to a variant but not a string (Error: Type Mismatch)
If I change the variable to a variant then I am not able to execute it as a recordset.
(Error: arguments are of the wrong type, are out of acceptable range or are in conflict with one another)
I know the approach I am using is not that easy therefore I need suggestions on how I may be able to achieve this.
Private Sub RunValidation_Click()
Dim ws As Worksheet
Dim sheet As Variant
Dim StrSQL As Variant
Dim sheetnumber As Integer
Dim irow As Integer
Dim rs As ADODB.Recordset
Dim elementcount As Integer
Call OptimizeCode_Begin
Call Start_DBConnect
irow = ScriptExecutor.Range("A" & Rows.count).End(xlUp).row
elementcount = irow - 13
StrSQL = ScriptExecutor.Range("A14: A" & irow).Value
Set rs = New ADODB.Recordset
rs.Open StrSQL, cn, adOpenDynamic, adLockReadOnly, adCmdText
If Not rs.EOF Then
rs.MoveFirst
End If
i = 1
sheetnumber = Application.Sheets.count - i
Set ws = Sheets.Add(After:=Sheets(Sheets.count))
ws.name = "Extracts-" & sheetnumber
Sheets("Extracts-" & sheetnumber).Range("A2").CopyFromRecordset rs
rs.Close
Set rs = Nothing
End Sub
Try joining each row in the range with CRLF to create a string. The array assigned from the range is a single column with many rows. The JOIN function needs a single row many columns array hence the transpose function.
Dim StrSQL As String, arLines As Variant
arLines = ScriptExecutor.Range("A14: A" & irow).Value
StrSQL = Join(Application.Transpose(arLines), vbCrLf)
If you have to ignore the --comments including those on the same line as a statement (and the blank lines) then building the string one line at a time is probably the simplest method.
Dim cell As Range, sLine As String, StrSQL As String
With ScriptExecutor.Range("A14: A" & irow)
For Each cell In .Cells
sLine = Trim(cell.Value)
' remove any comments --
i = InStr(1, sLine, "--", vbTextCompare)
If i > 0 Then
sLine = Left(sLine, i - 1)
End If
If len(sLine) = 0 Then
' skip blank lines
Else
If Len(StrSQL) > 0 Then sLine = vbCrLf & sLine
StrSQL = StrSQL & sLine
End If
Next
End With
Debug.Print StrSQL
With multiple queries in the same script you get multiple record sets so try using .nextRecordSet method.
Set ws = Sheets.Add(After:=Sheets(Sheets.Count))
ws.Name = "Extracts-" & sheetnumber
Set rs = oCon.Execute(sql)
iRow = 2
Do Until rs Is Nothing
With ws
.Range("A" & iRow).CopyFromRecordset rs
iRow = .Range("A" & Rows.Count).End(xlUp).Row + 2
End With
Set rs = rs.nextRecordSet
Loop
I am able to achieve the above question with a slightly different approach. The only precondition is that the user will have to remove the comments from the script.
The script looks something like this:
Sample Script.
and the code is as follows:
Private Sub RunValidation_Click()
Dim ws As Worksheet
Dim sheet As Variant
Dim sheetnumber As Integer
Dim irow As Integer
Dim rs As ADODB.Recordset
Dim fld As ADODB.field
Dim elementcount As Integer
Dim sqlscript As Variant
Dim StrSQL As String
Dim commands As Variant
Dim cmd() As Variant
Dim i As Integer
Dim j As Integer
Dim k As Integer
Dim results As String
Dim rownum As Integer
Dim col As Integer
On Error GoTo UserForm_Initialize_Err
If ScriptExecutor.TextUser = vbNullString Then
MsgBox ("Please enter User ID.")
GoTo UserForm_Initialize_Exit
End If
If ScriptExecutor.TextPwd = vbNullString Then
MsgBox ("Please enter Password.")
GoTo UserForm_Initialize_Exit
End If
Call OptimizeCode_Begin
Call Start_DBConnect
' Figuring out the last row with data
irow = ScriptExecutor.Range("A" & Rows.count).End(xlUp).row
elementcount = irow - 13
' Assigning range to a Variant variable
sqlscript = ScriptExecutor.Range("A14: A" & irow).Value
'Converting into String
StrSQL = Join(Application.Transpose(sqlscript), vbCrLf)
' Break the script into semi-colon
commands = Split(StrSQL, ";")
' Transfer values from array with empty values to array with empty values in the end
ReDim cmd(0 To 0)
j = 0
For i = LBound(commands) To UBound(commands)
If commands(i) <> "" Then
j = j + 1
cmd(UBound(cmd)) = commands(i)
ReDim Preserve cmd(0 To UBound(cmd) + 1)
End If
Next i
'remove that empty array field at the end
If UBound(cmd) > 0 Then
ReDim Preserve cmd(0 To UBound(cmd) - 1)
End If
Set rs = New ADODB.Recordset
' Open new sheet to paste results
k = 2
sheetnumber = Application.Sheets.count - k
Set ws = Sheets.Add(After:=Sheets(Sheets.count))
ws.name = "Extracts-" & sheetnumber
' Copy results in new sheet with field names
rownum = 1
For i = LBound(cmd) To UBound(cmd)
rs.Open cmd(i), cn, adOpenDynamic, adLockOptimistic, adCmdText
rs.MoveFirst
col = 1
For Each fld In rs.Fields
With ws.Cells(rownum, col)
.Value = fld.name: .HorizontalAlignment = xlLeft: .VerticalAlignment = xlTop: .EntireColumn.AutoFit: .Font.Bold = True: .Borders.Color = vbBlack
End With
col = col + 1
Next
rownum = ws.Range("A" & Rows.count).End(xlUp).row + 1
With ws.Range("A" & rownum)
.CopyFromRecordset rs:
.Borders.Color = vbBlack
rownum = ws.Range("A" & Rows.count).End(xlUp).row + 2
End With
rs.Close
Next
Set rs = Nothing
UserForm_Initialize_Exit:
On Error Resume Next
Call OptimizeCode_End
Call End_DBConnect
Exit Sub
UserForm_Initialize_Err:
MsgBox Err.number & vbCrLf & Err.Description, vbCritical, "Error!"
Resume UserForm_Initialize_Exit
End Sub
This seems to be working perfectly at the moment but I am testing it with different scenarios.
I am new to Vba and trying to pull data records from SQL table if it matches to the cell value in Spreadsheet. The record should be printed in another tab if it's matched.
I have wrote the below code, it only prints the last row of matched record but not the whole .
Dim oConn As ADODB.Connection
Dim rs As ADODB.Recordset
Dim fld As ADODB.Field
Sub CheckRecords()
Set rs = New ADODB.Recordset
Set oConn = New ADODB.Connection
oConn.ConnectionString = "Provider=SQLOLEDB;" & _
"Server=Myserver;" & _
"authenticateduser = True; database=Db1;" & _
"User ID=Userid;Password=password;"
oConn.Open ConnectionString
Dim rowtable As Long 'number of row in the worksheet
Dim strSQL As String 'SQL string
Dim stringa As String 'the record in the worksheet that I want to check
Dim row As Integer
Dim Col As Integer
Dim height As Integer
Dim ws As ThisWorkbook
Set ws = ThisWorkbook
Application.ScreenUpdating = False
'count the number of rows with records
rowtable = Cells(Rows.Count, "B").End(xlUp).row
For height = 3 To rowtable
'variable stringa = the record of my worksheet that I want to check into DB table
stringa = Sheet4.Cells(height, 2).Value
strSQL = "SELECT member, code, list, update_date from dbtable where member = '" & stringa & "'"
rs.Open strSQL, oConn
row = 2
Do While Not rs.EOF
Col = 1
For Each fld In rs.Fields
Sheet13.Cells(row, Col).Value = fld
Col = Col + 1
Next
row = row + 1
rs.MoveNext
Loop
rs.Close
Next
oConn.Close
End Sub
I am missing something in loop, as it prints 1 record that has matched but just cannot figure out. Can somebody help me out?
I am working on importing data from an Access table into Excel. The import code that I have works in terms of pulling in data, but I have an issue with the data that is pulled in when the column in the access table is a looked up value from another table. For instance, I have EmployeeID stored in a separate table which is looked up in the table that I am extracting. The extract pulls the data, but it only pulls the autonumber that's assigned to the employee on the employee table rather than the employee name. The employee name is stored in the third column of the employee table and I need that value when the extract runs, not the autonumber. However, I don't know how to specify the column that extracts in SQL via VBA. Can someone please help? Here's what I have so far:
Sub getAccessData()
Dim DBFullName As String
Dim Connect As String, Source As String
Dim Connection As ADODB.Connection
Dim Recordset As ADODB.Recordset
Dim Col As Integer
Dim lngLastColumn As Long
Dim lngLastRow As Long
Dim OXLSheet As Worksheet
Set OXLSheet = Worksheets("WorksheetName")
Worksheets("WorksheetName").Cells.Clear
'Datebase path info
DBFullName = "C:\Users\myname\Desktop\Database Backups\database.accdb"
'Open the connection for the database
Set Connection = New ADODB.Connection
Connect = "Provider=Microsoft.ACE.OLEDB.12.0;"
Connect = Connect & "Data Source=" & DBFullName & ";"
Connection.Open ConnectionString:=Connect
'Create RecordSet
Set Recordset = New ADODB.Recordset
With Recordset
'Data Filter
Source = "SELECT EmployeeID FROM tblRetirements WHERE AllowEnteredInPayroll]Is Null AND ApplicationCancelled = 'No'"
.Open Source:=Source, ActiveConnection:=Connection
'Write field Names
For Col = 0 To Recordset.Fields.Count - 1
Worksheets("WorksheetName").Range("A5").Offset(0, Col).Value = Recordset.Fields(Col).Name
Next
'Write Recordset
Worksheets("WorksheetName").Range("A5").Offset(1, 0).CopyFromRecordset Recordset
End With
ActiveSheet.Columns.AutoFit
Set Recordset = Nothing
Connection.Close
Set Connection = Nothing
With OXLSheet
lngLastColumn = .Cells(5, .Columns.Count).End(xlToLeft).Column
lngLastRow = .Cells(.Rows.Count, "A").End(xlUp).Row
.ListObjects.Add(xlSrcRange, .Range(.Cells(5, 1), .Cells(lngLastRow, lngLastColumn)), , xlYes).Name = "Table1"
ActiveSheet.ListObjects("Table1").TableStyle = "TableStyleMedium16"
End With
End Sub
What you see in a lookup column is actually the product of a join so to get the names instead of the IDs you need to define a SQL query and export its result instead of the table itself. To include all of the records from you main table, you need to use the LEFT JOIN. If you use INNER JOIN then you will get the same result unless you have records in your main table whose related record in the employee table has been deleted:
Sub getAccessData()
Dim DBFullName As String
Dim Connect As String, Source As String
Dim Connection As ADODB.Connection
Dim Recordset As ADODB.Recordset
Dim Col As Integer
Dim lngLastColumn As Long
Dim lngLastRow As Long
Dim OXLSheet As Worksheet
Set OXLSheet = Worksheets("WorksheetName")
Worksheets("WorksheetName").Cells.Clear
'Datebase path info
DBFullName = "C:\Users\myname\Desktop\Database Backups\database.accdb"
'Open the connection for the database
Set Connection = New ADODB.Connection
Connect = "Provider=Microsoft.ACE.OLEDB.12.0;"
Connect = Connect & "Data Source=" & DBFullName & ";"
Connection.Open ConnectionString:=Connect
'Create RecordSet
Set Recordset = New ADODB.Recordset
With Recordset
'Data Filter
Source = "SELECT tblEmployeeID.Name FROM tblRetirements " & _
"LEFT JOIN tblEmployeeID on tblRetirements.EmployeeID = tblEmployeeID.Name " & _
"WHERE [AllowEnteredInPayroll] Is Null AND ApplicationCancelled = 'No'"
.Open Source:=Source, ActiveConnection:=Connection
'Write field Names
For Col = 0 To Recordset.Fields.Count - 1
Worksheets("WorksheetName").Range("A5").Offset(0, Col).Value = Recordset.Fields(Col).Name
Next
'Write Recordset
Worksheets("WorksheetName").Range("A5").Offset(1, 0).CopyFromRecordset Recordset
End With
ActiveSheet.Columns.AutoFit
Set Recordset = Nothing
Connection.Close
Set Connection = Nothing
With OXLSheet
lngLastColumn = .Cells(5, .Columns.Count).End(xlToLeft).Column
lngLastRow = .Cells(.Rows.Count, "A").End(xlUp).Row
.ListObjects.Add(xlSrcRange, .Range(.Cells(5, 1), .Cells(lngLastRow, lngLastColumn)), , xlYes).Name = "Table1"
ActiveSheet.ListObjects("Table1").TableStyle = "TableStyleMedium16"
End With
End Sub
Revised Code:
Sub getAccessData()
Dim DBFullName As String
Dim Connect As String, Source As String
Dim Connection As ADODB.Connection
Dim Recordset As ADODB.Recordset
Dim Col As Integer
Dim lngLastColumn As Long
Dim lngLastRow As Long
Dim OXLSheet As Worksheet
Set OXLSheet = Worksheets("WorksheetName")
Worksheets("WorksheetName").Cells.Clear
'Datebase path info
DBFullName = "C:\Users\myname\Desktop\Database Backups\database.accdb"
'Open the connection for the database
Set Connection = New ADODB.Connection
Connect = "Provider=Microsoft.ACE.OLEDB.12.0;"
Connect = Connect & "Data Source=" & DBFullName & ";"
Connection.Open ConnectionString:=Connect
'Create RecordSet
Set Recordset = New ADODB.Recordset
With Recordset
'Data Filter
Source = "SELECT tblEmployeeID.Name FROM tblRetirements " & _
"INNER JOIN tblEmployeeID on tblRetirements.EmployeeID = tblEmployeeID.Name " & _
"WHERE [AllowEnteredInPayroll] Is Null AND ApplicationCancelled = 'No'"
.Open Source:=Source, ActiveConnection:=Connection
'Write field Names
For Col = 0 To Recordset.Fields.Count - 1
Worksheets("WorksheetName").Range("A5").Offset(0, Col).Value = Recordset.Fields(Col).Name
Next
'Write Recordset
Worksheets("WorksheetName").Range("A5").Offset(1, 0).CopyFromRecordset Recordset
End With
ActiveSheet.Columns.AutoFit
Set Recordset = Nothing
Connection.Close
Set Connection = Nothing
With OXLSheet
lngLastColumn = .Cells(5, .Columns.Count).End(xlToLeft).Column
lngLastRow = .Cells(.Rows.Count, "A").End(xlUp).Row
.ListObjects.Add(xlSrcRange, .Range(.Cells(5, 1), .Cells(lngLastRow, lngLastColumn)), , xlYes).Name = "Table1"
ActiveSheet.ListObjects("Table1").TableStyle = "TableStyleMedium16"
End With
End Sub
This is related to
Excel / VBA Remove duplicate rows by cross referencing 2 different sheets then deleting 1 row
I can't seem to get any VBA to work well or fast enough for a couple 100 rows.
Does Excel have a formula to remove duplicates from one sheet, by cross referencing another sheet?
Thanks for all your help.
Here is a much faster VBA solution, utilizing a dictionary object. As you can see, it loops only once through sheet A and sheet B, while your original solution has a running time proportional to "number of rows in sheet A" * "number of rows in sheet B".
Option Explicit
Sub CleanDupes()
Dim wsA As Worksheet
Dim wsB As Worksheet
Dim keyColA As String
Dim keyColB As String
Dim rngA As Range
Dim rngB As Range
Dim intRowCounterA As Integer
Dim intRowCounterB As Integer
keyColA = "A"
keyColB = "B"
intRowCounterA = 1
intRowCounterB = 1
Set wsA = Worksheets("Sheet A")
Set wsB = Worksheets("Sheet B")
Dim dict As Object
Set dict = CreateObject("Scripting.Dictionary")
Do While Not IsEmpty(wsA.Range(keyColA & intRowCounterA).Value)
Set rngA = wsA.Range(keyColA & intRowCounterA)
If Not dict.Exists(rngA.Value) Then
dict.Add rngA.Value, 1
End If
intRowCounterA = intRowCounterA + 1
Loop
intRowCounterB = 1
Do While Not IsEmpty(wsB.Range(keyColB & intRowCounterB).Value)
Set rngB = wsB.Range(keyColB & intRowCounterB)
If dict.Exists(rngB.Value) Then
wsB.Rows(intRowCounterB).Delete
intRowCounterB = intRowCounterB - 1
End If
intRowCounterB = intRowCounterB + 1
Loop
End Sub
You can do a lot with ADO and Excel.
Dim cn As Object
Dim rs As Object
Dim wb As Workbook
Dim sSQL As String
Dim sFile As String
Dim sCon As String
Dim sXLFileToProcess As String
Dim i
sXLFileToProcess = "Book1z.xls"
sFile = Workbooks(sXLFileToProcess).FullName
''Note that if HDR=No, F1,F2 etc are used for column names,
''if HDR=Yes, the names in the first row of the range
''can be used.
''This is the Jet 4 connection string, you can get more
''here : http://www.connectionstrings.com/excel
sCon = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" & sFile _
& ";Extended Properties=""Excel 8.0;HDR=Yes;IMEX=1"";"
''Late binding, so no reference is needed
Set cn = CreateObject("ADODB.Connection")
Set rs = CreateObject("ADODB.Recordset")
cn.Open sCon
'' In this example, the column header for column F is F, see notes
'' above on field (column) names. It also assumes that the sheets to
'' be merged have the same column headers in the same order
'' It would be safer to list the column heards rather than use *.
sSQL = sSQL & "SELECT b.Key,b.b,b.c,b.d,b.e FROM [SheetB$] As B " _
& "LEFT JOIN [SheetA$] As A " _
& "ON B.Key=A.Key " _
& "WHERE A.Key Is Null"
rs.Open sSQL, cn, 3, 3
Set wb = Workbooks.Add
With wb.Worksheets("Sheet1")
For i = 1 To rs.Fields.Count
.Cells(1, i) = rs.Fields(i - 1).Name
Next
.Cells(2, 1).CopyFromRecordset rs
End With
''Tidy up
rs.Close
Set rs = Nothing
cn.Close
Set cn = Nothing