MS access SELECT INTO in vba - sql

I'm having some issues with some functionality of my application. There is a particular instance where I have an instance of a 'pending class' on a form for an administrator to review. The form is populated with students associated with this pending class. After their grades are finished, I have a button at the footer that will delete this class from my 'pending' table and add the grades to all of the students. This works.
However, I want to essentially copy this pending class, which just has the class name, date, and teacher to a completed class table before it's deleted from pending. Since no data about this class other than the primary key(class number) persists throughout this form, I can't populate the other fields(class name, date) of the row into my completed class table.
I am trying a "SELECT INTO" operation in VBA to get these values. It's going like this:
dim cname as String
dim classdate as Date
dim pid as integer
dim teacher as String
dim qry as String
pid = [Forms]![frmClasses]![txtID]
qry = "Select className INTO cname FROM tblPending WHERE tblPending.id = " & " ' " & pid & " ' " & ";"
db.execute qry
debug.print qry
debug.print cname
From here, I do the same operations for each other variable, build my INSERT query, and execute it. The problem is-- my select into's are not working. Debug.print shows that the local variables were never initialized from the SELECT INTO statement. Any thoughts?

First, having all classes in one table and just setting a "NotPending" or "Completed" column would be better.
Having two identical tables for classes and moving values from one into the other to indicate status changes is bad database design.
If you really need to do this by using two tables and copying rows, then you need an INSERT INTO query (and not SELECT INTO), as already mentioned by Remou in the comments, because SELECT INTO creates a new table (or overwrites an existing one with the same name, if already there).
The syntax for INSERT INTO looks like this:
INSERT INTO CompletedClassTable (ClassName, Teacher)
SELECT ClassName, Teacher FROM tblPending WHERE id = 123
And finally, you asked this in a comment:
So SELECT INTO is completely different in Access than Oracle? In Oracle and PL/SQL, you can select a row into a variable OR a table. In Access can you not select into a variable?
To load a row into a variable, you need to use a Recordset.
Example code to load your query into a Recordset and output the ClassName field:
Dim RS As DAO.Recordset
Set RS = CurrentDb.OpenRecordset("SELECT * FROM tblPending WHERE id = 123")
If Not RS.EOF Then
Debug.Print RS("classname")
End If
RS.Close
Set RS = Nothing

Seems you want to retrieve a text value, className, from tblPending where tblPending.id matches the value found in your text box, txtID, and store that text value in a string variable named cname.
If that interpretation is correct, you needn't bother with a query and recordset. Just use the DLookup Function to retrieve the value, similar to this untested code sample.
Dim cname As String
Dim pid As Integer
Dim strCriteria As String
pid = [Forms]![frmClasses]![txtID]
strCriteria = "id = " & pid
cname = Nz(DLookup("className", "tblPending", strCriteria), vbNullString)
Debug.Print "cname: '" & cname & "'"
Notes:
I assumed the data type of the id field in tblPending is numeric. If it is actually text data type, change strCriteria like this:
strCriteria = "id = '" & pid & "'"
DLookup() returns Null if no match found. Since we are assigning the function's return value to a string variable, I used Nz() to convert Null to an empty string. Alternatively, you could declare cname As Variant (so that it can accept a Null value) and get rid of Nz().

Related

Problem importing an excel workbook into Access and adding a column; error 3127

I am creating a form in an Access database that allows a user to import an Excel workbook into the database, then inserts a column with that day's date as a way to log when the record was imported, with the idea that I can later compare this to a master database and update accordingly.
My code is below:
Private Sub btnImport_Click()
'create a new file system object that will check for conditions and import the file as a new table if a valid name is chosen
Dim FSO As New FileSystemObject
Dim strSQL As String
'Dim curDatabase As Object
Dim tableTest As Object
Dim fieldNew As Object
Dim todayDate As Date
Dim tempTable As String
tempTable = "TempTable" & CStr(Date)
'MsgBox TempTable
'If no file name in box
If Nz(Me.txtFileName, "") = "" Then
MsgBox "Please choose a file."
Exit Sub
End If
'If a file name is in box and the file can be located
If FSO.FileExists(Me.txtFileName) Then
fileImport.ImportExcel Me.txtFileName, tempTable
'once it imports the table, it then adds today's date (the upload date)
todayDate = Date
strSQL = "INSERT INTO tempTable (Upload_Date) Values (#" & Date & "#);"
DoCmd.RunSQL strSQL
'DoCmd.RunSQL ("DROP Table TempTable")
Else
'Error message if file can't be found
MsgBox "File not found."
End If
End Sub
Unfortunately, right now I am getting two problems.
The first is
run-time error 3127: The INSERT INTO statement contains an unknown
field name.
I thought I wanted to insert a new field, so I'm a little perplexed by this error.
I'm also getting another error; the compiler doesn't seem to like when I use tempTable for the table name. I'm trying to use a reference to the table name, rather than the actual name of the table itself, because this will end up being a daily upload, so the name of the table that is having this column inserted into it will change every day.
I appreciate any guidance that you can give; I'm fairly new to VBA.
UPDATE: I ended up solving this issue by A. using an UPDATE statement and using CurrentDb.Execute to add the date. I found that this worked for me:
strSQL = "ALTER TABLE TempTable ADD COLUMN Upload_Date DATE;"
strSQL2 = "UPDATE TempTable SET Upload_Date = '" & Date & "'"
DoCmd.RunSQL strSQL
CurrentDb.Execute strSQL2
INSERT INTO doesn't add columns, it just adds rows (with data in existing columns). Look into ALTER TABLE ( https://learn.microsoft.com/en-us/office/client-developer/access/desktop-database-reference/alter-table-statement-microsoft-access-sql )
I'm not sure if it's related, but the table name you use in strSQL is "tempTable", yet the table name you pass to fileImport.ImportExcel is "TempTable" (i.e. the capitalization of the first letter is inconsistent).
If the variable "tempTable" is meant to hold the name of the table (so it can be used for different table names) then it should be outside of the SQL strings:
strSQL= "ALTER TABLE " & tempTable " & " ADD COLUMN Upload_Date DATE;"
strSQL2 = "UPDATE " & tempTable & " SET Upload_Date = '" & Date & "';"
Otherwise you are amending and updating a table called "TempTable" rather than inserting the calculated table name from the variable into the SQL string.
Also note that there should be a semi-colon at the end of strSQL2 as well.

