Suggestions on How to execute SQL script using VBA in Oracle Database - excel

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.

Related

Add records from Excel vba to Ms Access adding blanks instead

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

Microsoft Access Export to Excel

Is it possible to export from microsoft access into microsoft excel, but have the columns on the ledt and the rows going down? I know the default export has it the other way arround.
Data is being being provided to a 3-rd party but they want in in the above format
So like:
Column1 | Row1 | Row2
Column2 | Row1 | Row2
Column3 | Row1 | Row2
Either open the table as a recordset and loop this multiple time filling a temporary table; then export this.
Or export the table as is to Excel, then call Transpose to "rotate" the filled range:
TRANSPOSE function
If you really have to do this in Access rather than looking at using TRANSPOSE in Excel, you will probably want to use some VBA to loop the fields in a recordset. Something like this should give you an idea (you will need to set a reference to the Excel object library in order to use early binding):
Sub sExportExcelTransposed()
On Error GoTo E_Handle
Dim db As DAO.Database
Dim rsData As DAO.Recordset
Dim objXL As New Excel.Application
Dim objXLBook As Excel.Workbook
Dim objXLSheet As Excel.Worksheet
Dim strXLFile As String
Dim lngRow As Long
Dim lngCol As Long
Set db = CurrentDb
Set rsData = db.OpenRecordset("SELECT * FROM dbo_svr_Country ORDER BY CountryName ASC;", dbOpenDynaset, dbSeeChanges)
If Not (rsData.BOF And rsData.EOF) Then
strXLFile = "J:\downloads\transpose.xlsx"
If Len(Dir(strXLFile)) > 0 Then Kill strXLFile
Set objXLBook = objXL.Workbooks.Add
Set objXLSheet = objXLBook.Worksheets(1)
lngCol = 1
For lngRow = 1 To rsData.Fields.Count
objXLSheet.Cells(lngRow, lngCol) = rsData.Fields(lngRow - 1).name
Next lngRow
lngCol = 2
Do
For lngRow = 1 To rsData.Fields.Count
objXLSheet.Cells(lngRow, lngCol) = rsData.Fields(lngRow - 1).Value
Next lngRow
lngCol = lngCol + 1
rsData.MoveNext
Loop Until rsData.EOF
objXLBook.SaveAs strXLFile
End If
sExit:
On Error Resume Next
rsData.Close
Set rsData = Nothing
Set db = Nothing
Set objXLSheet = Nothing
objXLBook.Close
Set objXLBook = Nothing
objXL.Quit
Set objXL = Nothing
Exit Sub
E_Handle:
MsgBox Err.Description & vbCrLf & vbCrLf & "sExportExcelTransposed", vbOKOnly + vbCritical, "Error: " & Err.Number
Resume sExit
End Sub
Regards,

Copy/paste cells based on header value

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

Check if multiple records of a worksheet exist in a SQL table using Excel Vba

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?

copyfromrecordset returns blank column

