In OS/X I have a Customer table with the fields Name, TaxCode, and Address defined with the "required" attribute (other fields not). In a layout without any of these required fields I execute a script with a Perform Find[Restore] step which looks for records containing value 1 in another field of the same table.
I have traced the script execution and when the script reaches the Perform Find step the following Dialog pops:
"Name" is defined to require a value. Allow this field to remain empty?
Revert Record No Yes
If I click Yes then comes an identical message for each of the remaining "required" fields in the table. Eventually the script finishes as expected, but of course this manual intervention makes the query cumbersome and unacceptable.
What is happening? and what can I do?
In your layout, there is a record with open state that doesn't meet your requirements. When you try to enter find mode, it fails to commit.
Try to use Get ( RecordOpenState ) to see what you get in the layout before you enter find mode – 0 is committed, while 1 and 2 are records not committed.
Also, make sure you do not have a "new record/request" script step before the "enter find mode"; it could be the reason of your trouble.
I checked the contents of the Customer table and found a register with all the fields empty. Once this record was deleted, the problem disappeared.
Cannot remember/explain how the empty record came about, probably some trouble during the creation process. What still surprises me is the circumstances in which Filemaker verifies the correctness of fields NOT involved in the actual Find.
Thanks for the suggestion.
Related
Literally as the title said.
I'm checking an old database left by earlier developer, and apparently instead of creating a new "Master" table, he created a table which contains constants in the form of JSONs. Now however I want to check whether that row is still used, and when is the last time it's used.
When transitioning, the developer doesn't provide documentation whatsoever. So I have to check on my own on how things should work. I want to know because the code is really messy. Also since I can't seem to find this on Google, it's something worth to ask.
You cannot log past events. PostgreSQL does not retain that information.
The best you can do is:
Set log_statement = 'all'
Examine the statements in the log.
I have an access project that is "linked" to a SQL database that now works like a charm. The last problem I solved was, making sure any Boolean fields be turned to bits with default of 0, and adding the TIMESTAMP in SQL due to the fact that ACCESS is not so much of a genius with record locking (so I was told) .
Now that I tried to connect direct to SQL server by using an ADODB.Recordset and setting the forms.recordset to the recordset, at the OnOpen event of the form, (this recordset runs a stored procedure in SQL, I get the data fine but get the error locking (write conflict) back.
This ADODB.Recordset cursorlocation is set to "adUseClient".
Obviously I no longer have the forms recordsource attached or assigned to the linked SQL table anymore.
Am I missing something? do I need to assign anything to the forms recordsource?
The Idea is trying to connect directly thru the use of stored procedures instead of linked tables.
thanks so much for any help.
The adding of timestamp is a VERY good idea. And do not confuse the term/name used timestamp to mean an actual date/time column. The correct term is "row version".
This issue has ZERO to do with locking. The REASON why you want this column added is because then Access will use that column to determine when the record is dirty, and more imporant figure out that the record been changed. If you omit this column, then access reverts to a column by column testing approach. Not only does this cause more network traffic, but worse for real type values, due to rounding, you can get the dredged this record has been changed by another user. But, it not been changed, and even columns with floating point values will cause access to error out with that changed record.
So, for all tables, and you even see the option included in the SSMA (the access to sql migration wizard that this option is available (and I believe it is a default).
So yes, it is HIGH but VERY high recommended that you include/add a rowversion column to all tables - this will help Access in a HUGE way.
And as noted, there is a long standing issue with bit fields that don't have a default setting. so, you don't want to allow bit fields to be added/created with a null value. So, ensure that there is a default value of 0 (you set this sql server side).
Ok, now that we have the above cleared up?
It not really all that clear as to why you want or need or are adopting a store procedure and code to load/fill up the form. You not see any better performance if you bind the form DIRECTLY to the linked table. Access will ONLY pull the reocrds you tell that form to load.
So, bind the form directly to the linked table. Then, you can launch/open the form say to once reocrd with this:
docmd.OpenForm "frmInvoices",,,"InvoiceNum = 123"
Now, you would of course change the above "123" to some variable or some way to prompt the user for what invoice to work on.
The invoice form will then load to the ONE record. So, even if the form bound (linked table) has 2 million rows? Only ONE record will come down the network pipe. So, all that extra work of a store procedure, creating a recordset and pulling it ? You will gain ZERO in terms of performance, but you are writing all kinds of code when it simply not required, and you not achieve any superior performance to the above one line of code that will automatic filter and ONLY pull down the record that meets the given criteria (in this example invoice number).
So:
Yes, all tables need a PK
Yes, all tables should have a rowversion (but it called a timestamp column - nothing to do with the actual time).
Yes, all bit fields need a default of 0 - don't allow null values.
And last but not least?
I don't see any gains in performance, or even any advantages of attempting to code your way though this by adopting store procedures and that of introducing reocrdset code when none is required, but worse will not gain you performance anyway.
According to the searching I've done over the past couple of days, what I'm trying to achieve should be fairly straightforward but nothing that I've found has solved my problems. This is my first time at using Access, or SQL at all. Apologies in advance for the length of the question.
Essentially I need to pick up a value from one table and multiply it by another in the another table, and then store the result in the second table, via forms and subforms.
The Problem:
I'm attempting to create a database of projects, part of which is a quotation tool. The database has several tables covering all the required inputs for our project managers, most of which are linked to the PKs of their parent tables.
My current attempt has a form (frmJobDetails) giving the details of the each project (linked to tblJobs). This form has two subforms:
frmJobRolesSubform details who's working on the project in what role ( and, notably, their sale rate.
frmJobProcessesSubform details the tasks, who's allocated to which task and the estimated number of hours to complete.
Both subforms link to their own tables (tblJobs_Roles and tblJobs_Processes respectively).
frmJobProcessesSubform obtains the people working on the project and their roles from frmJobRolesSubform so the manager can allocate a person to a task on frmJobProcessesSubform. This is done via a combobox: cboRole.
So far, so good.
I'm needing to obtain the sale rate of the person working on the given task so that I can calculate the cost of the task. Specifically, I'd like a field on the subform to calculate the cost of the task and then store it in tblJobs_Processes.
My Attempts
I've attempted to build a query (qryProcessCost) that calls cboRole, either as an expression in the Field cell or in the Criteria cell ([Forms]![frmJobsProcessesSubform]![cboRole]).
I'm aware this can't successfully when the form isn't active, but I'm getting Access' request for input for [Forms]![frmJobsProcessesSubform]![cboRole] when selecting from the subform. The query runs successfully when example values are hardcoded into the query. The query should, obviously, only return a single value.
I've tried setting the ControlSource of a textbox to [qryProcessCost]![dblProcessCost] (where dblProcessCost is the calculated field), but this can't then write to the table (as far as I can deduce). Also, I get a #Name? error in the cell and I can't seem to get to the bottom of that.
I've tried setting the RecordSource property of a combobox to SELECT [qryProcessCost]![dblProcessCost] FROM [qryProcessCost], and the ControlSource to the relevant field of the table. While this would be a clunky solution, it actually doesn't work anyway as it fails to pick up the value of [Forms]![frmJobsProcessesSubform]![cboRole].
I've tried using an intermediate textbox to determine what value cboRole is passing, and I'm happy with that - the primary key of the role assigned in frmJobsRolesSubform.
The Question:
I'm guessing that I'm probably going to have to resort to VBA at this point to get what I want but I'm unfamiliar with the Access VBA structures (though I've used Excel VBA a fair bit).
Anyone got any ideas, hints, suggestions or pointers?
Cheers in advance,
Aaron
In case anyone else has a similar problem, I've posted my complete solution below:
I implemented Gene's correction to my references, which provided something, but the query wouldn't update when the fields on the form were changed. As I noted in the question, I also really wanted it to be a text box rather than a combo box for usability reasons but wasn't sure how to have a Record Source and Control Source for a text box.
I ended up going round in circles, via VBA and macros and several types of error and happened upon a suggestion to use a DLookUp here. I hadn't been able to get them to work for this particular problem before, but I managed to make it work this time. Specifically, I put the DLookUp in the following macro:
SetProperty
Control Name txtBudgetCost
Property Value
Value =DLookUp("[dblCostRate]","[tblJobs_Roles]","[pkJobs_RoleID]="[cboRole])*[txtBudgetHours]
This macro was used for the After Update event of the relevant fields on the subform.
Setting the value property of a field to a DLookUp meant that I could set the Control Source property of a the text box txtBudgetCost to the relevant field in the table, to obtain the desired behaviour.
Both cboRole and txtBudgetHours are fields on the same subform as the field txtBudgetCost.
The only problem with this solution is that, when the subform is viewed in the Datasheet view, a #Name? error is given for the new record row. I probably just need to enter some error handling somewhere, though I haven't given much thought for what it should be just yet.
Cheers,
Aaron
Actually, I want to unset it.
I'm fixing up an FMP database that's been around for years. Part of it is a layout that shows a table of scheduled events. Whenever I move from one record to another, it automatically sorts on two fields: StartDate and StartTime. I don't want it to do that anymore, but I can't find it to remove it.
I don't think it's a script. There is a SORT script in the database, but removing it did not stop the auto-sort behavior.
Check the SCRIPT TRIGGERS for the fields, i think you will find in "OnObjectsave" it calls script.
right click on the field > set script triggers
It sounds like a Script Trigger may be running on the layout. To find out:
In Layout mode, navigate to LAYOUTS > LAYOUT SETUP...
and then click the SCRIPT TRIGGERS tab. Layout script
triggers can only be configured one layout at a time.
More information on script triggers can be found here: http://help.filemaker.com/app/answers/detail/a_id/7465
Is this data from a related file/table? There might a "sort" criteria be defined in the relation.
Yes, I have this nagging problem also. The solution is found in Filemaker Help, topic = Sort, item = Options for Sorting Records. See next to last item in their instruction table (FM-12).
The setting causing automatic sorting is found in the Sort definition dialog that defines sort order for your layout. Somewhere in your script you defined a sort for your layout. OR, when creating the layout, you defined a sort order for it. Whichever, you need to find and open the Sort dialog box. Unselect "Keep records in Sorted Order." Otherwise, Filemaker will re-sort automatically.
Hope this helps.
We've got a set of forms in our web application that is managed by multiple staff members. The forms are common for all staff members. Right now, we've implemented a locking mechanism. But the issue is that there's no reliable way of knowing when a user has logged out of the system, so the form needs to be unlocked. I was wondering if there was a better way to manage concurrent users editing the same data.
You can use optimistic concurrency which is how the .Net data libraries are designed. Effectively you assume that usually no one will edit a row concurrently. When it occurs, you can either throw away the changes made, or try and create some nicer retry logic when you have two users edit the same row.
If you keep a copy of what was in the row when you started editing it and then write your update as:
Update Table set column = changedvalue
where column1 = column1prev
AND column2 = column2prev...
If this updates zero rows, then you know that the row changed during the edit and you can then deal with it, or simply throw an error and tell the user to try again.
You could also create some retry logic? Re-read the row from the database and check whether the change made by your user and the change made in the database are able to be safely combined, then do so automatically. Or you could present a choice to the user as to whether they still wish to make their change based on the values now in the database.
Do something similar to what is done in many version control systems. Allow anyone to edit the data. When the user submits the form, the database is checked for changes. If the record has not been changed prior to this submission, allow it as usual. If both changes are the same, ignore the incoming (now redundant) change.
If the second change is different from the first, the record is now in conflict. The user is presented with a new form, which indicates which fields were changed by the conflicting update. It is then the user's responsibility to resolve the conflict (by updating both sets of changes), or to allow the existing update to stand.
As Spence suggested, what you need is optimistic concurrency. A standard website that does no accounting for whether the data has changed uses what I call "last write wins". Simply put, whichever connection saves to the database last, that version of the data is the one that sticks. In optimistic concurrency, you use a "first write wins" logic such that if two connections try to save the same row at the same time, the first one that commits wins and the second is rejected.
There are two pieces to this mechanism:
The rules by which you fail the second commit
How the system or the user handles the rejected commit.
Determining whether to reject the commit
Two approaches:
Comparison column that changes each time a commit happens
Compare the data with its committed version in the database.
The first one entails using something like SQL Server's rowversion data type which is guaranteed to change each time the row changes. The upside is that it makes it simple to roll your own logic to determine if something has changed. When you get the data, you pull the rowversion column's value and when you commit, you compare that value with what is currently in the database. If they are different, the data has changed since you last retrieved it and you should reject the commit otherwise proceed to save the data.
The second one entails comparing the columns you pulled with their existing committed values in the database. As Spence suggested, if you attempt the update and no rows were updated, then clearly one of the criteria failed. This logic can get tricky when some of the values are null. Many object relational mappers and even .NET's DataTable and DataAdapter technology can help you handle this.
Handling the rejected commit
If you do not leave it up to the user, then the form would throw some message stating that the data has changed since they last edited and you would simply re-retrieve the data overwriting their changes. As you can imagine, users aren't particularly fond of this solution especially in a high volume system where it might happen frequently.
A more sophisticated (and also more complicated) approach is to show the user what has changed allow them to choose which items to try to re-commit, Behind the scenes you would retrieve the data again, overwrite the values picked by the user with their entries and try to commit again. In high volume system, this will still be problematic because by the time the user has tried to re-commit, the data may have changed yet again.
The checkout concept is effectively pessimistic concurrency where users "lock" rows. As you have discovered, it is difficult to implement in a stateless environment. Users are notorious for simply closing their browser while they have something checked out or using the Back button to return a set that was checked out and try to recommit it. IMO, it is more trouble than it is worth to try go this route in a web-based solution. Assuming you write the user name that last changed a given row, with optimistic concurrency, you can inform the user whose changes are rejected who saved the data before them.
I have seen this done two ways. The first is to have a "checked out" column in your database table associated with that data. Your service would have to look for this flag to see if it is being edited. You can have this expire after a time threshold is met (with a trigger) if the user doesn't commit changes. The second way is having a dedicated "checked out" table that stores id's and object names (probably the table name). It would work the same way and you would have less lookup time, theoretically. I see concurrency issues using the second method, however.
Why do you need to look for session timeout? Just synchronize access to your data (forms or whatever) and that's it.
UPDATE: If you mean you have "long transactions" where form is locked as soon as user opens editor (or whatever) and remains locked until user commits changes, then:
either use optimistic locking, implement it by versioning of forms data table
optimistic locking can cause loss of work, if user have been away for a long time, then tried to commit his changes and discovered that someone else already updated a form. In this case you may want to implement explicit "locking" of form, where user "locks" form as soon as he starts work on it. Other user will notice that form is "locked" and either communicate with lock owner to resolve issue, or he can "relock" form for himself, loosing all updates of first user in process.
We put in a very simple optimistic locking scheme that works like this:
every table has a last_update_date
field in it
when the form is created
the last_update_date for the record
is stored in a hidden input field
when the form is POSTED the server
checks the last_update_date in the
database against the date in the
hidden input field.
If they match,
then no one else has changed the
record since the form was created so
the system updates the data.
If they don't match, then someone else has
changed the record since the form was
created. The system sends the user back to the form edit page and tells the user that someone else edited the record and they must reapply their changes.
It is very simple and works well enough.
You can use "timestamp" column on your table. Refer: What is the mysterious 'timestamp' datatype in Sybase?
I understand that you want to avoid overwriting existing data with consecutively updates.
If so, when the user opens a screen you have to get last "timestamp" column to the client.
After changing data just before update, you should check the "timestamp" columns(yours and db) to make sure if anyone has changed tha data while he is editing.
If its changed you will alert an error and he has to startover. If it is not, update the data. Timestamp columns updated automatically.
The simplest method is to format your update statement to include the datetime when the record was last updated. For example:
UPDATE my_table SET my_column = new_val WHERE last_updated = <datetime when record was pulled from the db>
This way the update only succeeds if no one else has changed the record since the last read.
You can message to the user on conflict by checking if the update suceeded via a SELECT after the UPDATE.