Access 2010 - Determine existence of record in one of three remote mdb files for subsequent select

There are three mdb files in folders on a network drive that may hold the required record(s). How do I determine which db holds the record(s), ideally without data transfer/linking/etc.? Then a single SQL or DAO select can get the data from the correct db. Note: I'm trying to use Access as a front end to SQL using existing Access data spread all around the network drives.
My current solution of configuring 3 DAO objects and checking for no results, in succession until found, seems to load the remote tables to the local recordset and takes too long.
Is there a way to use IF EXISTS in this scenario?
This code throws "Invalid SQL statement; expected DELETE,INSERT,PROCEDURE,SELECT,OR UPDATE" error but is generally what I'd like to do :
Dim strSQL As String
Dim strSku As String
Dim intDbToSearch As Integer
strSku = DLookup("SKUNo", "tblCurrentItem") 'Note: this returns valid SKU#
strSQL = "IF EXISTS(SELECT xxTable.SKUNo "
strSQL = strSQL & "FROM [S:\Our Inventory\Cust Sleeves.mdb].[xxTable] "
strSQL = strSQL & "Where xxTable.SKUNo = " & "'" & strSku & "') Then intDbToSearch = 1"
DoCmd.RunSQL strSQL
This is one of three IF Exists that would run if SKUNo not found in db 1 or 2.
Ultimately intDbToSearch should point to db 1,2,or 3 if SKUNo found or 0 if not.
Thanks
In the end, I pushed usage rules for the 3 databases upstream and can now predetermine which database to search. Thanks again for your input.
Will the sought for SKU always occur in only 1 of the tables?
If you don't want to set table links or use VBA recordsets, only other approach I can see is a query object with a dynamic parameter that references a form control for the SKU input. No idea if this will be faster and will need a query for each remote table.
SELECT SKUNo FROM xxTable IN "S:\Our Inventory\Cust Sleeves.mdb" WHERE SKUNo = Forms!formname!cbxSKU
Then just do DCount on the query.
Dim intDbToSearch As Integer
If DCount("*", "xxQuery") > 0 Then
intDbToSearch = 1
End If
Could UNION the SELECT statements so would have only 1 query object to work with.
SELECT "x1" AS Source, SKUNo FROM xxTable IN "S:\Our Inventory 1\Cust Sleeves.mdb" WHERE SKUNo = Forms!formname!cbxSKU
UNION SELECT "x2", SKUNo FROM xxTable IN "S:\Our Inventory 2\Cust Sleeves.mdb" WHERE SKUNo = Forms!formname!cbxSKU
UNION SELECT "x3", SKUNo FROM xxTable IN "S:\Our Inventory 3\Cust Sleeves.mdb" WHERE SKUNo = Forms!formname!cbxSKU;
How about a simple Function to check if exists by passing the table name and value?
Something like this:
Public Function ExistInTable(Byval TableName As String, ByVal Value As String) As Boolean
ExistInTable = (DCount("*", TableName, "[SKUNo]='" & Value & "'" > 0)
End Function
To call it:
Sub Test()
If ExistInTable("T1", "Whatever") Then 'Exists in T1
If ExistInTable("T2", "Whatever") Then 'Exists in T2
'....
End Sub

Access query updatable fields with group by

I want to create a split form based on a query where the fields are all grouped. The split form will not let me update the records because they are grouped. For instance let's say 10 records all had the same data in a field called "Company Name". Is there any way to make the query updatable such that when I change the data for "Company Name" on the grouped entry it will change for all of the records that are grouped?
Thanks
it is definitively not possible to update a grouped query.
The reason is, that a grouped query cannot contain the key (if you include it, you have no more a grouping, as the key is unique ...)
So Access has no clue, what is grouped and which records should be updated
What you have to do is:
create a form based on the query
add an event "on double-click" to the field you want to change
program a dialog to ask for new value
fire an sql to update
here a sample for steps 2-4
Private Sub DOK_DokumentNr_DblClick(Cancel As Integer)
Dim newvalue As Variant
Dim sSQL As String
newvalue = InputBox("enter new value", "DOC-Number", Me!DOK_DokumentNr.OldValue)
If newvalue <> Me!DOK_DokumentNr.OldValue Then
sSQL = "UPDATE T_Dokument SET DOK_DokumentNr = '" & newvalue & "' "
sSQL = sSQL & "WHERE DOK_DokumentNr = '" & Me!DOK_DokumentNr.OldValue & "'"
DoCmd.SetWarnings False ' to prevent the standard message for modifying data
DoCmd.RunSQL sSQL
DoCmd.SetWarnings True ' reset warnings to default
End If
End Sub

Create Variable or Method to use value in multiple forms

Fairly new to VBA. I have a list box on a form within Access which is populated with data from a table. Selecting a value from the list box gives and ID which is then used to perform a query. I need this ID to be available for use in another form to perform a query based on the Value. What is the best way of achieving this?
`Dim IDValue As String
IDValue = Me.lstBoxCompanyName.Value
CompDetailSQL = "SELECT * FROM Companies WHERE Companies.CompanyID = " & IDValue`
In a module
Dim IDValue AS Integer
Sub setIDValue(id As Integer)
IDValue = id
End Sub
Function getIDValue() As Integer
getIDValue = IDValue
End Function
Then from any of your code
'This will set you ID
setIDValue YOUR_VALUE
'This will retrieve the value
getIDValue
'so your code could be
setIDValue Me.Me.lstBoxCompanyName.Value
CompDetailSQL = "SELECT * FROM Companies WHERE Companies.CompanyID = " & getIDValue
Obviously this could use some Error Handling in the event that No IDValue is set. Also I used integer even though I see you are using a String the data type should be the same type as CompanyID so you can change Integer to String if needed but your will also have to change your query to SELECT * FROM Companies WHERE Companies.CompanyID = '" & getIDValue & "' because your query implies a number currently.
You can gain access to another forms controls by specifying the full path. Example :
Forms!MyFormsName!MyControlsName.Value
and
Forms!frmCustomer!CboCustomer.Column(0)
So if you want to access a value in VBA that is contained in another form and use it as part of a query it would look like:
CompDetailSQL = "SELECT * " & _
"FROM Companies " & _
"WHERE CompanyID = " & Forms!frmCustomer!lstBoxCompanyName.Value
in ms Access you can also simply use form_NAMEofFORM.NAMEofCONTROL
Example you can reference
dim myAmount as double
myAmount = form_receipt.total
to access the value showing on form receipt textbox total.

SQL statement for combobox row source

I'm trying to define a SQL statement to use as the Row Source for a ComboBox on an MSAccess form. The SQL should select records from a table tblI where a particular table field matches a variant parameter varS set by the user; however, if varS is Null or not present in another table tblS, the SQl should select all records in tblI.
I can code the first parts of this (varS matches or is null):
SELECT tblI.ID, tblI.S FROM tblI WHERE ((tblI.S = varS) OR (varS Is Null)) ORDER BY tblI.ID;
Where I'm struggling is incorporating the final element (varS not present in tblS). I can code a test for the absence of varS in tblS:
Is Null(DLookup("[tbls.ID]","tblS","[tblS.ID]= " & varS))
but I can't work out how to incorporate this in the SQL statement. Should this work?
SELECT tblI.ID, tblI.S FROM tblI WHERE tblI.S = varS OR varS Is Null OR DLookup("[tbls.ID]","tblS","[tblS.ID]= " & varS) Is Null ORDER BY tblI.ID;
When run as a query it returns every record in tblS no matter the value of varS.
Table structure:
tblI contains 2 fields, Autonumber ID and Long S
tblS contains 1 field, Autonumber ID
My own approach to this problem would be something like this:
Private Sub SetComboSource(vID as Variant)
Dim sSQL as String
sSQL = "SELECT tblI.ID, tblI.S " & _
"FROM tblI "
If IsNull(vID) = False Then
If IsNumeric(vID) = True Then
If DCount("ID", "tblS", "ID = " Clng(vID)) > 0 Then
sSQL = sSQL & "WHERE tblI.S = " & CLng(vID)
End If
End If
End If
sSQL = sSQL & " ORDER BY tblI.ID"
Me.cboComboBox.RowSource = sSQL
End Sub
BTW, I recommend you give your tables and fields more descriptive names and then use aliasing in your SQL, especially for table names. I also think it's best to avoid using Variant variables. I usually use Longs for something like this and I take a value less than 1 to mean that the user didn't select anything, or selected ALL, or whatever meaning you want to derive from it. In other words, my ID's are always a number greater than zero and an ID of less than 1 in a variable means that the ID is empty. Which I use as a signal to create a new record, or to return all records, or whatever meaning you want to derive from it in the given context.
The following should work;
SELECT tblI.ID, tblI.S
FROM tblI
WHERE tbl.ID=varS
OR varS NOT IN(SELECT ID from tblS)