Access front end, SQL Server back end.
Simple update query
PARAMETERS ParamTransactionID Long, ParamVoidFlag Short;
UPDATE tblTransaction
SET tblTransaction.VoidInProgressFlag = [ParamVoidFlag]
WHERE (tblTransaction.TransactionID=[ParamTransactionID]);
using the query here
Set qdef = CurrentDb.QueryDefs("qUPD-tblTransaction_VoidInProgress")
qdef.Parameters![ParamVoidFlag] = VoidFlag
qdef.Parameters![ParamTransactionID] = TransactionID
qdef.Execute dbFailOnError + dbSeeChanges
qdef.Close
gives
[Microsoft][ODBC SQL Server Driver]Query timeout expired
ODBC--update on a linked table 'tblTransaction' failed.
Editing the table directly works.
Opening the query and giving parameters works.
From the app still doesn't.
UPDATE
Deleted the view, no affect.
The old version is now getting the same failure, so it seems like it is not a code issue.
The only thing in common is the table, so it might be a minor change I made there.
I'll check and see if it is just that table or the entire database.
But seems odd that I can make the change by running the query directly, but get different results running it from code.
UPDATE 2
I thought that perhaps it was something to do with the entire database being read-only somehow, and this is just the first place it is getting hit. But no. Other forms can update their tables with no issues.
So it looks related to the specific table. But it still seems odd that I can update perfectly fine by just running the Update Query.
UPDATE 3
To make testing easier, I am running the queryfrom the main menu form, instead of going through all the forms to get to the point where it fails.
Running against the original DB schema worked. Made the same changes again, replacing NTEXT with VARCHAR(MAX), and it still works.
Back to the original table, still works.
Go back through all the forms, fails.
So the problem seems to be related to one of the forms that is open.
I'll go back through that sequence again.
Also, this explains why it works from the query and not from the form.
Sadly, I can't get to the query to run that while the form is open.
Ok, the first question is why/how did you wind up with "Short" data type for the parameter?
It should be:
PARAMETERS ParamTransactionID Long, ParamVoidFlag Bit;
UPDATE tblTransaction
SET tblTransaction.VoidInProgressFlag = [ParamVoidFlag]
WHERE (tblTransaction.TransactionID=[ParamTransactionID]);
You also state that this query works when you run it from the UI.
So, in code, then you have this:
Make sure that ALL code modules have option Explicit at the start like this:
Option Compare Database
Option Explicit
So, your code should now be:
Dim qdef as DAO.QueryDef
Set qdef = CurrentDb.QueryDefs("qUPD-tblTransaction_VoidInProgress")
qdef.Parameters![ParamVoidFlag] = VoidFlag
qdef.Parameters![ParamTransactionID] = TransactionID
qdef.Execute dbFailOnError + dbSeeChanges
So correct the data type you have for ParmaVoidFlag.
Also, check the table name in the left nave pane - is it
dbo_tblTransaction
or
tblTransaction.
Also, OPEN UP the linked table in design view - (ignore read only message). Look at the data types. You MUST have a PK defined - so check for a PK.
Next up, on sql server side, true/false fields MUST NOT have a default of null. If they do then updates will fail. So in sql server, make sure the table in question has a default of 0 set.
Last but not least?
If the query still errors out, then you need to add a timestamp (not date time) to the sql server table and re-link. This will/is required if any columns are floating point.
After you add the option explicit to the start of the code module, then also from menu do a debug->compile - make sure code compiles.
It turned out that the query failed if a particular form was open.
That form queried the same table, but with Snapshot instead of Dynaset.
I don't know why that locks the table. It has a proper key and index. But Dynaset fixes it.
Related
Two related questions. I hope that's OK. If not, please feel free to focus on the second question!
I have an Access 2019 database form that uses a macro for search functionality. The form recordsource is based on two tables using a left join. Any macro options that use ApplyFilter based on any fields in the joined tables operate correctly and quickly.
However, I need a search to use a subquery and for some reason the Macro Where Condition does not support a sub query (it shows a red exclamation point and gives an error when trying to save the macro "The 'ApplyFilter' macro action has an invalid value for the 'Where Condition' argument").
The Where Condition is:
JobPropertyID in (select PropertyID from Properties where PropertyAddress like '*' & [Address contains] & '*')
(I have tried various combinations of % and * wildcards, and quotes).
This used to work in earlier versions of Access (we upgraded from 2003 to 2019).
So, question 1 is - Is this a known limitation?
(I can work-around it by using RunCode to set the Filter and FilterOn in VBA code).
The second, and more important question relates to the performance when using a sub query. For example, this pseudo query to return jobs at matching property addresses:
select JobID, JobDescription, CompanyName from JobDetails Left Join Company on JobCompanyID = CompanyID where JobPropertyID in (select PropertyID from Properties where PropertyAddress like '*High Street*')
This does work but can take about a minute to run in Access. If I run the query in SQL Server Management Studio it shows the results in less than a second. I looked at the output in SQL Profiler and it appears that Access is requesting all rows from the joined tables and all rows from the Properties table (with no criteria being applied to either) and then, presumably, applying the filter and the sub query internally.
Is there a way to encourage Access to let SQL Server do the work?
I have tried using a pass through query, and this returns the correct results quickly, but is read only, so not suitable for a form that allows editing.
I suppose I could display the search results in subform and apply a filter to the main form from the OnCurrent event in the subform. But this seems a rather clunky solution.
Any thoughts welcome.
Access tends to like Exists over In a lot when it comes to performance/translation, and that second query can be rewritten to an Exists:
select JobID, JobDescription, CompanyName from JobDetails
Left Join Company on JobCompanyID = CompanyID
where Exists(select 1 from Properties where PropertyAddress like '*High Street*' AND JobPropertyID = PropertyID )
As for that filter, you can pass a filter string to avoid the macro from doing weird stuff:
="JobPropertyID in (select PropertyID from Properties where PropertyAddress like '*' & [Address contains] & '*')"
But you should really consider using VBA for your own sanity (working with macros is intended for beginners but quickly becomes harder than learning VBA even when doing trivial tasks), unless that's restricted in your company, in which case you will run into obstacles using Access.
Any client side filter from Access? One table - works great.
A left join - usually ok!
but, after that ? The problem is that Access (and most) ODBC connections view EACH table as a 100% separate link. While your linked tables point to SQL server? They don't have to!
So, one table could point to say a local FoxPro/dbase table.
Another table could point to a linked Excel sheet.
And another table could point to sql server.
So, if you try and use any compilex sql - involving joins, or ESPECIALLY sub-quires, then Access really can't make the assuming that these additional tables are from the same server - and they might be Excel sheets!! - So, those data sources are "often" seen and assumed by access to be seperate tables. (and while Access is amazing that you can say do sub-queries against to linked Excel sheets? Well, there no server on the other end - Access client side has to figure this out to make it work.
You have two great choices to fix this issue.
the best choice? Build that query server side (in SSMS stuido). Save it as a view. Now the joins and all that jazz will occur 100% server side. You can STILL very effective filter against that "table" in Access. You of course now will have a linked "view" in access. But access sees this as one table - filters work fantastic in this case.
And in a lot of cases, that sub-query can be re-wrtten as a left join (not always - but often so). So again, build that complex join server side, save it as view, and then from Access link to that view.
You can filter against columns in both parent and child table - it again works well.
However, if you really need that sub query and worse so that sub query needs values/paramters from the child table?
Well, then build the query but use what is called a Pass-though query in Access. When you do this, the sql is sent raw un-touched to sql server (no difference in speed, and what you put in that PT query will work 100% exactly the same if you just typed that SQL right into SSMS. So, for example, that query that ran so fast?
Take it 100% "as-is", un-changed. Build a PT query in Access, and then cut+paste in that working and speedy query you had. It will now run the same in Access. Just keep in mind that a PT query in access is read only, but often with sub-query etc., such queries would be anyway.
The next last one? Well, with a PT query in Access, you can use those to call + use + consume a stored procedure. And stored procedures are often used because this lets you EASY set parameters in say a sub query (not that you ever really needed say t-sql code, and some big procedure to run - but you need paramaters - and a SQL stored procedures give you this ability.
in VBA, to execute that stored procedure? Well, I create a working PT query, (could be anything or any T-SQL). I then use that PT query over and over in code.
eg like this:
dim rstData as DAO.RecordSet
With Currentdb.QueryDefs("MyPTQuery")
.SQL = "EXEC dbo.GetInvoices"
set rstData = .OpenRecordSet()
End if
Now above, the stored procedure (to get say invoices) did not have any parameters, but it can and could. So, say you have to pass the year, and month to get invoices.
So, your PT query could/would be used like this (from SSMS).
EXEC dbo.GetInvoices 2021, 3
So, you have to pass it the year + month. IN VBA, then we could go:
dim Mymonth as integer
dim Myyear as integer
MyMonth = Month(date) ' get current month based on today date
Myyear = Year(date) ' get current year based on today date
With Currentdb.QueryDefs("MyPTQuery")
.SQL = "EXEC dbo.GetInvoices " & MyYear & "," & MyMonth
rst = .OpenRecordSet()
End if
And you not limited to shoving the results into a recordset.
Say, we have a report - set the source of the Report to "MyPTQquery"
Then do this:
With Currentdb.QueryDefs("MyPTQuery")
.SQL = "EXEC dbo.GetInvoices " & MyYear & "," & MyMonth
End if
' now open report
docmd.OpenReport "rptInvoices",acViewePreview
So that query can be used for anything - reports, forms or a VBA recordset.
So I find that views are the most easy, perform REALLY good, and you can even have criteria against that view, and it works very well. (so this is the least amount of work). So built that messy multi-table join server side, save as view, and then link from Access client side. This works VERY well, since often you can take a REALLY big dog pile query in Access with multi-table joins - it not going to work well.
So, you take that messy query, move it to SSMS - get it working. Then save that working SQL as a view. Now re-name (or delete) the query. Say it was
QueryInvoices.
Well, now link to the view with the same name. now that VBA code, the forms, and the reports that were ALL using that messy client side query don't have to be changed!! - the name of the server side view is thus linked with that old query name. Again this gives fantastic performance.
So, the above should give you some ideas. As noted, views are your friend here. But as noted, the problem is you can't inject/have parameters in the sub query from Access being passed to a view. So, you can either build a stored procedure - use above PT Query idea.
And the other way? Well, you build the whole SQL string in VBA code, shove it into that PT query - and that also works. (but in-line messy long sql statements in VBA code can be quite a challenge. But for parameters in a sub-query, then the view trick can't be used, so you travel down the PT query road.
I'm getting the error message:
This record has been changed by another user since you started editing it. If you save the record, you will overwrite the changes the other user made...
I know this is a common question and I've spent hours researching and testing but to no avail. A few notes:
There are no bit fields anywhere in my database
All tables have a primary key, data type = identity
All tables have a create/modified timestamp trigger on updates and insert
I'm fairly certain that the problem has to do when the form (and multiple subforms) are creating the identity fields and/or the timestamp triggers. Specifically, I only get this error on the "Individual Fish" table when I go back to edit an old 'fish' (as shown on the screenshot). If I just tab through the form and don't make any edits, it works fine. But if I need to edit anything on a previous 'fish' - after the identity / trigger fires - then it gives me the error.
I've gone through and added If Me.Dirty Then Me.Dirty = False End If to every form for the following events:
On Current, On Load, On Click, After Update, Before Update, Before Insert, On Dirty.
I also added DoCmd.RunCommand acCmdSaveRecord to On Deactivate. I will admit that I am not great at VBA, so there could be something silly I did here. Code attached. I've also messed around with Record Locks = Edited Record.
So far nothing seems to work. Please let me know if you think I'm missing something. Also, if you have any random comments or suggestions about my database design or anything else, I always welcome feedback.
Thanks!
UPDATE:
Albert's answers got me to the right place. The short version is to add a rowversion (aka timestamp) field to all tables. This was mentioned on several other posts, but i didn't realize the [awfully named] "timestamp" didn't actually have to do with date or time. Thanks for the help!
]3
Ok, lots of things here to check. First of all, placing a me.Dirty = false in on-current, or events like before update will cause the "same" event to try and trigger again. You really (but really really) don't want to do this. (so wild random tossing in of me.dirty in those events will only make this issue much worse and often cause the very same event to trigger again.
next up:
All tables have a create/modified timestamp trigger on updates and insert
Ok, now the above is confusing. Do you have an actual trigger - as that is a separate issue and will often trigger the record been modified by someone else.
Also when we speak of a timestamp column, do keep in mind that such columns have ZERO ZERO ZERO to do with datetime. Over the years Microsoft has attempted to "change" the name used. The correct name is ROWVERSION, and this column is NOT a datetime data type column, but is called timestamp. Do NOT in ANY WAY confuse this rowversion system/column with that of a datetime column.
So, the assumptions are:
You have a timestamp column - this is of data type timestamp. This column is NOT touched by your code or trigger in ANY WAY. This column is NOT of datetime, nor of datetime2, but is of data type timestamp.
If you don't have a actual timestamp column here (it does not need to be on the form), then you will get constant "dirty" warnings. You also get this with any real data type columns - especially if they are set by server code. (access will round differnt).
Bottom line:
You need a actual rowversion column (of type timestamp) in that table. Behind the scenes if access does NOT find this column, then it will do a column by column compare, and without question with a trigger to set some LastUpdated column with GETDATE() on the server side trigger, then this will cause nothing but problems. Introduction of a timestamp column will STOP access from doing the column by column compare after a update, and it will ONLY look at the timestamp column. So, your trigger can now update the LastUpdated, and the timestamp column should not change from access points of view.
So, you need to be sure:
A PK column is seen by access - all sql tables need a PK.
All tables should have a rowversion column.
If you do add a timestamp (rowverison) column to the problem table, then make sure you re-link on the access client side. In fact after ANY table change or modifications server side, then you should re-link the client side.
I would remove any stray me.Dirty = False code in that form.
You can place a "save" button on the form if you wish, and simply have it go
if me.dirty = true then me.Dirty = False
Edit
With the above setup, you should be able to re-introduce your server side trigger that sets the LastUpdated. However, you not want any code in the form that "touches" or uses that column. You can however should be able to drop in that LastUpdated column into the form and see it update after you save.
Bottom line as I have run across this error on an upgrade of SQL Server to 2016, due not assume "timestamp" is of data type "datetime". It is not. The data type that Access requires is of type "timestamp". Add a column with that data type to any table that is editable through Access and that will clear the "Write conflict with grayed out save button" message.
Summary
In a Microsoft Access 2010 database (accdb), I have a form that dynamically loads other forms into a subform object on the main form. The forms used in the subform object are bound to ODBC pass-through queries that execute stored procedures to return recordsets. I can't figure out why I can use one sproc and it works perfectly fine, but if I bind the form to another sproc, it fails to load the subform.
Technical Walkthrough
I have two pass-through ODBC queries. qryGood and qryBad. They use identical ODBC connection strings (ODBC;DRIVER=SQL Server;SERVER=MyServer;UID=MyUser;Trusted_Connection=Yes;DATABASE=MyDatabase), and the SQL behind them is identical, but pointing to two different SQL stored procedures on the SQL 2012 database server.
qryGood source: exec spGoodProc 123456
qryBad source: exec spBadProc 123456
The SQL behind the sproc is very simple. Return records from a single table, filtering by the ID passed as a parameter. (Some will do more complex things, but I am just focusing on a simplified example here that demonstrates the problem.)
The RecordSource property of frmMySubform is set to qryBad.
The subform SourceObject is set via VBA code: sfrmMain.SourceObject = "frmMySubform" No errors are thrown at this point. While the SourceObject property now returns frmMySubform, the .Form object does not seem to be set.
I then try to reference a property on the subform: Debug.Print sfrmMain.Form.Name This fails with error 2467: The expression you entered refers to an object that is closed or doesn't exist.
I can then open frmMySubform in design view, change the RecordSource property to qryGood and it works just fine. This seems to point to a problem with spBadProc that only manifests itself when used as the RecordSource on a subform.
What I Have Tried
In an effort to troubleshoot this problem, I have used the process of elimination to narrow this down as far as I can, but I am still not understanding why the one sproc works and the other doesn't. Both return records just fine in SQL and when running the pass-through query directly. Both work fine when opening the form directly. It only becomes a problem when the form is set as a SourceObject in a subform control.
I have used sp_procedure_params_rowset to compare the parameters in the sprocs, and they are identical. I have compared the data types of the columns in SQL and there is nothing new or different in tblBad that isn't in tblGood. I have also tried profiling the SQL server while setting the form, and it seems to call the sproc just fine. I didn't see any clues when comparing the trace between the bad and the good calls.
Setting the RecordSet directly to an ODBC link to tblBad works just fine (and I presume a view would be fine as well) but having the simple stored procedure wrapper somehow triggers the error.
I have also compared the security, properties and extended properties for spGoodProc and spBadProc and they are identical.
My Question
What can I do on the troubleshooting side to reduce this down further? Has anyone out there encountered similar issues with bound sprocs on subforms? I am working on a very complex database with hundreds of forms, tables and queries, so I would really like to understand why this is occurring before I go too far down this path.
Thanks in advance for any insight you are able to share on this perplexing problem. :-)
Found it!
After tracing it back to something with the specific table, I removed all constraints, keys, and then columns from a copy of the table, systematically testing to see if I could pinpoint the problem. Sure enough, it was a specific column name in the stored procedure!
Simply aliasing this column to a different name solved the problem. (See below for expanded details)
Update after Further Testing
After additional testing to further pinpoint the issue, I think I now understand why this was occurring. When you link an ODBC table and specify a unique (key) column, Access will automatically attempt to set the LinkMasterFields and LinkChildFields to the key column name when a subform is loaded and the subform has a column with the same name. While this works fine with linked tables or views, it does not work when the RecordSource of the subform is set to a stored procedure.
If you attempt to do this by manually adding the subform, you will see the following notification:
However, if you set the subform target through VBA code, you don't get any warning or error message. It simply doesn't (fully) load the subform. #Albert D. Kallal, you were right on about this being related to a master/child fields issue!
I was able to consistently reproduce the issue in a test database file in both Access 2010 and Access 2016. If you would like to see this for yourself, you can use the following steps to reproduce it:
Create a SQL table with a PrimaryID column.
CREATE TABLE [dbo].[tblBugTest](
[PrimaryID] [int] NOT NULL,
[TestColumn] [nchar](10) NULL
) ON [PRIMARY]
Add a couple test records to the table you just created.
Create a Stored Procedure to return the records.
CREATE PROCEDURE [dbo].[spBugExample]
AS SELECT * FROM tblBugTest
Create a blank Microsoft Access database (accdb).
Using ODBC, create a linked table to tblBugTest.
Important: Select PrimaryID as the unique column.
Create a pass-through query named qryPassThrough using ODBC to the same database, and set the SQL to exec spBugExample.
Open the query to verify that it returns records.
Create three blank forms in the database. frmMain, frmSubForm, and frmBlank.
Add frmBlank as a subform to frmMain. Name the subform sfrmSubform.
Set the RecordSource of frmMain to the linked table.
Add a button to frmMain to switch the subform from frmBlank to frmSubForm.
Private Sub cmdShowBug_Click()
With Me.sfrmSubform
.SourceObject = "frmSubForm"
Debug.Print .LinkMasterFields
Debug.Print .LinkChildFields
Debug.Print .Form.Name
.SourceObject = "frmBlank"
End With
End Sub
Set the RecordSource of frmSubForm to qryPassThrough.
Drop a couple bound controls onto frmSubForm.
Test frmSubForm by itself. It should load a record from tblBugTest.
Open frmMain and click the button. It should throw an error.
If you step through the code, you will see that before setting the SourceObject, the LinkMasterFields property is blank. After setting SourceObject, you can hover over LinkMasterFields and see that it is now set to the PrimaryID column.
Workarounds
Changing any of the following will work around the error by avoiding the problematic auto-linking of the master/child fields.
Delete and relink the linked table, this time not specifying a unique column.
Alias the column in the Stored Procedure to a different name than the unique column.
Clear the RecordSource property of the parent form.
Clear the subform RecordSource property and set the RecordSet after loading the subform.
Use a view or linked table instead of a stored procedure in the subform.
Keep in mind that the subform data source will be attempted to be loaded BEFORE the main form loads. What this suggests is that on the main form's load event, you will
First setup the PT query.
Then set the OBJECT source of the sub form.
In other words, the source object of the sub form control should be blank.
You code then to set up the PT query will be:
With currentdb.queryDefs("qryGood")
.SQL = "EXEC spGoodProc " & 123456
end with
Of course you can replace the 123456 with a varible, or even a value from a text box (from the main form).
Now that the PT query is setup, you THEN are to set the form that the sub-form is to load.
So, after above code, we then have:
me.mySubForm.SourceObject = "name of subform goes here"
So, it should be about a total of 4 lines of code. And as above shows, you don't even need any connection string stuff in your VBA code.
So, just keep in mind:
Setup the PT query as per above. You can then launch a report, or even a form, or in this case set the form that the sub-form control is to load. This also suggests/hints that you need to remove the source object of the sub form control (leave it blank).
You can have the sub-from source object set, but then this would suggest that you setup the PT query source as per above BEFORE you launch the main form with the sub form based on the PT query. As noted, this set of steps is required since the sub form actually load and resolves it data source BEFORE the main form displays and renders. So, by leaving the source-object blank for the sub form, then you the developer re-gains complete control over the order of loading.
I have an Access 2010 database that's linked to a SQL Azure backend (yes, I know this isn't ideal, and it's being slowly phased out). On the backend, I have a stored procedure that I want to use to populate a read-only subform each time a new record is loaded. I'm attempting to do this by generating a recordset in VBA, then setting the subform's RecordSet property. It actually works, but with a nasty side-effect.
When I set the RecordSet property, it also seems to be setting the RecordSource property of the subform. That RecordSource is something Access can't parse, because it's meant to be a call to the backend. If I try using a DAO passthrough query to generate the recordset, the RecordSource looks like:
EXEC dbo.GetDuplicateAddressesByManufacturer N'...', N'...', N'...'
If I try using an ADO command to generate the recordset, it looks like:
{ call dbo.GetDuplicateAddressesByManufacturer ?, ?, ? }
As soon as I try to move to the next record, Access throws an error because it tries to load a new record for the subform first, and it can't open what it sees as the subform's RecordSource. If I'm trying the DAO route, it tells me "Invalid SQL Statement", and if I'm using ADO, it tells me "Data provider could not be initialized."
Anyone have an idea how I can get around this? Isn't there a way to set the RecordSet property without also setting the RecordSource? I could swear I've done that before, but maybe I've just never noticed that the RecordSource gets set too.
Failing that, is there a way I can interject some code before the Form_Current event to remove the subform's RecordSource? The code I'm using to set the RecordSet each time works great -- the problem is the error that's raised before my code works. Once I dismiss the error message, everything works fine, but obviously I don't want users to get an error message each time they change records.
If all else fails, I guess I could always use the query to populate a local temp table, but it seems like a lot of overhead to do every single time someone moves to a new record.
Why bother with a stored procedure? Just link the sub form to the table, and setup the link master/child settings. You only pull down the required records.
If the sub form is a complex query with multiple tables, then you certainly want the data joins etc. to occur server side and AGAIN simply create a view and again set the sub form source to that view (and again the link master/child settings will do all the dirty work for you).
And there no reason why you cannot create a pass-through query and SIMPLE assign that to the forms recordSource.
It does not matter what “junk” you place inside of the query, and that includes RAW T-SQL.
And while you can load up the DAO reocrdset with this pass-thought, you really don’t need to. I suppose for some happy reason you are doing this, and at least if you must, then recordSoruce becomes the name of the pass-though and NOT your raw T-SQL anyway.
However, really, just dump all that recordset junk, and just go:
Me.MySubForm.Form.RecordSource = "my pass though query".
Thus above is only one line.
You doing all these hand stands to increase performance then at the end of the day why does your main form allow navigation? You should build a search screen, display the results, let user pick a row and THEN launch the main form to edit/display the ONE record along with the sub form data.
When they close, they are back to searching for the next customer etc. This approach also thus solves your messy navigation issues. It also why the web and most software works this way (it reduces bandwidth issues).
However, if you must have navigation, and for some reason CANNOT use a view and cannot let Access use the link master/child settings to do your dirty work?
Then in the forms on-current event you can modify the pass-though and simply re-assign it to the sub form.
Eg:
With CurrentDb.QueryDefs("qPass")
.SQL = "select * from FaxBook3 where id = 3"
End With
'
Me.RecordSource = "qpass"
Now how in the above I am using RAW T-SQL in the pass-though query, and then simply assign the pass-though to the forms recordsource (in your case you assign to the sub form.
Me.MySubForm.Form.RecordSource = above
And there NO reason why the above .SQL cannot be your stored procedure
.SQL = "Exec your-storedProcedure " & strVbaStringParmater
And again assign the form (or sub form) recordSource.
So you REALLY do not need to create some reocrdset in code and it not yield you any performance increase, but will cause you to write more code and have problems as you outlined in your post.
I am using SQL Server Management studio and keep getting the same error, and the only way to get rid of it(usually) is to reset the SQL server(which is very annoying, and sometimes impossible from my remote machine)
When I add a row to a table, and then I goto "Edit Top 200 Rows" it all displays and acts fine, and I go to a field I want to change. Then I change something like 0 -> 1 and then I get a nice friendly popup saying "Data has changed since the Results Pane was last retrieved... Optimistic Concurrency Control Error" If from here I say "Yes to commit changes to database anyway" I get "No row updated... The updated row has changed or been deleted since data was last retrieved"
It's a very annoying little thing, cause I don't like having to look up RIDs and then make an update statement(and possibly having to worry about escaping 's by hand)
Is there some way to turn this concurrency checking off or something? I know the row wasn't updated or anything, and I tried completely closing Sql Server Management Studio and reopening to no avail, and also tried refreshing the result pane, or refreshing the column view. Nothing gets rid of this error, but if I do a "update ... set ...=..." then it works, so I'm not really having any concurrency error..
I had exactly the same problem. It looks like this article was pretty good at solving it. Seems all sorts of buggy things in some versions.
See: You may receive an error message when you try to use SQL Server Management Studio to update a row of a table in SQL Server 2005.
The table contains one or more columns of the text or ntext data type. The value of one of these columns contains the following characters.
Percent sign (%)
Underscore (_)
Left bracket ([)
The table does not contain a primary key.
http://social.msdn.microsoft.com/Forums/en-US/sqldatabaseengine/thread/7bf48a75-58a0-41d7-b514-b804a49ae8ff/
it seems to be a bug in SSMS I don't think I have over 4000 characters, but I can confirm this only happens on rows that have more data than others.. there seems to be some abritary limit that I can't quite put my finger on..
So, plainly SSMS is complete crap. I'll be looking for a new SQL manager..
You shouldn't edit a table directly from the table view.. you should use an UPDATE sql command.