I need to query an XLS "database" from within an Excel Workbook via ADO.
I am using the following:
...code
objRecordset.Open "SELECT * FROM [MY_TABLE$] WHERE Code_ID = " & the_ID & ", objConnection, adOpenStatic, adLockOptimistic, adCmdText
...code
It runs well if I am only searching for an Id (the_ID), eg 1234
But what I need is to search for various the_ID's at the same time....
So for example any matches of ID's 1234, 1225, 6225, 5656 should return on the query.
So more or less an array of Id's.
Any help is appreciated...
You can join the array of ids using Join then use IN in the sql, e.g.
Dim ids(3) As String
ids(0) = "1234"
ids(1) = "1225"
ids(2) = "6225"
Dim sql As String
sql = "SELECT * FROM [MY_TABLE$] WHERE Code_ID IN (" & Join(ids, ",") & ")"
If you are getting your ids from a range then this answer will be of interest to you.
Note
Since you are only querying your own spreadsheet I have assumed security may not be a major concern, however I would normally recommend using parameterised queries, this would either require a known number of ids, or you would have to generate the sql on the fly, something like:
WHERE Code_id IN (#Param1, #Param2, #Param3, #Param4)
It should not be too hard to make your sql like this then add your ids as parameters to the recordset. I haven't used VBA in a long time though, so I can't quite recall the right way to add the parameters (or if it is even possible). If I remember I will update the answer.
Related
How should I write the name of the recordset correctly? I have a type mismatch error on & rsg & at "sq3=..." line. Thanks in advance.
Dim rsG As DAO.Recordset
Dim sq2, sq3 As String
sq2 = "SELECT * from GeneralTable "
sq2 = sq2 & "where gsede='LION' and (gExclu is null) and (gAda is null) "
sq2 = sq2 & "order by gnomb,gnif;"
Set rsG = CurrentDb.OpenRecordset(sq2)
sq3 = "UPDATE " & rsG & " SET gsede ='AA' WHERE gsede='LION'"
DoCmd.RunSQL (sq3)
You are mixing up totally different ways to update data - UPDATE SQL and VBA recordset.
If you want to update a recordset row by row, you do something like
Do While Not rsG.EOF
If rsG!foo = "bar" Then
rsG.Edit
rsG!gsede = "AA"
rsG.Update
End If
rsG.MoveNext
Loop
Do the update using a single query:
update generaltable
set gsede = 'AA'
where gsede ='LION' and (gExclu is null) and (gAda is null);
If you do the update off of rsG, then you'll likely do a separate update for each row, and that is inefficient.
You can't mix and match SQL and recordset objects. You can either build a full update statement that includes the logic of the first select (as other answer suggests), or you can open a dynamic recordset and loop through the records programmatically to make the updates.
That being said, looping through large recordsets programmatically to make updates is usually better handled by a bulk SQL statement, unless it is essential to consider each row individually inside the code - which in your basic example would not be the case.
MS has a good article on the DAO.Recordset object. There is a dynamic-type Recordset example about halfway down.
I have asked a similar question recently. However, I don't think I knew the extent of what I was wanting to accomplish with my VBA at that time. I am using Access 2010 and creating an on_click command within a form.
So my intention is to create a query, and the approach I was going to take was as follows:
varSQL2 = "SELECT * FROM Inventory WHERE Part_ID=" & rs!Part_ID & ";"
Set rs2 = db.OpenRecordset(varSQL2, dbOpenDynaset)
varStock_Level = rs2!Stock_Level +rs!Quantity
the rs!quantity and rs!Part_ID are from another query earlier in my code. stock_level and part_ID are fields in the Inventory table.
I now need to be able to create a query to count how many records came back as a result of varSQL2. so I can do something along the lines of:
varSQL2 = "SELECT * FROM Inventory WHERE Part_ID=" & rs!Part_ID & ";"
Set rs2 = db.OpenRecordset(varSQL2, dbOpenDynaset)
varStock_Level = rs2!Stock_Level +rs!Quantity
if count >1 then
....[code]....
end if
I don't really know where to start with this, slightly confused my self with subqueries. I believe I need 2 queries not one. Any help is appreciated.
There are several ways:
Create a query with COUNT function (similar as you did in refered question)
Use your query, use the rs2.movelast and read count out with rs2.RecordCount
Use domain count
Dim lngRows As Long
lngRows = DCount("*", "Inventory", "Part_ID='" & rs!Part_ID & "'")
From memory:
rs.movelast
debug.print rs.recordCount
You must always Movelast before using RecordCount. I your table/query is big, it can be time consuming.
Could any expert tell me why I always get this error "Expected:End of Statement" when I run the following code in Access VBA :
FNSQL = DoCmd.RunSQL _
"SELECT First_Name FROM tblPatient_Info WHERE Patient_ID = '" & 1967 & "';"
Thank you so much in advance!
Your code is wrong in a couple different ways.
You don't tell us what FNSQL is, so one can only imagine.
Mistake #1
Mentioned by #simoco, DoCmd.RunSQL is used in the following scenarios:
Here's the API: DoCmd.RunSQL SQLStatement UseTransaction
SQLStatement : A string expression that's a valid SQL statement for an action query or a data-definition query. It uses an INSERT INTO, DELETE, SELECT...INTO, UPDATE, CREATE TABLE, ALTER TABLE, DROP TABLE, CREATE INDEX, or DROP INDEX statement.
Include an IN clause if you want to access another database.
Mistake #2
FNSQL
I'm not sure what you are aiming to do with the results of the query, but this is for your general knowledge because if you didn't know the RunSQL syntax you may be unfamiliar with Recordsets.
You can assign your stored query's results to a Recordset and do with it as you please, much like a table.
Dim db As DAO.Database
Dim rs As DAO.Recordset
Dim SQL As String
Dim firstName As String
SQL = "SELECT First_Name FROM tblPatient_Info WHERE Patient_ID = 1967;"
' OR
SQL = "SELECT First_Name FROM tblPatient_Info WHERE Patient_ID = '" & 1967 & "';"
' You can write Debug.Print SQL to make sure you're getting the right SQL.
Set db = CurrentDb
Set rs = db.OpenRecordset(SQL)
' The disadvantage of this approach is that the query string must be compiled each time it runs
' OR
Set rs = db.OpenRecordset(YourSaveQueryName)
'This would be the faster method of the 2.
You can then manipulate your data however you want, and loop through the entire recordset.
firstName = "Larry"
If rs.BOF And rs.EOF Then
' Do nothing, the recordset is empty
Else
Do Until rs.EOF
rs.Edit
rs!FirstName = firstName 'This is how you access properties in the recordset
rs.Update
rs.MoveNext
Loop
etc.
I think you need parentheses around your SELECT statement when you pass it to RunSQL, because it's called as a function that returns "something" that gets assigned to FNSQL. So something like this:
FNSQL = DoCmd.RunSQL( _
"SELECT First_Name FROM tblPatient_Info WHERE Patient_ID = '" & 1967 & "';")
In fact, looking at the RunSQL documentation, RunSQL doesn't return anything so, even if you fix your original issue, you'll still find there's an error there.
Furthermore, again according to the documentation, you can only execute SELECT ... INTO statements with RunSQL. For simple SELECT ... FROM statements, simoco is right -- you need to use a Recordset.
2 errors.
First, DoCmd.RunSQL won't do anything with just a select. Maybe you are missing your action (delete or something) or maybe you want to read it, so you should use CurrentDb.OpenRecordset . You'll need to post more code so we can better understand where you are going with this.
Second, if Patient_ID is an integer (and I'm guessing it is), you don't need the '. You don't need the ; either.
So the query should look like this:
varInt = 1967
"SELECT First_Name FROM tblPatient_Info WHERE Patient_ID = " & varInt
If you don't want to use a var for the int, just insert it directly into the string like so:
"SELECT First_Name FROM tblPatient_Info WHERE Patient_ID = 1967"
In case Patient_ID is a string, you will indeed need ', but you are also missing " around the string, so it should look like this:
"SELECT First_Name FROM tblPatient_Info WHERE Patient_ID = '" & "1967" & "'"
Not a correct answer to this question but it's worth noting I just wanted to see if a row existed in the database. I assumed that would require me to write some sort of select or result query. However access has a separate function just to run count operations.
So this code I ended up with:
Dim count As Long
count = DCount("release_id", "list_releases", "list_id = 1 AND release_id = " & Me!release_id)
If count Then
Me!owned.Enabled = False
MsgBox "You own this release."
End If
can this be done, say I have rows (1,2,3,4,5) and I want to grab three rows, select one randomly and then get it's neighbors, so maybe the random selection is row (3), I can also grab (2,4) if I wanted its neighbors, do I just pick one at random and then look for the unique key before and after like this or can I do it all in one sql statement.
I was going to use ADO from excel to pull records (so VBA connects to access, opens a recordset with sql instructions and so on).
Hope I was clear!
I would love to just do this all in a SQL statement
I am not sure Access is capable of all the SQL commands such as SQL Server, so this may be a bit of a problem. If you have a primary key though, you can easly generate a Select query in VBA and then pass open recordset with this SQL.
Dim sSQL as String
Dim lRand as Long
Dim rs as ADODB.Recordset 'or DAO.Recordset'
lRand = VBA.Int(VBA.Rnd() * TableRecordCount) ' TableRecordCount is the number of records in the table that you need to get somehow'
sSQL = "SELECT * FROM TableName WHERE (ID>=" & lRand - 1 & " AND ID <=" & lRand + 1
set rs = CurrentDB.OpenRecordset(sSQL, ...)
I am now not absolutely sure of what you want to use and depending on ADODB or DAO choice, you need to open the recordset accordingly with wither Call rs.Open or Set rs = DB.OpenRecordset
I have a table, call it TBL. It has two columns,call them A and B. Now in the query I require one column as A and other column should be a comma seprated list of all B's which are against A in TBL.
e.g. TBL is like this
1 Alpha
2 Beta
1 Gamma
1 Delta
Result of query should be
1 Alpha,Gamma,Delta
2 Beta
This type of thing is very easy to do with cursors in stored procedure. But I am not able to do it through MS Access, because apparently it does not support stored procedures.
Is there a way to run stored procedure in MS access? or is there a way through SQL to run this type of query
You can concatenate the records with a User Defined Function (UDF).
The code below can be pasted 'as is' into a standard module. The SQL for you example would be:
SELECT tbl.A, Concatenate("SELECT B FROM tbl
WHERE A = " & [A]) AS ConcA
FROM tbl
GROUP BY tbl.A
This code is by DHookom, Access MVP, and is taken from http://www.tek-tips.com/faqs.cfm?fid=4233
Function Concatenate(pstrSQL As String, _
Optional pstrDelim As String = ", ") _
As String
'example
'tblFamily with FamID as numeric primary key
'tblFamMem with FamID, FirstName, DOB,...
'return a comma separated list of FirstNames
'for a FamID
' John, Mary, Susan
'in a Query
'(This SQL statement assumes FamID is numeric)
'===================================
'SELECT FamID,
'Concatenate("SELECT FirstName FROM tblFamMem
' WHERE FamID =" & [FamID]) as FirstNames
'FROM tblFamily
'===================================
'
'If the FamID is a string then the SQL would be
'===================================
'SELECT FamID,
'Concatenate("SELECT FirstName FROM tblFamMem
' WHERE FamID =""" & [FamID] & """") as FirstNames
'FROM tblFamily
'===================================
'======For DAO uncomment next 4 lines=======
'====== comment out ADO below =======
'Dim db As DAO.Database
'Dim rs As DAO.Recordset
'Set db = CurrentDb
'Set rs = db.OpenRecordset(pstrSQL)
'======For ADO uncomment next two lines=====
'====== comment out DAO above ======
Dim rs As New ADODB.Recordset
rs.Open pstrSQL, CurrentProject.Connection, _
adOpenKeyset, adLockOptimistic
Dim strConcat As String 'build return string
With rs
If Not .EOF Then
.MoveFirst
Do While Not .EOF
strConcat = strConcat & _
.Fields(0) & pstrDelim
.MoveNext
Loop
End If
.Close
End With
Set rs = Nothing
'====== uncomment next line for DAO ========
'Set db = Nothing
If Len(strConcat) > 0 Then
strConcat = Left(strConcat, _
Len(strConcat) - Len(pstrDelim))
End If
Concatenate = strConcat
End Function
I believe you can create VBA functions and use them in your access queries. That might help you.
There is not a way that I know of to run stored procedures in an Access database. However, Access can execute stored procedures if it is being used against a SQL backend. If you can not split the UI to Access and data to SQL, then your best bet will probably be to code a VBA module to give you the output you need.
To accomplish your task you will need to use code. One solution, using more meaningful names, is as follows:
Main table with two applicable columns:
Table Name: Widgets
Field 1: ID (Long)
Field 2: Color (Text 32)
Add table with two columns:
Table Name: ColorListByWidget
Field 1: ID (Long)
Field 2: ColorList (Text 255)
Add the following code to a module and call as needed to update the ColorListByWidget table:
Public Sub GenerateColorList()
Dim cn As New ADODB.Connection
Dim Widgets As New ADODB.Recordset
Dim ColorListByWidget As New ADODB.Recordset
Dim ColorList As String
Set cn = CurrentProject.Connection
cn.Execute "DELETE * FROM ColorListByWidget"
cn.Execute "INSERT INTO ColorListByWidget (ID) SELECT ID FROM Widgets GROUP BY ID"
With ColorListByWidget
.Open "ColorListByWidget", cn, adOpenForwardOnly, adLockOptimistic, adCmdTable
If Not (.BOF And .EOF) Then
.MoveFirst
Do Until .EOF
Widgets.Open "SELECT Color FROM Widgets WHERE ID = " & .Fields("ID"), cn
If Not (.BOF And .EOF) Then
Widgets.MoveFirst
ColorList = ""
Do Until Widgets.EOF
ColorList = ColorList & Widgets.Fields("Color").Value & ", "
Widgets.MoveNext
Loop
End If
.Fields("ColorList") = Left$(ColorList, Len(ColorList) - 2)
.MoveNext
Widgets.Close
Loop
End If
End With
End Sub
The ColorListByWidget Table now contains your desired information. Be careful that the list (colors in this example) does not exceed 255 characters.
No stored procedures, no temporary tables.
If you needed to return the query as a recordset, you could use a disconnected recordset.
Perhaps instead of asking if Jet has stored procedures, you should explain what you want to accomplish and then we can explain how to do it with Jet (it's not clear if you're using Access for your application, or just using a Jet MDB as your data store).
Well, you can use a Recordset object to loop through your query in VBA, concatenating field values based on whatever criteria you need.
If you want to return the results as strings, you'll be fine. If you want to return them as a query, that will be more complicated. You might have to create a temporary table and store the results in there so you can return them as a table or query.
You can use GetString in VBA which will return the recordset separated by any value you like (e.g. comma, dash, table cells etc.) although I have to admit I've only used it in VBScript, not Visual Basic. W3Schools has a good tutorial which will hopefully lend itself to your needs.
You can write your stored procedure as text and send it against the database with:
Dim sp as string
sp = "your stored procedure here" (you can load it from a text file or a memo field?)
Access.CurrentProject.AccessConnection.Execute sp
This supposes you are using ADODB objects (ActiveX data Objects Library is coorectly referenced in your app).
I am sure there is something similar with DAO ...
#Remou on DHookom's Concatenate function: neither the SQL standard nor the Jet has a CONCATENATE() set function. Simply put, this is because it is a violation of 1NF. I'd prefer to do this on the application side rather than try to force SQL to do something it wasn't designed to do. Perhaps ACE's (Access2007) multi-valued types is a better fit: still NFNF but at least there is engine-level support. Remember, the question relates to a stored object: how would a user query a non-scalar column using SQL...?
#David W. Fenton on whether Jet has stored procedures: didn't you and I discuss this in the newsgroups a couple of years ago. Since version 4.0, Jet/ACE has supported the
following syntax in ANSI-92 Query Mode:
CREATE PROCEDURE procedure (param1 datatype[, param2 datatype][, ...]) AS sqlstatement;
EXECUTE procedure [param1[, param2[, ...]];
So Jet is creating and executing something it knows (in one mode at least) as a 'procedure' that is 'stored' in the MDB file. However, Jet/ACE SQL is pure and simple: it has no control-of-flow syntax and a PROCEDURE can only contain one SQL statement, so any procedural code is out of the question. Therefore, the answer to whether Jet has stored procedures is subjective.