I have a table on Excel, range A1:Sn (where n is the LastRow). Previously I used to to loop in each row and insert it one by one.
This works fine, and I can resort back to it, but I am looking to insert the entire recordset ("A1:S" & LastRow) into SQL Table, rather than looping row by row.
The reason for this is if I am inserting as a whole recordset will be treated as 1x operation, and therefore will make generating a receipt id for multiple users significantly easier.
Code
Dim con As ADODB.Connection
Dim rs As ADODB.Recordset
Set con = New ADODB.Connection
Set rs = New ADODB.Recordset
dim i as long
dim LastRow as long
LastRow = Sheets("Project_Name").Cells(Rows.count, "B").End(xlUp).row
con.Open "Provider=SQLOLEDB; Data Source=LO1WPFSASDB001 ; Initial Catalog=database; User ID=username; Password=password; Trusted_Connection=no"
rs.Open "SELECT * from table;", con, adOpenKeyset, adLockOptimistic
With rs
for i = 1 to LastRow
.addnew
!somevalue = range("A1:S" & LastRow)
.update
next
.Close
End With
con.Close
Set con = Nothing
Set rs = Nothing
I cannot seem to get it to work. I would appreciate your input.
It seems that you need to change the loop structure.
Dim con As ADODB.Connection
Dim Rs As ADODB.Recordset
Dim strConn As String
Dim i As Long, j As Integer
Dim LastRow As Long
Dim vDB
Set con = New ADODB.Connection
Set Rs = New ADODB.Recordset
LastRow = Sheets("Project_Name").Cells(Rows.Count, "B").End(xlUp).Row
vDB = Sheets("Project_Name").Range("A1:S" & LastRow)
strConn = "Provider=SQLOLEDB; Data Source=LO1WPFSASDB001 ; Initial Catalog=database; User ID=username; Password=password; Trusted_Connection=no"
With Rs
.ActiveConnection = strConn
.CursorType = adOpenDynamic
.LockType = adLockOptimistic
.Open
For i = 1 To UBound(vDB, 1)
.AddNew
For j = 1 To UBound(vDB, 2)
.Fields(j) = vDB(i, j)
Next j
.Update
Next i
End With
With Rs
End With
Set Rs = Nothing
You still need to loop the data on excel and insert/add it.
Following your current code. The second loop is to insert the columns.
for i = 1 to LastRow
.addnew
For n = 0 To .Fields.Count - 1
.Fields(n).Value = Cells(i, n + 1)
Next n
.update
next
I would take a different approach to avoid the second loop. Instead of using .addnew I would loop the data in excel, create the INSERT Strings and do an .Execute "INSERT ..." instead. You can skip the rs.Open using this method, just opening the connection and executing on it is fine.
for i = 1 to LastRow
sqlString = "INSERT INTO TableName (Field1, Field2, Field3, Field4...) VALUES (Cells(i, 1), Cells(i, 2), Cells(i, 3), Cells(i, 4)...)"
con.Execute(sqlString)
next
Edit: Using the insert method, you must enclose text values with ' marks otherwise the INSERT statement will return an invalid value type.
Related
I'm trying to load a table from Microsoft Access and paste it into Excel cells with VBA.
My path is correctly finding my .accdb file and does error until the first Cells(row x).Value = statement.
The "OpenRecordset" method not referencing a table, makes me feel like I shouldn't be passing in the name of the table- or using a different method altogether.
I get an error: "Run-time error '3265' Application-defined or object-defined error
Here is my code below:
Sub ImportAccessButton()
Dim row As Integer
Dim dbPassengerCarMileage As Database
Dim rstPassengerCarMileage As Recordset
row = 3
Set dbPassengerCarMileage = OpenDatabase(ThisWorkbook.Path & "\Cars.accdb")
Set rstPassengerCarMileage = dbPassengerCarMileage.OpenRecordset("Amber")
If Not rstPassengerCarMileage.BOF Then
Do Until rstPassengerCarMileage.EOF
Cells(row, 1).Value = rstPassengerCarMileage!MAKE
Cells(row, 2).Value = rstPassengerCarMileage!Model
Cells(row, 3).Value = rstPassengerCarMileage!VOL
Cells(row, 4).Value = rstPassengerCarMileage!HP
Cells(row, 5).Value = rstPassengerCarMileage!MPG
Cells(row, 6).Value = rstPassengerCarMileage!SP
Cells(row, 7).Value = rstPassengerCarMileage!WT
row = row + 1
rstPassengerCarMileage.MoveNext
Loop
End If
'Close database and Cleanup objects
rstPassengerCarMileage.Close
dbPassengerCarMileage.Close
Set rstPassengerCarMileage = Nothing
Set dbPassengerCarMileage = Nothing
End Sub
It uses ADODB. The CopyFromRecordset command speeds up.
Sub ImportAccessButton()
Dim Rs As Object
Dim strConn As String
Dim i As Integer
Dim Ws As Worksheet
Dim strSQL As String
set Ws = ActiveSheet
strConn = "Provider=Microsoft.ACE.OLEDB.12.0;" & _
"Data Source=" & ThisWorkbook.Path & "\Cars.accdb" & ";"
strSQL = "SELECT * FROM Amber"
Set Rs = CreateObject("ADODB.Recordset")
Rs.Open strSQL, strConn
If Not Rs.EOF Then
With Ws
.Range("a2").CurrentRegion.ClearContents
For i = 0 To Rs.Fields.Count - 1
.Cells(2, i + 1).Value = Rs.Fields(i).Name
Next
.Range("a" & 3).CopyFromRecordset Rs
End With
End If
Rs.Close
Set Rs = Nothing
End Sub
I have the following code, where .Fields... is not getting executed. Loop is directly closing the connection without adding records into table.
Code:
Sub insertIntoTable()
Dim moviesConn As ADODB.Connection
Dim moviesData As ADODB.Recordset
Dim moviesField As ADODB.Fields
Dim r As Range
Set moviesConn = New ADODB.Connection
Set moviesData = New ADODB.Recordset
moviesConn.ConnectionString = conStrAccess
moviesConn.Open
On Error GoTo closeConnection
With moviesData
.ActiveConnection = moviesConn
.Source = "tblFilmDetails"
.LockType = adLockOptimistic
.CursorType = adOpenForwardOnly
.Open
On Error GoTo closeRecordset
For Each r In Range("A3", Range("A2").End(xlDown))
.AddNew
.Fields("Title").Value = r.Offset(0, 1).Value
.Fields("Release_Date").Value = r.Offset(0, 2).Value
.Fields("Length").Value = r.Offset(0, 3).Value
.Fields("Genere").Value = r.Offset(0, 4).Value
.Update
Next r
End With
closeRecordset:
moviesData.Close
closeConnection:
moviesConn.Close
End Sub
Please suggest
I was able to get your code to work using this connection string:
"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=C:\Users\best buy\Desktop\test.accdb;Persist Security Info=False;"
We can rule out data mismatch error because it throws a 3219 Runtime Error Operation before closing the connection.
Range("A3", Range("A2").End(xlDown)) should probably be changed Range("A2", Range("A" & Rows.Count).End(xlup)) for two reasons:
It skips row 2
If there is no data beyond row 2 you will add 1048575 empty records (Ask me how I know)
If you have a large dataset you should comment out .Update and use .UpdateBatch after you have added all the records. This will greatly improve performance.
For Each r In Range("A3", Range("A2").End(xlDown))
.AddNew
.Fields("Title").Value = r.Offset(0, 1).Value
.Fields("Release_Date").Value = r.Offset(0, 2).Value
.Fields("Length").Value = r.Offset(0, 3).Value
.Fields("Genere").Value = r.Offset(0, 4).Value
'.Update
Next r
.UpdateBatch
Note: If you have the table open while adding the records then you have to press F5 to refresh the table and view the new data.
How can I foreach the returned rows from ADODB.Resultset? And is it possible to get values by column name (for example if I want to get value from column "name" at row 3 to cell A1)?
Here is my current code without any loops:
Dim oConn As ADODB.Connection
Set oConn = New ADODB.Connection
oConn.Open "Driver={MySQL ODBC 5.2 Unicode Driver};Server=****;Database=****;Uid=****;Pwd=****;"
Dim oRS As ADODB.Recordset
Set oRS = New ADODB.Recordset
oRS.Open "SELECT * FROM report_access", oConn, adOpenStatic
With oRS
Cells(1, 1).CopyFromRecordset oRS
End With
Thanks in advance.
Try this for iteration
while not (ors.EOF Or ors.BOF)
for each x in ors.Fields
'Assign cell content
next
ors.MoveNext
wend
you can move the queried data to an array using the 'GetRows' and get the values from the array as you wish
For example
arr = oRS.GetRows
i = 0
If Not (oRS.EOF And oRS.BOF) Then
oRS.MoveFirst
Do Until oRS.EOF = True
MsgBox arr(0, i)
i = i + 1
oRS.MoveNext
Loop
Else
MsgBox "There are no records in the recordset."
End If
This will Alert the first column values
Note : Here arr(3,2) means 3rd column and 2nd row
Not directly. You can move to row 3 with 3 oRS.MoveNext
while not oRS.EOF And oRS.AbsolutePosition < 2
oRS.MoveNext
wend
[A1] = oRS.Fields("name")
or you can find the index of the "name" field and use oRS.GetRows
Dim nameIndex As Integer
For nameIndex = 0 To oRS.Fields.Count - 1
If oRS.Fields(nameIndex).Name = "name" Then Exit For
Next
arr = oRS.GetRows
[a1] = arr(2, nameIndex)
Update
You can probably use oRS.Move
oRS.Move 2
[A1] = oRS.Fields("name")
My actual code is the following:
Private Sub Worksheet_BeforeDoubleClick(ByVal Target As Range, Cancel As Boolean)
Call extract
End Sub
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
End SubSub extract()
Dim cn As Object
Dim uid1, pass1, srvrnm --pass parm
Set cn = CreateObject("ADODB.Connection")
Dim rs As Object
Set rs = CreateObject("ADODB.Recordset")
Dim cmdSQLData As Object
Set cmdSQLData = CreateObject("ADODB.Command")
uid1 = "SSSS" --user-id
pass1 = "JJJJ" --pass words
srvrnm = "JJJJSDS" --server name
On Error GoTo err1
cn.Open "Driver=Teradata; DBCName="& srvrnm& ";uid="& uid1& ";AUTHENTICATION=ldap;pwd="& pass1& "; Trusted_Connection=True"
Set cmdSQLData.ActiveConnection = cn
On Error Resume Next
query1 = "select tablename,databasename as xx from DBC.TABLES sample 2;"
cmdSQLData.CommandText = query1
cmdSQLData.CommandTimeout = 0
Set rs = cmdSQLData.Execute()
Sheet1.Cells(2, 1) = rs.Fields(0).Value
Set rs = Nothing
err1:
End Sub
My question is how to get database table result in excel rows and column with as many rows as in table and consequent number of columns
You need to loop through your recordset and display the data. So where you have this:
Set rs = cmdSQLData.Execute()
Sheet1.Cells(2, 1) = rs.Fields(0).Value
Set rs = Nothing
You need something like this:
Dim r as Integer 'Row
Dim c as Integer 'Column
For c = 0 To rs.Fields.Count - 1
'This will start on Row 1, Column A (due to + 1) and place all the field headers
Sheet1.Cells(1, c + 1).Value = rs.Fields(c).Name
Next c
r = 3 'We'll start on Row 3
Do While Not rs.EOF
For c = 0 to rs.Fields.Count - 1
'c + 1 means we're starting on Column 1 (A)
'If we wanted to start in Column d, we would use c + 4
Sheet1.Cells(r, c + 1).Value = rs.Fields(c)
Next
r = r +1
rs.MoveNext
Loop
Regarding the comments: To add the field headers, you create an initial loop and simply do not move to the next record. See the modified code above.
The fastest way to copy the entire recordset to an excel sheet should be:
Sheet1.Cells(2, 1).CopyFromRecordset rs
Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Questions asking for code must demonstrate a minimal understanding of the problem being solved. Include attempted solutions, why they didn't work, and the expected results. See also: Stack Overflow question checklist
Closed 9 years ago.
Improve this question
I have 9 sheets that connect to different tables in teradata, each time i have to enter my user name and password to refresh and get new set of data. could someone please advice how do i write a VBA code that could change the connection string for each connection and refresh the data table.I am a begginner in VBA and have no clue in codding in VBA
Thanks
Syam
Here is what I do: I put the following in cells A2:B5
Data Source:
Database:
I put the SQL in cell D2. I use Row 1 for telling me how long the query takes. Then, I add a button anywhere on the page. Then I call the code below. It looks complicated, but the core of the functionality is all in Get_Data_Teradata.
The Get_SQL Function simply reads down column D until it finds a blank row and then returns a big block of text for the SQL. You could replace this with a hardcoded SQL statement.
Pop_Col_Heads puts the column headings from the result in Row 1. Note, that I have discovered a Bug in Excel 2010 on Win 7 where I can only populate columns once or twice per Excel session. If I quit and load Excel again, it works another once or twice.
Copy_Data_From_RDBMS places the ADODB RecordSet into a range in the active sheet. I had to do some tweaks to handle inserts and updates because they don't return any rows.
Sub Get_Data_Teradata()
'Supports Multi Query
Dim cn As ADODB.Connection
Dim sConnect As String
Set cn = New ADODB.Connection
Dim rs As ADODB.Recordset
Set rs = New ADODB.Recordset
Dim cmdSQLData As ADODB.Command
Set cmdSQLData = New ADODB.Command
Dim sQueries() As String
sConnect = "Persist Security Info=True; Session Mode=ANSI; " & _
"Data Source=" & ActiveSheet.Range("B2").Value & ";" & _
"Database=" & ActiveSheet.Range("B3").Value & ";" & _
"User ID=" & ActiveSheet.Range("B4").Value & ";" & _
"Password=" & ActiveSheet.Range("B5").Value & ";"
sQueries = Get_SQL(ActiveSheet.Range("D2:D9999"))
nRow = 1 'initialize to start at the top of the page
For i = 0 To UBound(sQueries) - 1
cn.Open sConnect
Set cmdSQLData.ActiveConnection = cn
cmdSQLData.CommandText = sQueries(i) 'TELL VBA TO LOAD THE QUERY INTO TERADATA
cmdSQLData.CommandType = adCmdText
cmdSQLData.CommandTimeout = 0
Set rs = cmdSQLData.Execute()
Call Pop_Col_Heads(rs, nRow)
nRow = Copy_Data_From_RDBMS(rs, nRow)
cn.Close
Next i
End Sub
Dim a As Long
Dim i As Long
Dim nIndex As Long
Dim sSQL() As String
Function Get_SQL(oRange As Object) As String()
'First figure out how many rows the SQL statement is
a = 0
For Each cCell In oRange
a = a + 1
If cCell.Value = "" Then
a = a - 1
Exit For
End If
Next cCell
'Num rows = a now
'Step through and parse into array
i = 0
nIndex = 0
ReDim Preserve sSQL(1)
For Each cCell In oRange
i = i + 1
If i > a Then
Exit For
ElseIf cCell.Value = "<Multi>" Then
nIndex = nIndex + 1
ReDim Preserve sSQL(nIndex + 1)
Else
sSQL(nIndex) = sSQL(nIndex) & To_Text(cCell.Value) & " "
End If
Next cCell
Get_SQL = sSQL
End Function
Sub Pop_Col_Heads(rs As Object, nRow As Long)
Dim rHeads As Range
Dim fFields As Field
Dim nCol As Integer
nCol = 0
If nRow = 1 Then
ActiveSheet.Range("E1:ZZ1").ClearContents
End If
Set rHeads = ActiveSheet.Range("E1").Offset(nRow - 1, 0)
Do While nCol < rs.Fields.Count
sTemp = rs.Fields(nCol).Name
rHeads.Cells(nRow, nCol + 1).Value = rs.Fields(nCol).Name
ActiveSheet.Calculate
rHeads.Cells(nRow, nCol + 1).Value = sTemp
nCol = nCol + 1
rHeads.WrapText = True
rHeads.VerticalAlignment = xlVAlignTop
Loop
End Sub
Function Copy_Data_From_RDBMS(rs As Object, nRow As Long) As Long
'Supports Multi Query
If nRow = 1 Then
x = Get_Last_Row_Find(ActiveSheet.Range("E1:ZZ64000"))
ActiveSheet.Range("E2:ZZ" & x).ClearContents
End If
On Error Resume Next
rs.MoveFirst
On Error GoTo 0
If Not rs.EOF Then
ActiveSheet.Range("E2").Offset(nRow - 1, 0).CopyFromRecordset rs
x = Get_Last_Row_Find(ActiveSheet.Range("E1:ZZ64000"))
Copy_Data_From_RDBMS = x + 1
ActiveSheet.Range("E2:ZZ" & x).Offset(nRow - 1, 0).WrapText = False
Else 'no results (e.g. insert)
ActiveSheet.Range("E2").Offset(nRow - 1, 0).Value = "<no data returned>"
End If
rs.Close
Set rs = Nothing
End Function