Why can't I do a "with x as (...)" with ADODB and Oracle? - vba

I fail to execute an SQL query with a with clause via ADODB and Oracle.
That is, the following snippet works:
Dim cn As ADODB.connection
Set cn = ....
Dim rs As ADODB.recordSet
Set rs = New ADODB.Recordset
rs.Open "select 'foo' x from dual", cn
Do While Not rs.eof
...
rs.MoveNext
Loop
However, the following doesn't work - it genererats a Run-Time error 3704: Operation is not allowed when the object is closed.
Dim cn As ADODB.connection
Set cn = ....
Dim rs As ADODB.recordSet
Set rs = New ADODB.Recordset
rs.Open "with w as (select 'foo' x from dual) select x from w", cn
Do While Not rs.eof
...
rs.MoveNext
Loop
Obviously, this is a trimmed-down demonstration of the real problem which consists
of a more sophisticated query.
It seems to me that ADODB sort of parses the query before it passes it to the Oracle instance and does not understand the with clause. Anyway, any help on this is highly
appreciated.

Ok, it really seems as though ADODB expects a query statement to actually start with select.
Therefore, a work around for the problem might be to enclose the statement in a select * from ( .... ) like so:
Dim sql As String
sql = "with w as (select 'foo' x from dual) select x from w"
' enclose the statement:
sql = "select * from (" & sql & ")"
rs.Open sql, cn

Above method did not work for me.
Adding ";" prior to WITH keyword resolved the issue.
Dim sql As String
sql = ";with w as (select 'foo' x from dual) select x from w"
rs.Open sql, cn

Related

How to write a query that uses a number as parameter and number type field?

I need to start a query to retrieve data from Access database using VBA which I want to use a variable number as a parameter. Is it possible?
like the:
field name: NMT field type (number)
table name: Orders
and the code is like the following:
Dim Con As New ADODB.Connection
Dim RS As New ADODB.Recordset
Dim X as Integer
X = me.textbox1.value
Con.Open "Provider= Microsoft.ACE.OLEDB.12.0;Data Source=" & U.Database01 & "\DB.accdb;Persist Security Info=False"
Rs.Open "select * from Orders where nmt = '" + X + "'", Con, adOpenDynamic, adLockPessimistic
Whenever I run this query, I get a run-time error '13' type mismatch.
Any suggestions ?
Multiple Issues
Type-mismatch in WHERE clause:
Your query (i.e. the WHERE clause) tries to compare a Number-column from database with a String-value (e.g. WHERE numberField = '123'). This will result in a runtime error Type mismatch (Error 13). See also similar question.
Unsafe to use + to concatenate Strings
When building the query you tried to concatenate the query-template with the number-parameter by a plus-sign. This works only when operating on numbers. See related question
Solution
remove single-quotes: you should compare the Number-column NMT with a number literal (e.g. WHERE nmt = 123)
use & to concatenate strings. This will also convert numbers to strings. Besides I explicitly used CStr function below.
Dim Con As New ADODB.Connection
Dim RS As New ADODB.Recordset
Dim strSQL As String
Dim nmtNumber as Integer ' you named it x before
nmtNumber = me.textbox1.value
strSQL = "SELECT * FROM Orders WHERE nmt = " & CStr(nmtNumber) ' removed single-quotes and used ampersand to concatenate with converted string
Con.Open "Provider= Microsoft.ACE.OLEDB.12.0;Data Source=" & U.Database01 & "\DB.accdb;Persist Security Info=False"
RS.Open strSQL, Con, adOpenDynamic, adLockPessimistic
Further improvement
I already extracted the SQL string (building) into a separate variable strSQL above.
Better would be to use predefined/prepared and parameterized queries:
QueryDef (DAO) where you can set the parameters (type-safe). See this question.
Command (ADODB) where you can set parameters (type-safe). See this question.
See also
What is ‘Run-time error ‘13’: Type mismatch’? And How Do You Fix It?
VBA Type Mismatch Error

Conditional inner join statement (VBA/SQL) to generate multiple values

