I want a new entry created in table Moments using the predefined defaults for each field.
I've tried the following lines and I'm getting a syntax error for both. What would be the correct method to do this in Access?
INSERT INTO Moments VALUES ()
Access highlights the end bracket after clicking ok on syntax error.
and
INSERT INTO Moments () VALUES ()
Access highlights the first bracket after clicking ok on syntax error.
and
INSERT INTO Moments default values
Access highlights default after clicking ok on syntax error.
In VBA, this can easily be achieved with recordsets:
Dim rs As DAO.Recordset
Set rs = CurrentDb.OpenRecordset("Moments") 'Open table
rs.AddNew 'Add row
rs.Update 'Commit to table
Related
I have tried making a dataset using the dataset tool to link it on the design side however that hasn't worked as it keeps disappearing and this is the other way it does not display any values on the datagrid view i am unsure why and i am fairly new to this side of vb so if you could explain it as well that would be great. Thanks in advance.
Imports MySql.Data.MySqlClient
Public Class Search
Private Async Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Try
If TextBox1.Text = "" Then 'this acts as a simple presence check on the textbox
Else
Dim val = "name"
If RadioButton1.Checked = True Then 'This changes the type of search i do as it filters which column the query looks in
val = "type"
End If
Await getDataSet(TextBox1.Text) ' waits for infomation to be retrieved
End If
Catch ex As System.Exception
System.Windows.Forms.MessageBox.Show(ex.Message) 'Catches any errors
End Try
End Sub
Async Function getDataSet(partname As String) As Task(Of DataSet) 'This retrieves the values that matches the users input
Return Await Task.Factory.StartNew(
Function()
Dim connectionString = "server=localhost; userid=root; password=; database=partstest1; CharSet=utf8;" 'These are the login details for the database in the form of a connection string
Dim commandText = "SELECT ID, Benchpoint, Name, Type, BrandID FROM `parts` WHERE `name` Like '%" & TextBox1.Text & "%';"
Using connDB = New MySqlConnection(connectionString), objCmd = New MySqlCommand(), objAdpt = New MySqlDataAdapter()
connDB.Open()
objCmd.Connection = connDB
objCmd.CommandText = commandText
objCmd.CommandType = CommandType.Text 'These lines specify the command i am using and execute it
objAdpt.SelectCommand = objCmd
Dim objDs = New DataSet()
objAdpt.Fill(objDs) 'Puts all of the values into a dataset
PartsDataGridView.DataSource = objDs.Tables(0) 'This shows the datasource and displays it
Console.WriteLine(objDs)
Return objDs
End Using
End Function)
End Function
End Class
Install MySQL/connector/etc
Install MySQL for Visual Studio (needed to get it to show as a datasource in VS) - https://dev.mysql.com/downloads/windows/visualstudio/
Add a new dataset to your project
Double click the dataset, right click the surface of it, choose Add >> TableAdapter
Choose existing connection or New Connection if you have no existing. Choose MySQL Database (install MySQL for Visual Studio from step 2 if this is absent)
Enter server details, choose to save the password, back in the wizard choose Yes, include sensitive.. and click Next, choose Yes, save the connection string, click Next, choose Use SQL Statements, Next, enter a suitable query that selects based on the primary key such as SELECT * FROM yourtable WHERE id = #id, Next, call the methods FillById and GetDataById to distinct them from other queries you may add later like FillByCity or GetDataByStatus. At the end of this wizard you should have a datatable and tableadapter that looks like your database table:
Switch to the form designer, and also ensure that the Data Sources window is visible (click View Menu >> Other Windows >> *Data Sources**), expand all the nodes in the Data Sources panel
Drag the node representing your table (mine is called person) out of the datasources window and drop it on the form. Several things will appear:
a datagridview already bound to the dataset's datatable showing person data,
a toolstrip with a textbox for inputting the ID (remember you made a query that takes a parameter),
another toolstrip with navigators and a save button),
a dataset (needed to store the data),
a bindingsource (acts as a link between the dataset's datatable and the grid, knows what current record is showing, allows sorting and filtering),
a tableadapter (an enhanced data adapter that will pull data from the db into the dataset and send back any changes),
a tableadapter manager (a device that knows what order to run updates in for hierarchical data)
Also, as a demonstration of how it works, drag all the nodes UNDERNEATH person onto the form also - you can see they have different icons. They will re-use most of the components on the form but you'll get textboxes, datetimepickers, checkboxes etc that are bound to the bindingsource and will hence show the current item from the underlying list (the datagridview shows them all, with an indicator of which row is current)
Run the project, type an ID into the textbox, hit **FillById*, write some new info into another row at the bottom - it will commit to the underlying dataset/datatable when you change row in the DGV, note that flicking back and forth between the rows in the DGV causes the textboxes to change as the notion of Current row is updated (navigating the grid, or clicking the arrows in the navigator changes the bindingsource .Current property, and all controls bind through the bindingsource
hit save - now go look in your db in MySQL Workbench - the new record is there. You've made a full databound UI, and not written a line of code - all the code is there in the FormX.vb if you want to see it - written for you by the designer. Take a look
Note that it still says -4 (in my UI) for the temp id; the program didn't download the new ID that was assigned by the DB. To make it do this, youre supposed to be able to go back to your dataset, right click the tableadapter, click Advanced Options, and tick on Refresh the datatable - this will cause the adapter to update any local IDs after it saves the row. IDs can cascade update if they are part of a datarelation between two datetables. In MySQL this doesn't work (I'm reporting a bug to them now), but you can make it work by manually editing the XML file representing the dataset (right click the DataSet in solution explorer, choose Open With.. Choose XML Editor, locate the insert statement eg:
INSERT INTO `person` (`Name`, `Birthdate`, `Salary`) VALUES (#p1, #p2, #p3)
Add another statement after it so it looks like:
INSERT INTO `person` (`Name`, `Birthdate`, `Salary`) VALUES (#p1, #p2, #p3)
;SELECT `Id`, `Name`, `Birthdate`, `Salary` FROM `person` WHERE `Id` = last_insert_id()
Now, saving the table will cause the UI to refresh with the ID values calculated by the auto increment in the DB
I have a macro that opens an ADODB connection to a MySQL library. There are two queries that are generated in the macro. The first, which works, is a select statement to check for duplicates. The second, which is not working, is to insert records onto the same table the select statement referenced. I am receiving no errors from VBA, and when I copy/paste directly into MySQL the query works fine.
At the top of my macro I set up the connection as follows:
TimesheetConn = "DRIVER={MySQL ODBC 5.3 ANSI Driver}; SERVER=*server number*;PORT=*port number*;database=my_db;UID=User;PWD=password;Option=2"
'Connection Info
Dim cnn As ADODB.Connection
On Error GoTo AdoError
Set cnn = New ADODB.Connection
With cnn
.ConnectionString = TimesheetConn
.Open
.CommandTimeout = 0
End With
Set FIT_Data = New ADODB.Recordset
Set Task_Data = New ADODB.Recordset
I then develop the Select query (fitidquery) and run it as:
FIT_Data.Open fitidquery, cnn, CursorType = 2
I do not close the connection, but then move on to generate my next query, an insert query (addtasks3), by looping through rows and assigning variables.
Then I try to call the connection again with:
Task_Data.Open addtasks3, cnn, adOpenForwardOnly, adLockReadOnly
cnn.Close
And it doesn't work, nor does it give me any errors either for VBA or SQL. As said before, copying the result of debug.print(addtasks3) into MySQL the query runs correctly and inserts the records.
I've tried opening a 2nd connection with the same parameters. That didn't work as well. I moved the On Error language down to in front of the second query call and it moves on to the AdoError message which seems to indicate that the connection is lost there.
I appreciate any help!
If you issue an Insert-statement, there is no need to involve a recordset. You can simply execute
cnn.Execute addtasks3
Or, if you want to get the number of rows you inserted, use a ADODB.Command:
Dim cmd As New ADODB.Command
cmd.ActiveConnection = cnn
cmd.CommandText = addtasks3
Dim rowsInserted As Long
Call cmd.Execute(rowsInserted)
Debug.Print rowsInserted & " rows inserted."
So I have to say thank you to all of you that mentioned On Errors. Your descriptions after rereading finally made it click with me what I did. I had thought that On Error Resume Next only worked for the line it was in front of in the code (I am still very much a VBA newbie learning as I go) and didn't realize that it would follow through the rest of the code. I had one before my first query because if its a new row it wouldn't return anything to check a duplicate against and cause an error. Hence why I wasn't getting error messages.
I added "On Error GoTo 0" after that section and now I'm getting error messages again. Turns out my user is denied use of the Insert Command for that table (even though I modeled it after a table where it does have use), so it looks like I just need to figure that out. Thank you everyone!
Ok, here's the deal. I have a previously existing SQL Server 2008 database linked to an Access 2002 database via linked tables/views. Up until now, the item code has been a nvarchar type.
I have a SQL query which casts the item codes as Int and an Access 2002 linked query that uses the MAX() function to give me the highest value. It is from this highest value I wish to start incrementing the item codes by 1 every time the "New" record button is selected.
Right now, when "New" is selected, the form is blank, waiting for input. What I want to do is, when "New" is selected, to have the value of the MAX() function query passed to a variable, have 1 added to it, and the resulting value placed in the "Item Code" text box.
It sounds easy enough, but for some reason I can't seem to get it to work. I know Access fairly well, but my VBA is fairly weak.
Sound like it could be done with a custom function.
Dim rs as dao.recordset
Dim db as dao.database
Dim NextInt as string
set db = currentDb
set rs = db.openrecordset(YourMaxQuery,dbOpenSnapshot,dbSeeChanges)
if rs.recordCount >0 THEN
NextInt = Cstr(rs!MaxValue + 1)
END
set rs = nothing
set db = nothing
return NextInt
Call the function in the update statement of your query and it should give you the value you're looking for.
Sorry I took so long to get back to this thread.
Ok, I ended up going with a GlobalSequence in MS SQL Server 2008. Basically just created a table with the max id value as a seed, and matched it with a column that has a bit value to prevent rollbacks and duplicate item codes should a record get deleted. After that, it was pretty easy. :)
Is there a technique I'm overlooking when coding my form?
I have a pair of cascading combos.
ComboSource filters the options available for choice in ComboInformation
ComboInformation sets the contents of a mandatory field in a table
The rowsource for ComboSource is:
SELECT tblSource.SourceID, tblSource.Source
FROM tblSource
ORDER BY tblSource.Source;
The rowsource for ComboInformation is:
SELECT tblInformation.InformationID, tblInformation.SourceID, tblInformation.InformationSelector
FROM tblInformation
WHERE (((tblInformation.SourceID)=[ComboSource])) OR ((([ComboSource]) Is Null))
ORDER BY tblInformation.InformationSelector;
It is valid to select a value in ComboSource which results in the .Listcount for ComboInformation being zero. The user can choose to type a new value in ComboInformation and be prompted to create the relevant item in the Information table linked to the ComboSource entry in the Source table, or they can choose to navigate back to ComboSource to select a different Source.
The problem arises when a user edits an existing record and changes ComboSource to a value for which there is no associated Information records. In the After_Update event for ComboSource I have the following code, intended to update ComboInformation to reflect the new source and force the user to select a new value for ComboInformation (otherwise they could save the record with the old value of Information without realising it).
Me.ComboInformation.Requery 'Reflect the current source
'Set a default value for ComboInformation
If Me.ComboInformation.ListCount > 0 Then
Me.ComboInformation.DefaultValue = Me.InformationTitle.ItemData(0)
Else
On Error Resume Next 'Ignore inevitable error
Me.ComboInformation.DefaultValue = Null
On Error GoTo PROC_ERR 'restore normal error handling
End If
'Force the user to update Information by setting content to "" -- if this isn't done, the
'record can be saved with the 'previous value of Information and the user may not realise
'they haven't made any change
ComboInformation.SetFocus
If Not Me.NewRecord Then
If Me.ComboInformation.ListCount = 0 Then
'Clear info to force it to be updated
On Error Resume Next 'Ignore inevitable error
ComboInformation.Text = "" 'Set it to an invalid value
ComboInformation= Null
On Error GoTo PROC_ERR
Else
ComboInformation= ComboInformation.DefaultValue 'set it to a valid value
End If
End If
End If
If the user selects a source for which there are no valid information choices, and then chooses to create a new value for Information, no problem. But if they decide to navigate back to ComboSource instead, they get an error message insisting they complete ComboInformation, which they can't do. They can undo the change they made to get back to the previous state, but that isn't an intuitive response to an error message telling them to complete ComboInformation.
Is there either a way to allow them to navigate back to ComboSource when ComboInformation is invalid or another way to force them to update ComboInformation after updating ComboSource before saving the record that doesn't have this issue?
Update about the error message: I can trap it in Form_Error although the error message isn't generated there -- no other procedures in the stack. The error message is the message associated with the Information field in the Information table (Validation rule: Is Not Null; Validation text; Every piece of evidence must derive from a piece of information)
Further update: I could handle the error (3316) in the Form_OnError but there will be occasions when isn't a 'valid error' that I don't want to ignore...
Other things tried so far:
Refreshing the page doesn't help.
Demonstration database
There's a database to demo the problem at https://s3-eu-west-1.amazonaws.com/genquiry/test.accdb
Open the only form in the database, set the Source to S3 and attempt to navigate back to Source to select a different value.
I think my last message kind of hit it. Instead of having the Information combo be bound, set it's Control Source dynamically in the AfterUpdate event of the Source field. You're trying to forcefully fill a bound field with a NULL when there's a "Is Not Null" validation set up in the table design. If you unbind it, you should be able to get it to work.
Do you have to clear the second combo box on the after update? I wrote a quick test one and it seems to clear it automatically.
I do not think that the issues is whether or not the combo box is bound, but that you are setting focus to the combobox before you might have items in it or knowing if the user wants to add items to it which could be done before they enter.
Private Sub cboSource.AfterUpdate()
cboInformation.Requery
' if the combobox doesn't refresh to be empty or have a default after the
' requery check the row source for information.
' This way you don't have to focus the second combo box and run into problems
' when you leave it
Dim db as DAO.database
Dim rs as DAO.recordset
Set db = CurrentDB()
Set rs = db.OpenRecordset("SELECT tblInformation.InformationID,
tblInformation.SourceID, tblInformation.InformationSelector
FROM tblInformation
WHERE (((tblInformation.SourceID)= " & ComboSource.value & "))
OR (((" & ComboSource.value & " ) Is Null))
ORDER BY tblInformation.InformationSelector")
If rs.recordcount <> 0 then
cboInformation.SetFocus
Else
' Set and empty value for the combobox so they can't accidentally save the
' record with an old value.
' Prompt them to either select a new value that has records or create a
' new one here
End If
rs.close
db.close
Set rs = nothing
Set db = nothing
end Sub
One possibility:
If I set ComboInformation and ComboInformation.DefaultValue to -1 rather than Null when ComboInformation.Listcount = 0, this avoids triggering the table validation rule (Is Not Null) when the user navigates back to ComboSource. However, it is still an invalid value (as it breaks data integrity between tables) and the user is prevented from exiting the form or saving the record until a valid value is entered (by choosing a different value for Source or creating a value in the Information list).
It works, but it isn't an elegant solution and depends on -1 always being an invalid value for the Automatically-generated InformationID field in the linked Information table, which I don't believe is a safe assumption...
However, if I modify the method slightly to find the lowest existing InformationID field (call this LOWID) and then use LOWID-1 to set ComboInformation, that also works, and always produces an invalid value for the period it is in use.
This is somewhat related to this question, but I was hoping there may be a more elegant / simple solution than defining a User-Defined Function.
Background
In essence, this is a common question: I need to dynamically modify the RowSource query of a combobox control on an Access form. The twist is that there is a possibility the resulting query may throw an exception when executed. That is because the source table of the query exists in a different database file which is defined dynamically and may not exist, or even if the file does exist, the desired table or column may not.
Ideally, I would like to be able to catch this problem when the assignment is made, so that the combobox will be disabled and the user cannot invoke the invalid query allowing the end-user to know there is no data available for the field.
For example I would like something like this:
Private Sub UpdateComboRows(src as String)
On Error Goto InvalidQueryError
cmbBox.RowSource = "SELECT [colName] FROM [dBase III;DATABASE=K:\" & src & "].[tblName];"
' Something here to invoke RowSource query and throw error
cmbBox.Enabled = True
Exit Sub
InvalidQueryError:
cmbBox.RowSource = ""
cmbBox.Enabled = False
End Sub
Or something using if-then statements.
Question
Are there any 'sleek' approaches to this, or am I stuck with trying to populate a dummy DAO recordset? Is there some way of invoking the Combobox RowSource query besides the Dropdown event?
Why not take a step back and check first that the database exists with Dir, then check that the column exists in a function. Very roughly.
Function IsDBOk() As Boolean
On Error GoTo Err_Trap
''The default for boolean is false, so
''IsDBOK=False at this point
sFile=Dir("K:\" & src)
If sFile<>"" Then
Dim rs As DAO.Recordset
Set rs = CurrentDB.OpenRecordset("SELECT [colName] " _
& "FROM [dBase III;DATABASE=K:\" _
& src & "].[tblName];")
If Not rs.Eof() Then
''The selection is only okay if the recordset
''contains records, however, you could return
''a different error for each problem
''file missing, column missing, empty recordset
IsDBOk = True
End If
End If
Exit Function
Err_Trap:
''A missing column will give error 3061
''IsDBOk is already set to false
End Function