ms-access save query result in a string - sql

I have a query saved in the queries section. I am running the query from VBA. Is it possible to save the results of this query to a string?

An ADO Recordset has a GetString method which might be useful to you.
I have a query named qryListTables which looks like this:
SELECT m.Name AS tbl_name
FROM MSysObjects AS m
WHERE
(((m.Name) Not Like "msys%"
And (m.Name) Not Like "~%")
AND ((m.Type)=1))
ORDER BY m.Name;
Notice that query uses % instead of * as the wildcard character. The reason for that choice is that ADO requires ANSI wild card characters (% and _ instead of * and ?).
I can use the following function to spit out a string containing the quoted names of regular tables in my database, separated by semicolons, by calling it like this:
? DemoGetString("qryListTables", True)
Public Function DemoGetString(ByVal pQueryName As String, _
Optional ByVal AddQuotes As Boolean = False) As Variant
'* early binding requires a reference to Microsoft ActiveX
'* Data Objects Library
'Dim rs As ADODB.Recordset
'Set rs = New ADODB.Recordset
'* use late binding; no referenced needed
Dim rs As Object
Set rs = CreateObject("ADODB.Recordset")
Dim varOut As Variant
rs.Open pQueryName, CurrentProject.Connection
If AddQuotes Then
varOut = """" & rs.GetString(2, , , """;""") '2 = adClipString
' strip off last quote
If Len(varOut & vbNullString) > 0 Then
varOut = Left(varOut, Len(varOut) - 1)
End If
Else
varOut = rs.GetString(2, , , ";") '2 = adClipString
End If
rs.Close
Set rs = Nothing
DemoGetString = varOut
End Function

Ok.. taking a complete shot in the dark here...
The query you are running is literally a query... think of it as its OWN table... it can be referenced as you would any other table, and can be queried against.
If you are trying to return a single string item based on a single criteria your best bet is a Dlookup:
Lookup = Nz(DLookup(string Field, string Table, string Criteria), "")
If your looking for a group of records:
dim tsSQL as string
stSQL = "SELECT * FROM table WHERE field=criteria"
dim toRecordset as new ADODB.Recordset
toRecordset.open stSQL, CurrentProject.AccessConnection, int Keyset, int Lock
Then you can directly access the fields by:
If toRecordset.RecordCount > 0 then
String = toRecordset!FieldName
End If
W/o more information... that about it...
Also it works in the other direction as well..
You can do:
toRecordset.AddNew
toRecordset!Field = Value
toRecordset.Update
I hope somewhere in there is an answer for you.
To get the entire query you could change up the select statement from example one to "SELECT * FROM query name" and that should pull the whole thing in.

Related

VBA dictionary as a property of a class

I have this simplified class named clsWarehouseSum.
Option Compare Database
Option Explicit
Private wh_units As Scripting.Dictionary
Public Function availableUnits(warehouse As String) As Long
'Debug.Print wh_units(warehouse)
If wh_units Is Nothing Then Set wh_units = New Scripting.Dictionary
If Not wh_units.Exists(warehouse) Then
Dim SQL As String
Dim RS As DAO.Recordset
SQL = "SELECT sum(units) as tot_units " _
& "FROM warehouse " _
& "WHERE warehouse = '" & warehouse & "' "
Set RS = CurrentDb.OpenRecordset(SQL)
wh_units.Add (warehouse), RS("tot_units")
End If
availableUnits = wh_units(warehouse)
End Function
I try to use it like this:
Sub test()
Dim wh As New clsWarehouseSum
Debug.Print wh.availableUnits("Cohasset")
Debug.Print wh.availableUnits("Cohasset")
End Sub
While the first Debug.Print prints what's expected, the second one gives me an error:
Run time error 3420, Object Invalid or no longer set. When I step through the code, it correctly evaluates both if statements as false. Yet, the last line of the function gives me the error mentioned above. What am I doing wrong?
Why?
Add Debug.Print TypeName(wh_units(warehouse)) before the availableUnits = wh_units(warehouse) line and if it prints anything else than Long to the Immediate window then you might want to cast to Long using CLng while you also have some error handler in place.
Or, you might want to make sure that the line wh_units.Add (warehouse), RS("tot_units") is adding a Long to your dictionary so you should check the type before you add.
As a general rule, when you return a specific data type from a dictionary or collection, you should always have checks in place either when you add the data to the dict/coll or when you return it so that you avoid type incompatibility and runtime errors.

Database field properties

Follow Vba routine:
Dim CampoRS As string
Dim Requerido As Boolean
Dim Zero As Bollean
Dim rs As DAO.Recordset
With frmCurriculum
Set rs = dbCRM.OpenRecordset("SELECT * FROM tblCurriculum", dbOpenDynaset, dbSeeChanges, dbPessimistic)
rs.MoveFirst
For n = 0 To .Controls.Count - 1
CampoRS = .Controls.item(n).Tag
Requerido = rs.Fields(CampoRS).Required
Zero = rs.Fields(CampoRS).AllowZeroLenght
...
Using DAO, this routine get the properties "Required" and "AllowZeroLenght" of a field in a recordset.
I need get the same properties but using ADO
If you look here https://flylib.com/books/en/3.9.1.29/1/ you can see that the Field.Attributes property is a bitmask, so you can check for example
(rs.Fields("Name").Attributes and 64) = 64
(or use the adFldMayBeNull ADO constant) to check if a field is nullable, but I don't see the equivalent for "allow zero length" there.
This might be a case where you need to DAO/ADOX if you want both of those properties though.

Access VBA Run-time error 3078, or Type Mismatch on DCount function

Objective: I'm building VBA code to filter through an address table SunstarAccountsInWebir_SarahTest. I want to loop through first and see if the address is "valid".
If it is not valid – export to different table.
If it is valid – it enters another nested if/then within "valid" address rows:
If their ID, external_nmad_id matches the ID of the second table 1042s_FinalOutput_7, I want to update one of the columns in the second table box13c_Address.
If it doesn't match an ID of the second table – it will be exported to a different table.
My problem is when I run my code it is returning
Run-Time error 3078: cannot find table or query
(it's breaking at the line where I compare the value of the cell (as string) against the DCount of table 2). If I remove the quotes around it I get a different error:
Type mismatch against the DCount
I feel like I'm missing something simple but can't tell what. How can I get my code to match a string value called in !external_nmad_id against the rest of the table called in my string? DCount("[ID]", StrSQL1)
Public Sub EditFinalOutput2()
'set variables
Dim i As Long
Dim qs As DAO.Recordset
Dim ss As DAO.Recordset
Dim StrSQL1 As DAO.Recordset
Dim IRSfileFormatKey As String
Dim external_nmad_id As String
Dim nmad_address_1 As String
Dim nmad_address_2 As String
Dim nmad_address_3 As String
Dim mytestwrite As String
'open reference set
Set db = CurrentDb
Set qs = db.OpenRecordset("SunstarAccountsInWebir_SarahTest")
'Set ss = db.OpenRecordset("1042s_FinalOutput_7")
'Set StrSQL1 = db.OpenRecordset("SELECT RIGHT(IRSfileFormatKey, 10) As ID
'FROM 1042s_FinalOutput_7;")
With qs.Fields
intCount = qs.RecordCount - 1
For i = 0 To intCount
If (IsNull(!nmad_address_1) Or (!nmad_address_1 = !nmad_city) Or (!nmad_address_1 = !Webir_Country) And IsNull(!nmad_address_2) Or (!nmad_address_2 = !nmad_city) Or (!nmad_address_2 = !Webir_Country) And IsNull(!nmad_address_3) Or (!nmad_address_3 = !nmad_city) Or (!nmad_address_3 = !Webir_Country)) Then
DoCmd.RunSQL "INSERT INTO Addresses_ToBeReviewed SELECT SunstarAccountsInWebir_SarahTest.* FROM SunstarAccountsInWebir_SarahTest WHERE (((SunstarAccountsInWebir_SarahTest.external_nmad_id)='" & qs!external_nmad_id & "'));"
Else:
Set ss = db.OpenRecordset("1042s_FinalOutput_7")
Set StrSQL1 = db.OpenRecordset("SELECT RIGHT(IRSfileFormatKey, 10) As ID FROM 1042s_FinalOutput_7;")
If !external_nmad_id = DCount("[ID]", StrSQL1) Then
ss.Edit
ss.Fields("box13c_Address") = qs.Fields("nmad_address_1") & qs.Fields("nmad_address_2") & qs.Fields("nmad_address_3")
ss.Update
Else: DoCmd.SetWarnings False
DoCmd.RunSQL "INSERT INTO Addresses_NotUsed SELECT SunstarAccountsInWebir_SarahTest.* FROM SunstarAccountsInWebir_SarahTest WHERE (((SunstarAccountsInWebir_SarahTest.external_nmad_id)='" & qs!external_nmad_id & "'));"
DoCmd.SetWarnings True
End If
End If
qs.MoveNext
Next i
End With
'close reference set
qs.Close
Set qs = Nothing
ss.Close
Set ss = Nothing
End Sub
The issue is that the DCount function cannot operate directly against a Recordset.
You are declaring StrSQL1 as a RecordSet object and setting it to a RecordSet based on your Select statement.
Set StrSQL1 = db.OpenRecordset("SELECT RIGHT(IRSfileFormatKey, 10) As ID FROM 1042s_FinalOutput_7;")
You are then trying to pass this RecordSet to the DCount function which cannot accept a RecordSet object as the Domain parameter. As you can see in MSDN the DCount function requires a String parameter in the second position to define the "query" that you wish to "Count". Hence the 3078 error. When you remove the quotes around [ID] in your DCount line, you get Type Mismatch as a compile error because [ID] is not a String or String variable.
After you resolve that, you might want to reconsider your If statement. You haven't provided a sample of what kind of value !external_nmad_id will contain, other than the fact that it is a String value. The DCount function is going to return the number of rows found in the Domain (query) that you told it to count, so it appears you will be comparing a string (which may possibly contain alpha characters) to a number. Access will implicitly convert the DCount numeric result to a String for the sake of the comparison, but if your !external_nmad_id String is truly 10 characters or contains alpha characters, they will never match.
You cannot use a VBA recordset inside a domain aggregate like DCount as a string literal is required for table/query name argument. Simply save your query and then reference it by name in DCount.
SQL (save as query)
SELECT RIGHT(IRSfileFormatKey, 10) As ID FROM 1042s_FinalOutput_7;
VBA
If !external_nmad_id = DCount("[ID]", "mySavedQuery") Then
...
End If

Cyrillic letter displaying as question marks in VBA from Oracle

We've got a problem displaying Cyrillic letters from Clob field (Oracle) on Excel spreadsheet using ADODB.RecordSet (MS ActiveX Data Objects 2.8 Library).
As an example, I created a table with one Clob field. I inserted just one row with Cyrillic letters and am trying to show the value on Excel spreadsheet. However, the text shows as ???? both in Msgbox and in the cell. This happens only while getting values from Clob fields. It works fine if we query them from varchar. I've tried on 11.2.0.2.0 and 12.2.0.1.0. The behavior is the same. What could we do in order to fix this?
NLS_CHARACTERSET in db is AL32UTF8.
The VBA code is as follows:
Private Sub UnloadReportBtn_Click()
Dim RecordSet As ADODB.RecordSet
Set RecordSet = getTestClob
While RecordSet.EOF = False
MsgBox RecordSet.Fields("TEST1")
Cells(7, 7) = RecordSet.Fields("TEST1")
RecordSet.MoveNext
Wend
End Sub
Public Function getTestClob()
Dim Query As String
Query = "SELECT TEST1 FROM TEST_CLOB"
Set getTestClob = getRecordSet(Query)
End Function
Public Function getRecordSet(Query As String) As ADODB.RecordSet
Dim SQLCommand As ADODB.Command
Dim RecordSet As ADODB.RecordSet
Set SQLCommand = New ADODB.Command
Set SQLCommand.ActiveConnection = Connection
SQLCommand.CommandText = Query
SQLCommand.CommandType = adCmdText
SQLCommand.CommandTimeout = 0
Set getRecordSet = SQLCommand.execute
End Function
Set your NLS_LANG value to a character set which supports Cyrillic characters, e.g. CL8MSWIN1251 or AL32UTF8.
You can do this by Environment variable, for example
SET NLS_LANG=AMERICAN_RUSSIA.CL8MSWIN1251
or in your Registry at HKLM\SOFTWARE\Wow6432Node\ORACLE\KEY_%ORACLE_HOME_NAME%\NLS_LANG (for 32 bit), resp. HKLM\SOFTWARE\ORACLE\KEY_%ORACLE_HOME_NAME%\NLS_LANG for 64-bit.
See also OdbcConnection returning Chinese Characters as "?" for more details.

SQL.REQUEST Excel function Alternative? Create one with VBA?

I am looking to execute SQL SELECT statement inside a single cell in Excel, using other cells as inputs to the SELECT statement. After some searching, I found that the sql.request function would have done exactly what I'm looking for. However, that function was deprecated in after 2002, and I'm running Excel 2007 and 2010 here at work. Citation
I have tried to create a Macro / VBA script that does the same thing, but haven't been able to get very far with it. I do all my programming in LabVIEW, Mathematica, and SQL; I have no idea what's going on in VBA. This is what I've managed to come up with:
Sub Test2()
' Declare the QueryTable object. I'm not actually sure why this line is here...
Dim qt As QueryTable
' Set up the SQL Statement
sqlstring = "SELECT `Substrate ID` FROM temp_table WHERE `id`=" & Range("A1").Value
' Set up the connection string, reference an ODBC connection
connstring = "ODBC;DSN=OWT_x64;"
' Now implement the connection, run the query, and add
' the results to the spreadsheet
With ActiveSheet.QueryTables.Add(Connection:=connstring, Destination:=Range("A22"), Sql:=sqlstring)
.Refresh BackgroundQuery:=False
End With
End Sub
There are three primary issues with the above code:
This code returns the column ID ("Substrate ID") in cell A22, and the result of the SQL query in cell A23. I only want the result, and I only want it in cell A22. All queries are forced to return only 1 row and 1 column.
I don't know how to make it so that the output cell, A22, is whatever cell is active when the script is run. Also, the input cell, A1, should be the cell directly to the left (column-1) of the active cell.
I don't know how to turn this into an Excel function
=sql.request(connection_string,[output_ref],[driver_prompt],[query_text],[col_names_logical])
which is my final goal. This way, I can give this code to others at my company and they can easily use it.
The connection is a ODBC connection to a MySQL 5.6 database. The query is pretty simple, and along the lines of:
SELECT column FROM table WHERE id=excel_cell_value
as you can see from the VBA code that I have.
Currently, I run a query in a different Excel worksheet that returns all rows of the "id" and "Substrate ID" columns and then run VLOOKUP to find the item of interest. This is starting to become an issue, as our database size is growing quite fast.
So, I ask:
How can I get rid of the column ID in the result?
How can I turn this into a custom excel function? I've looked at Office.com and it doesn't seem too difficult, but I need a working script first.
-OR- Has anyone already made a custom function that they're willing to share?
Thanks!
EDIT: Managed to get something working thanks to Tim's link.
Function SQLQuery(sqlString As String, connString As String, Optional TimeOut As Integer) As String
SQLQuery = Error 'Assume an error happened
Dim conn As ADODB.Connection
Dim record As ADODB.Recordset
Set conn = New ADODB.Connection
conn.ConnectionString = connString
conn.Open
Set record = New ADODB.Recordset
If TimeOut > 0 Then
conn.CommandTimeout = TimeOut
End If
record.Open sqlString, conn
Dim cols As Long
Dim i As Long
cols = record.Fields.Count 'Count how many columns were returned
If Not record.EOF Then 'Put results into comma-delimited string
record.MoveFirst
s = ""
If Not record.EOF Then
For i = 0 To cols - 1
s = s & IIf(i > 0, ",", "") & record(i)
Next i
End If
End If
SQLQuery = s
End Function
However, it's quite slow. Any ideas on how to speed it up?
Here's a quick test of caching the connection. On a test worksheet with 100 lookups it reduced calculation time from about 18 sec to about 0.5 sec
Remember though it will keep a connection open until you close Excel (or the VB environment gets reset).
If you want to test the difference in your environment, you can comment out the marked line (don't forget to also press the "stop" button in the VBE to clear the static variables).
Function SQLQuery(sqlString As String, connString As String, _
Optional TimeOut As Integer) As String
Static cs As String
Static conn As ADODB.Connection
SQLQuery = Error 'Assume an error happened
Dim s
If conn Is Nothing Or connString <> cs Then
Set conn = New ADODB.Connection
conn.ConnectionString = connString
conn.Open
If TimeOut > 0 Then conn.CommandTimeout = TimeOut
cs = connString '###comment this out to disable caching effect
End If
Dim record As New ADODB.Recordset
record.Open sqlString, conn
Dim cols As Long
Dim i As Long
cols = record.Fields.Count 'Count how many columns were returned
If Not record.EOF Then 'Put results into comma-delimited string
record.MoveFirst
s = ""
If Not record.EOF Then
For i = 0 To cols - 1
s = s & IIf(i > 0, ",", "") & record(i)
Next i
End If
End If
SQLQuery = s
End Function