I'm quite new to VBA/SQL and I'm trying to execute a conditional inner join.
I have two tables with a column in common ("CRM" and "CodeCRM") and I would like to obtain an email adress from table2 ("Desks") when something is triggered (CodeBlocage = 101) in table1 ("Flux") in order to add it to an automatic email.
Dim StrDestinataire As String
Select Case Strtable
Case "Flux", "GAFIJOUR"
Dim cn As ADODB.Connection
Dim rs As ADODB.Recordset
Dim Y As String
Dim sSql As String
Set cn = CurrentProject.Connection
sSql = "Select AddMailCRM from Desks Inner Join Flux on Desks.CODECRM = Flux.CRM WHERE Flux.CODEBLOCAGE = '101'"
Set rs = cn.Execute(sSql)
If Not rs.BOF And Not rs.EOF Then
Y = rs.Fields("AddMailCRM").Value
End If
StrDestinataire = Y
cn.Close
Everything works great except that it should be returning more than one value for the email adress. Any leads?
Thank you
There are three keywords that may cause confusion:
SELECT in sql
SELECT determines the columns in the resulting recordset. If your sql statement is SELECT Name, Number FROM Employees, the SELECT part tells you that the resulting recordset will have two columns named Name and Number.
Select Case in VBA
Select Case is a programming construct for conditionals. You'd use it when you don't want to use a bunch of If..ElseIf..Else statements, but anything you can do with If you can do with Select Case.
Select Case A
Case "Flux"
Execute these VBA statements when the variable A = Flux
Case "Capacitor"
Execute these statements when A = Capacitor
Case Else
Execute these statements when A is neither Flux nor Capacitor
End Select
CASE in sql
The CASE keyword in sql is like Select Case in VBA, but it's used in the field list of a SELECT sql statement (for one).
SELECT Name, CASE WHEN Number = 1 THEN 'One' ELSE 'Two' END MyNum FROM Employees
If you execute this recordset, you get two columns (Name, MyNum). The MyNum field will contains the text One when Number is 1 for that record and the text Two if Number is anything but 1.
Recordsets
You have both Excel and Access tags, so I'm going to assume you're using ADO in either one of those. Your statement
Y = Select email from table2 Inner Join table1 on table2.Crm = table1.Crm WHERE table1.Code = 1
Doesn't do anything - it wouldn't compile. Let's assume you want a variable, Y, to contain the email that would be returned if you executed that sql statement.
Sub GetEmail()
Dim cn As ADODB.Connection
Dim rs As ADODB.Recordset
Dim Y As String
Dim sSql As String
Set cn = New ADODB.Connection
cn.Open "MyConnectionStringGoesHere"
sSql = "Select email from table2 Inner Join table1 on table2.Crm = table1.Crm WHERE table1.Code = 1"
Set rs = cn.Execute(sSql)
If Not rs.BOF And Not rs.EOF Then
Y = rs.Fields("email").Value
End If
End Sub
In this case I have to create a recordset and execute that recordset for a certain connection. Presumably the join and the WHERE clause ensures that it will only return one record. But if it returns more, this example will only use the email from the first record.
Before I grab the Value of the email field, I make sure that the recordset returned at least one record. If it's at the beginning of the file (BOF) and at the end of the file (EOF) at the same time, that means there are no records in the recordset.
Solved.
Dim StrDestinataire As String
Select Case Strtable
Case "Flux", "GAFIJOUR"
Dim cn As ADODB.Connection
Dim rs As ADODB.Recordset
Dim Y As String
Dim sSql As String
Set cn = CurrentProject.Connection
sSql = "Select AddMailCRM from Desks Inner Join Flux on Desks.CODECRM = Flux.CRM WHERE Flux.CODEBLOCAGE = '101'"
Set rs = cn.Execute(sSql)
If Not rs.BOF And Not rs.EOF Then
Y = rs.Fields("AddMailCRM").Value
End If
StrDestinataire = Y
cn.Close

How do I import data from a Teradata table to MS access in an automated way?

