Access VBA simple sql select statement - sql

Coming from years of web development where PHP and SQL statements were so simple, this recent task I've been required to undergo with MS Access and VBA is absolutely doing my head in at how much it complicates SQL statements. Mind you I have no prior knowledge about VBA so it could be extremely simple and I'm not just getting it, but all I want to do is
"SELECT type FROM tblMatter WHERE id='$id'"
When I wear my PHP cap, I want to think okay, we are going to have one row of data, that's going to be an array, and I want one object out of that array. Simple.
VBA, however, complicates the $#!t out of it. So far my code looks something like this
Dim matterSQL As String
Dim matterRS As Recordset
matterSQL = "SELECT type FROM tblMatter WHERE id'" & id & "'"
Set matterRS = CurrentDb.OpenRecordset(matterSQL)
MsgBox matterRS![type]
CurrentDb is defined much much earlier in the code to open the connection to the database, and the error is on the line containing OpenRecordset with the error: Data type mismatch in criteria expression.
As I said, I'm new to VBA so I don't know what the heck I'm doing, and all the documentation on the internet is nowhere near helpful. But all I want to do is to get one piece of data from the table.
Thanks in advance!
Edit: I needed to build upon this with another query that takes the info from the last query to run. Same kind of ordeal:
Dim costSQL As String
Dim costRS as Recordset
costSQL = "SELECT email FROM tblScaleOfDisb WHERE category=" & category
Set costRS = CurrentDb.OpenRecordset(costSQL)
MsgBox costRS![email]
This time I'm getting an error on the line containing OpenRecordset with the error: Too few parameters. Expected 1.
Which I don't understand because the code is practically the same as the first half of the question. What have I done wrong?

You are missing = in the condition
try below
matterSQL = "SELECT type FROM tblMatter WHERE id='" & id & "'"
Also if id is numeric you don't need '
matterSQL = "SELECT type FROM tblMatter WHERE id=" & id
Too few parameters. Expected 1.
This happens when the field name in your sql query do not match the table field name
if the field name are correct i believe the the datatype of category is not numeric then you have to use '
costSQL = "SELECT email FROM tblScaleOfDisb WHERE category='" & category &"'"
Always try to use parameterised query to avoid SQL injection

You must understand or prepare few things before you start coding on a new platform. such as
Using Keywords/ Reserved keywords
Capturing Errors
basic arithmetic operations/ string operations.
Available functions / methods
Ways of cleaning your variables after using it
in your case you also need to learn about MS ACCESS SQL. Which is pretty similar to standard SQL but (limited to and) strongly influenced by MS Access internal functions.
SQL execution will return n Rows as result. Each row will have n number of columns. You need to understand how you need to loop through result sets.
Please do have some error capturing method. I will help you to understand the direction before spending hours in Google.
in your first SQL: you have a reserved keyword Type. use square brackets to escape reserved keywords. In where condition numeric fields must not have string quotes and strings must have them.
Tip: You can use the MS Access visual query builder to build your query and copy the SQL to VBA.
list of reserved keywords in MS ACCESS: https://support.microsoft.com/en-us/kb/286335
list of functions: http://www.techonthenet.com/access/functions/
Error handling : http://allenbrowne.com/ser-23a.html
Clean/close your objects after usage by explicitly setting as nothing: Is there a need to set Objects to Nothing inside VBA Functions

Related

Retrieve more than 255 characters from a query field