I am creating an ADODB connection to a Sybase database, executing an SQL statement into a recordset, and then using the CopyFromRecordset method to paste the contents of the recordset to a range. This has been working fine but I recently moved PC's at work and now one of the columns is returning nothing.
When I run the same SQL in SQuirreL the column is not blank.
If I pause the VBA and try to look at one of the values in the column / field in question (ie ?rst.fields(1).value in the immediate Window) I get the following error message:
Run-time error '-2147467259 (80004005)': Unspecified error.
In the Squirrel results Metadata tab the column in question is described as:
ColumnIndex 2
getColumnName CommentText
getColumnTypeName text
getPrecision 2147483647
getScale 0
isNullable 0
getTableName xxxxxxx
getSchemaName
getCatalogName
getColumnClassName java.sql.Clob
getColumnDisplaySize 2147483647
getColumnLabel CommentText
getColumnType 2005
isAutoIncrement FALSE
isCaseSensitive FALSE
isCurrency FALSE
isDefinitelyWritable FALSE
isReadOnly FALSE
isSearchable FALSE
isSigned FALSE
isWritable TRUE
The code in question is below, but, as stated the code does not seem to be the problem as it has worked previously - any ideas?
Sub ImportComments()
Dim wsData As Worksheet
Dim rng As Range
Dim cn As ADODB.Connection
Dim rst As ADODB.Recordset
Dim x As Long
Dim rngSQL As Range
Dim cell As Range
Dim sSQL As String
Dim sProvider As String
Dim sDS As String
Dim sDataSource As String
Dim sUser As String
Dim sCatalog As String
Dim sPassword As String
Dim rngDS As Range
Dim rngThisDS As Range
Dim sConnect As String
Dim sInstance As String
Dim fSuccess As Boolean
Dim sError As String
On Error GoTo ProcExit
'delete previous comments if they exist
If SheetExists("Comments_Data_Import", ThisWorkbook) = True Then
Application.DisplayAlerts = False
ThisWorkbook.Sheets("Comments_Data_Import").Delete
Application.DisplayAlerts = True
End If
'create comments sheet
Set wsData = ThisWorkbook.Worksheets.Add(After:=ThisWorkbook.Sheets("EWI_Data_Import"))
wsData.Name = "Comments_Data_Import"
'build sql string
Set rngSQL = Range(ThisWorkbook.Sheets("SQL").Range("A2"), _
ThisWorkbook.Sheets("SQL").Range("A2").End(xlDown))
For Each cell In rngSQL
sSQL = sSQL & cell.Value & " "
Next cell
'define login components
Set rngDS = ThisWorkbook.Worksheets("Login").Range("rngInstance").CurrentRegion
Set rngDS = rngDS.Offset(1, 0).Resize(rngDS.Rows.Count - 1)
sProvider = "Provider=ASEOLEDB.1;"
sUser = "User ID=" & ThisWorkbook.Worksheets("Login").Range("rngUsername").Value & ";"
sPassword = "Password=" & ThisWorkbook.Worksheets("Login").Range("rngPassword").Value
'try to log in to each instance exiting when succesful
Set cn = New ADODB.Connection
cn.CommandTimeout = 600
'turn off error hadling to allow for connection errors On Error Resume Next
For Each rngThisDS In rngDS.Rows
'complete connect string
Err = 0
sInstance = rngThisDS.Cells(1, 1)
sDS = "Data Source=" & rngThisDS.Cells(1, 2) & ";"
sCatalog = "Initial Catalog=" & rngThisDS.Cells(1, 3) & ";"
sConnect = sProvider & sDS & sUser & sCatalog & sPassword
'attempt to open
cn.Open sConnect
'If successful Then
If Err = 0 Then
'flag success
fSuccess = True
'execute SQL
On Error GoTo ProcError
Set rst = cn.Execute(sSQL)
'copy data into comments sheet
wsData.Range("A2").CopyFromRecordset rst
'Put in the headers
Set rng = wsData.Range("A1")
For x = 1 To rst.Fields.Count
rng.Offset(0, x - 1).Value = rst.Fields(x - 1).Name
Next x
FormatComments
Exit For
End If
Next rngThisDS
If fSuccess = False Then
MsgBox ("Unable to connect to Insight")
Else
MsgBox "Connected to and exported data from " & sInstance
End If
ProcExit:
Set wsData = Nothing
Set rng = Nothing
Set cn = Nothing
Set rst = Nothing
Set rngSQL = Nothing
Set cell = Nothing
Set rngDS = Nothing
Set rngThisDS = Nothing
Exit Sub
ProcError:
MsgBox "Error: " & Err.Description
Resume ProcExit
End Sub
According to the CopyFromRecordset() MSDN:
When this method copies the recordset to the worksheet, the results
will be truncated if you do not specify a range that is large enough
to hold the contents of the recordset.
Consider specifying the range with MoveFirst command reset:
' Copy data into comments sheet
rst.MoveLast
rst.MoveFirst
wsData.Range("A2:Z500").CopyFromRecordset rst
Or entire worksheet (starting at A1, of course inserting row for column headers)
wsData.Cells.CopyFromRecordset rst
But even then, CopyFromRecordset() is sensitive to data and cursory types even memory (since you pull all data and dump at once), so consider altogether replacing the method and iterate through records for the rows. Even other languages (PHP, Python, Java, etc.) run queries this way, opening cursor and iterating through resultset.
' Put in the headers
Set rng = wsData.Range("A1")
For x = 1 To rst.Fields.Count
rng.Offset(0, x - 1).Value = rst.Fields(x - 1).Name
Next x
' Put in rows
Dim col As Integer, row As Integer
rst.MoveLast
rst.MoveFirst
Set rng = wsData.Range("A2")
row = 0
Do While Not rst.EOF
For col = 0 To rst.Fields.Count - 1
rng.Offset(row, col).Value = rst(col)
Next col
row = row + 1
rst.MoveNext
Loop

Resources