I am new to MS Access. I have been trying to import data from a Teradata table to MS Access database. I could establish the connection between the two using VBA. However, I am not being able to write the contents to the access database.
For Excel, we generally use objects like sheets and range to populate the values. What are Access counterparts of these objects?
Given below is the code that I have been using:
Sub TBEN_PR_DSM_SEAS()
Dim cn As ADODB.Connection
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 query As String
cn.Open "DRIVER={Teradata}; DBCNAME=ABC2; Persist Security Info=True; User ID= ******; Password=******; Session Mode=ANSI;"
Set cmdSQLData.ActiveConnection = cn
query = "SELECT * FROM PRODBBYCIADHOCWRK.TBEN_PR_DSM_SEAS;"
cmdSQLData.CommandText = query
cmdSQLData.CommandType = adCmdText
cmdSQLData.CommandTimeout = 0
Set rs = cmdSQLData.Execute()
End Sub
Can anyone please help me out with the rest of the part? I am using Access 2007-2010.
Thanks and regards,
Nirvik
MS Access is an interesting piece of software as it can serve as both a RDMS database and GUI console to a database. By default, it connects to the Jet/ACE SQL Engine (Windows .dll files) which would compare to SQLite another file-level RDMS. However, with MSAccess.exe Office program, this default can be switched or supplemented with any other ODBC/OLEDB compliant database including the server-level RDMS (Oracle, SQL Server, MySQL, Sybase, even Teradata) using linked tables. And in connecting to external backends it would compare to MySQL's phpmyadmin, SQL Server's Management Studio, PostgreSQL's pgAdmin, and other consoles.
Therefore, consider creating a linked table to Teradata using DoCmd.TransferDatabase where changes reflect on both ends without constant import and export of table data:
DoCmd.TransferDatabase acLink, "ODBC Database", _
"ODBC;DRIVER={Teradata}; DBCNAME=ABC2; Persist Security Info=True; User ID= ******;" _
& "Password=******; Session Mode=ANSI;", acTable, "TBEN_PR_DSM_SEAS", "NewAccessTable"
And for a static, local copy (which would add redundancy to your application needs) you can run an append or make-table query in Query Window or VBA's DoCmd.RunSQL or CurrentDb.Execute to a local Access table using above linked table.
INSERT INTO NewAccessTable SELECT * FROM [TBEN_PR_DSM_SEAS]
SELECT * INTO NewAccessTable FROM [TBEN_PR_DSM_SEAS]
Insert the data in the recordset into the Access table, using new recordset, Connection and Command objects.
Sub TBEN_PR_DSM_SEAS()
Dim cn As ADODB.Connection
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 query As String
cn.Open "DRIVER={Teradata}; DBCNAME=ABC2; Persist Security Info=True; User ID= ******; Password=******; Session Mode=ANSI;"
Set cmdSQLData.ActiveConnection = cn
query = "SELECT * FROM PRODBBYCIADHOCWRK.TBEN_PR_DSM_SEAS;"
cmdSQLData.CommandText = query
cmdSQLData.CommandType = adCmdText
cmdSQLData.CommandTimeout = 0
Set rs = cmdSQLData.Execute()
'Up to here is your code.
'Asuming you have a table in Access with identical number of fields, and field names:
dim dRst as dao.Recordset, fld as Variant
set dRst = CurrentDb.("AccessTable")
Do While Not Rs.EOF
dRst.AddNew
For Each fld in dRst.Fields
dRst.Fields(fld.Name) = rs.Fields(fld.Name)
Next
'Update an entire record:
dRst.Update
Rs.MoveNext: Loop
End Sub
Tables are Sheets and Queries select Ranges.
Fields are Columns and Records are Rows.
'Loop through Records
Do Until rs.EOF
'rs.Fields(0) is the first field returned from the Teradata query
'executed in your initial question.
Debug.Print rs.Fields(0)
rs.MoveNext
Loop
'Append to Table
'Have to create Table1 in Access database.
'Table1
'Field Name: Column1
'Data Type: Text
If rs.BOF = False Then rs.MoveFirst 'BOF = Beginning of file
DoCmd.SetWarnings False
Do Until rs.EOF 'EOF = End of file
DoCmd.RunSQL ("INSERT INTO Table1 (Column1) SELECT '" & rs.Fields(0) & "'")
rs.MoveNext
Loop
DoCmd.SetWarnings True
cn.Close
Set cn = Nothing

Reading from hidden Oracle Tables with VBA ADODB

I'm trying to read from different Tables on a Oracle Database using VBA and Excel.
Usually when read the tables I run something like:
Dim rs As Object
Set rs = CreateObject("ADODB.Recordset")
Dim query As String: query = "SELECT * FROM OBJ_NAME"
rs.Open query, con
However, this does not work for all the tables. Using SQL Developer i usually run at the beginning:
exec session#.open_session();
After which I can read any table. Is there a way to run this command with an ADODB.Recordset in the beginning too? Just replacing the SQL Query with the command did not work.
Or is there different way to read this 'hidden' Tables?
Try something like that:
Dim rs As Object
Set rs = CreateObject("ADODB.Recordset")
Dim query As String: query = "begin session#.open_session; end;"
With rs
.ActiveConnection = con
.Open query, con
End With
Set connectToDB = con
This should work using VBA. Let me if it worked :)

Data type mismatch on SQL Query in VBA

I am trying to do an SQL query in VBA to retun a specific case number. Whenever I execute the query, it returns an error of "Data Type Mismatch in Criteria Expression". I am passing the query an integer to use to query an autonumber primary key.
Dim c As ADODB.Connection
Dim r As ADODB.Recordset
Dim strSQL As String, strManager As String
Set c = New ADODB.Connection
c.Open "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=Commit Tracker.accdb; Persist Security Info=False;"
strSQL = "SELECT * FROM CommitTrk WHERE CASE_ID_NBR = '" & CInt(frmCommitViewer.lstCases.Value) & "'"
Set r = c.Execute(strSQL)
Of course the debug hilights the execute command. Any help would be appreciated. Am I passing the wrong datatype to match the autonumber? If so, what datatype should I be using? Thanks!
if CASE_ID_NBR has numeric type, you should use it without quotes:
strSQL = "SELECT * FROM CommitTrk WHERE CASE_ID_NBR = " & CInt(frmCommitViewer.lstCases.Value)
you may also want to read this: Global Variables in SQL statement