I have a query that displays more than 255 characters in a field, and I want to put that data into a variable that I can process. Unfortunately, MS Access truncates the field value return to 255 characters.
(At least when using this method:)
MyVar = Nz(rst.Fields("myfield").Value)
Most of the workarounds I've found online suggest to create a table, modify the desired field setting to Long Text, and then migrate the data from the query to the table, but I'm getting the same results. The Long Text field is still truncated during the DoCmd execution.
(At least when I do it this way:)
CurrentDB.Execute "Insert Into target_table Select myquery.* From myquery"
Other suggestions mention to change the field to group by "First", but the field reverts to "Expression" when run because the field definition includes a function that runs during the query to manipulate the results.
The query also isn't mine and is rather complicated, using other field expressions that call other functions across tables. I would like to avoid reverse engineering the entire thing just to update a table if at all possible. The data is already on my screen, looking at me - I just want to be able to use it.
It's a very Microsoft-ish solution for an MS product to display some data and then tell you it can't find the stuff it just gave you (I'm looking at you, file explorer), but I'm hoping someone here might have a viable suggestion. Perhaps some other query-to-table methods that don't truncate? Some other query setting, or field retrieval method?
Try this:
Dim MyVar As String
Dim Value As Variant
Value = rst.Fields("myfield").Value
MyVar = Nz(Value)
In any case, this works:
MyVar = Nz(DLookup("myfield", "myquery", "Id = " & someId & ""))
However, most likely your myquery is the limiting factor. It must be a straight select query to not truncate memo-fields.

when is it safe to NOT use parametised query

I know this is a loaded question but I want to understand the theory here.
I've tried googling and checking SO for this and i DO understand the SQL injection threat.
My question is: Do you ALWAYS needs parametised queries?
eg.
If I have a user input box to search for "pxName"
I have a string:
"SELECT * FROM tblPeople WHERE fName = '" & pxName & "'"
I completely see the risk here, we are open to attack because the user has the power to insert what they want into the input box.
If however I have a variable that I have created.
eg.
Created new record via an ADODB recordset.
Obtained new PK
run query:
"SELECT * FROM tblPeople WHERE personID = " & NewPrimaryKey
Is this a threat?? As in, should this be parametised as parametised queries are "what you do" or am I actually at risk of SQL injection even though there is no place for a user to even input their dodgy strings to perform an attack?
The main fact is I have created an access app just for me to use and now a bunch of friends with similar businesses are keen to try it. It isn't using Parametisation at all at the moment however of maybe 50 query strings in the VBA, there are only 2 with actual user input boxes, the rest are reports and self generated variable queries like the one above.
Thanks!

SELECT query using LIKE property in Microsoft Access returns no results when it should

I'm sure I'm making some kind of rookie error here, but I have no idea what the problem is. I am trying to run a simple query on one table in a microsoft access database using the LIKE property to find records that have a certain text string in a particular field. More specifically, the table, called Catreqs, has a few fields, bib_num, MARC_336, MARC_337, and MARC_338. The MARC_336 field has a text string in it and I want a query that selects all the records for which that text string includes the characters "txt".
Here's my query:
SELECT [Catreqs].record_num, [Catreqs].MARC_336
FROM [Catreqs]
WHERE [Catreqs].MARC_336 Like '%txt%';
I should note that I created this query in MS Access design view and this is the query that was generated when I switched to SQL view. I am a little familiar with SQL and even less familiar with Access so this is actually my preferred way of dealing with it.
I've also tried using Like '*txt*' but that didn't return any results either. For reference, here is the entire text string these characters are in:
text txt rdacontent
Any suggestions thoughts on why this fails and how I can fix it?
Thanks!
In Access, for a string you must use the * character.
Check if [Catreqs] has rows where MARC_336 contains "txt".
This is the official documentation of Access:
https://support.office.com/en-us/article/Like-Operator-b2f7ef03-9085-4ffb-9829-eef18358e931?ui=it-IT&rs=en-001&ad=IT&omkt=en-001

SQL Injection: Why is the following code dangerous?

An intern has written the following vb.net code:
Public Sub PopulateUsersByName (name As String)
'Here, the parameter 'name' is unsantized, coming straight from the form
Dim query As String = ""
query += vbNewLine + " SELECT Id, FirstName, LastName FROM dbo.[Users]"
query += vbNewLine + " WHERE FirstName LIKE '" + REPLACE(name, "'", "''") + "'"
query += vbNewLine + " OR LastName LIKE '" + REPLACE(name, "'", "''") + "'"
'Execute SQLCommand with above query and no parameters
'Then do some other stuff
END Sub
I have explained that in general, one should never use string concatenation when trying to do something like the above. The best practice is to use either an SP, or an SQLClient.SQLCommand with parameters.
His logic is: any sql varchar(xxx) gets sanitized by replacing all single quotes with two single quotes (and adding additional single quotes at the start and end of the string).
I am unable to provide an example of something the user could type that would get around this - I'm hoping I can find something more convincing than "But, as a general principal, one should avoid this - you know... coz... well, don't argue with me - I'M THE BOSS AND MY WISH IS YOUR COMMAND.".
Note: The code will always connect to our Microsoft SQL Server. But I can imagine it failing to sanitize the input on some other SQL Implementation.
UPDATE:
Just to make it a little clearer, what I'm looking for is a possible value of the parameter name which will allow someone to inject SQL into the query.
Try this with his code using '%_%' (with and without the single quotes) as the input....same as this in SQL....
select SELECT Id, FirstName, LastName FROM dbo.[Users] from TBL_EMPLOYEES where FirstName like '%_%' or LastName like '%_%'
irrespective if it failing or not, his is very poor code... fair enough this one is only a one liner, but anything more than that, including complicated SQL statements would be difficult to maintain and debug.. in any case, using Sps gets him used to using them AND allows him to take advantage of the flexibility and power of T-SQL... LOL, i'd dread to think how some of the SQL I have written would look like in code.....
You know, I come across code like this (and a lot worse) all the time... just because it might work for a while DOESN'T mean it's the right way to do it.... If your intern that does NOT listen to experience he will NEVER make a good developer and that is sadly a fact
I once reduced a junior developers attempt at importing a CSV file (50 million rows) in the same way your intern has done, from her 300 lines of code (which was never going to work) to just one line of LINQ to convert it to XML (we couldn't use Bulk Insert or bcp) and a fancy SP... Was bullet proof, job done....
I can get a list of all your users. If name = %%
Now I know the full name of everyone in your database.
I would consider that a security hole.
Sanitizing is not the answer. You can by pass quotes and use "smuggling". A good example is http://danuxx.blogspot.com.br/2011/08/sql-injection-bypassing-single-quotes.html
Also a good practice (to use dynamic queries) is to use parametric dynamic queries. Also SPs can do the trick (if you don't use dynamic queries inside it off course).

How can I make MS Access Query Parameters Optional?

I have a query that I would like to filter in different ways at different times. The way I have done this right now by placing parameters in the criteria field of the relevant query fields, however there are many cases in which I do not want to filter on a given field but only on the other fields. Is there any way in which a wildcard of some sort can be passed to the criteria parameter so that I can bypass the filtering for that particular call of the query?
If you construct your query like so:
PARAMETERS ParamA Text ( 255 );
SELECT t.id, t.topic_id
FROM SomeTable t
WHERE t.id Like IIf(IsNull([ParamA]),"*",[ParamA])
All records will be selected if the parameter is not filled in.
Note the * wildcard with the LIKE keyword will only have the desired effect in ANSI-89 Query Mode.
Many people mistakenly assume the wildcard character in Access/Jet is always *. Not so. Jet has two wildcards: % in ANSI-92 Query Mode and * in ANSI-89 Query Mode.
ADO is always ANSI-92 and DAO is always ANSI-89 but the Access interface can be either.
When using the LIKE keyword in a database object (i.e. something that will be persisted in the mdb file), you should to think to yourself: what would happen if someone used this database using a Query Mode other than the one I usually use myself? Say you wanted to restrict a text field to numeric characters only and you'd written your Validation Rule like this:
NOT LIKE "*[!0-9]*"
If someone unwittingly (or otherwise) connected to your .mdb via ADO then the validation rule above would allow them to add data with non-numeric characters and your data integrity would be shot. Not good.
Better IMO to always code for both ANSI Query Modes. Perhaps this is best achieved by explicitly coding for both Modes e.g.
NOT LIKE "*[!0-9]*" AND NOT LIKE "%[!0-9]%"
But with more involved Jet SQL DML/DDL, this can become very hard to achieve concisely. That is why I recommend using the ALIKE keyword, which uses the ANSI-92 Query Mode wildcard character regardless of Query Mode e.g.
NOT ALIKE "%[!0-9]%"
Note ALIKE is undocumented (and I assume this is why my original post got marked down). I've tested this in Jet 3.51 (Access97), Jet 4.0 (Access2000 to 2003) and ACE (Access2007) and it works fine. I've previously posted this in the newsgroups and had the approval of Access MVPs. Normally I would steer clear of undocumented features myself but make an exception in this case because Jet has been deprecated for nearly a decade and the Access team who keep it alive don't seem interested in making deep changes to the engines (or bug fixes!), which has the effect of making the Jet engine a very stable product.
For more details on Jet's ANSI Query modes, see About ANSI SQL query mode.
Back to my previous exampe in your previous question. Your parameterized query is a string looking like that:
qr = "Select Tbl_Country.* From Tbl_Country WHERE id_Country = [fid_country]"
depending on the nature of fid_Country (number, text, guid, date, etc), you'll have to replace it with a joker value and specific delimitation characters:
qr = replace(qr,"[fid_country]","""*""")
In order to fully allow wild cards, your original query could also be:
qr = "Select Tbl_Country.* From Tbl_Country _
WHERE id_Country LIKE [fid_country]"
You can then get wild card values for fid_Country such as
qr = replace(qr,"[fid_country]","G*")
Once you're done with that, you can use the string to open a recordset
set rs = currentDb.openRecordset(qr)
I don't think you can. How are you running the query?
I'd say if you need a query that has that many open variables, put it in a vba module or class, and call it, letting it build the string every time.
I'm not sure this helps, because I suspect you want to do this with a saved query rather than in VBA; however, the easiest thing you can do is build up a query line by line in VBA, and then creating a recordset from it.
A quite hackish way would be to re-write the saved query on the fly and then access that; however, if you have multiple people using the same DB you might run into conflicts, and you'll confuse the next developer down the line.
You could also programatically pass default value to the query (as discussed in you r previous question)
Well, you can return non-null values by passing * as the parameter for fields you don't wish to use in the current filter. In Access 2003 (and possibly earlier and later versions), if you are using like [paramName] as your criterion for a numeric, Text, Date, or Boolean field, an asterisk will display all records (that match the other criteria you specify). If you want to return null values as well, then you can use like [paramName] or Is Null as the criterion so that it returns all records. (This works best if you are building the query in code. If you are using an existing query, and you don't want to return null values when you do have a value for filtering, this won't work.)
If you're filtering a Memo field, you'll have to try another approach.