Access Error "This Recordset is not Updatable" having SQL Server backend - sql-server

I have a form that displays a list of clients. The form contains 3 combo boxes and others are Text boxes. So when I add the tables to the query which contains the data that goes into the combo box, it doesn't work but when I remove the table the query becomes Updatable. I have attached the images of both the queries.
Updatable Query
SELECT [1-01_Clients_tbl].CNR,
[FNC] & " " & [SNC] AS [Service User Full Name],
[1-01_Clients_tbl].PT AS [Physiotherapist name (adjust)],
[1-01_Clients_tbl].[PDS Score (txt)] AS [Score (txt)],
[1-01_Clients_tbl].[PDS Score (nmbr)] AS [Score (No)],
[1-01_Clients_tbl].[Date for WLI],
[1-01_Clients_tbl].DOB AS [Date of Birth],
[1-01_Clients_tbl].TASCode,
[1-01_Clients_tbl].PTact AS [Active Status Physio Details (adjust)],
[_Circular_Temp].ActiveStatus AS [Active Status APH]
FROM ([1-01_Clients_tbl]
LEFT JOIN [1-01-Clients_TransferXtra_tbl] ON [1-01_Clients_tbl].CNR = [1-01-Clients_TransferXtra_tbl].CNR)
LEFT JOIN _Circular_Temp ON [1-01_Clients_tbl].CNR = [_Circular_Temp].CNR
WHERE ((([1-01_Clients_tbl].CNR)<>1 Or ([1-01_Clients_tbl].CNR)=2)
AND (([_Circular_Temp].ActiveStatus)="yes"));
Non-Updatable Query
SELECT [1-01_Clients_tbl].CNR,
[FNC] & " " & [SNC] AS [Service User Full Name],
[1-01_Clients_tbl].PT AS [Physiotherapist name (adjust)],
[1-01_Clients_tbl].[PDS Score (txt)] AS [Score (txt)],
[1-01_Clients_tbl].[PDS Score (nmbr)] AS [Score (No)],
[1-01_Clients_tbl].[Date for WLI],
[1-01_Clients_tbl].DOB AS [Date of Birth],
[1-01_Clients_tbl].TASCode,
[1-01_Clients_tbl].PTact AS [Active Status Physio Details (adjust)],
[_Circular_Temp].ActiveStatus AS [Active Status APH],
[5-10_TeamActiveStatus_Codes_tbl].TextVisible,
[2-01_TeamIDNormalized_tbl].CTeamID
FROM (((([1-01_Clients_tbl]
LEFT JOIN [1-01-Clients_TransferXtra_tbl] ON [1-01_Clients_tbl].CNR = [1-01-Clients_TransferXtra_tbl].CNR)
LEFT JOIN _Circular_Temp ON [1-01_Clients_tbl].CNR = [_Circular_Temp].CNR)
INNER JOIN [5-10_TeamActiveStatus_tbl] ON [1-01_Clients_tbl].CNR = [5-10_TeamActiveStatus_tbl].CNR)
LEFT JOIN [2-01_TeamIDNormalized_tbl] ON [1-01_Clients_tbl].CNR = [2-01_TeamIDNormalized_tbl].CNR)
INNER JOIN [5-10_TeamActiveStatus_Codes_tbl] ON [5-10_TeamActiveStatus_tbl].TeamActiveStatusCode = [5-10_TeamActiveStatus_Codes_tbl].TAScodeID
WHERE ((([1-01_Clients_tbl].CNR)<>1 Or ([1-01_Clients_tbl].CNR)=2)
AND (([_Circular_Temp].ActiveStatus)="yes"));
A) Images of Updatable Query
1) The Query
2) Datasheet view of the Query
3) Design view of the form associated with the above query
4) The Actual working form
Note that CTeamID(Team column in the form) and Text Visible(Team Active Status column in the form) is missing as these data is derived from two different tables and that's where the issue starts.
B) Images of Non-Updatable Query
1) The Query
2) Datasheet view of the Query
3) The Form
So here when I added the tables from where we are getting the CTeamID(Team column in the form) and Text Visible(Team Active Status column in the form) data it's now not updatable query.
Any ideas or suggestions as to how to make it working or how to improve the query to make it updatable? Thank you in advance.

Well, it looks like the introduciton of the "inner join" is what blows up this query.
however, I would consider building the query in sql server, and linking as a view.
However, DO KEEP in mind:
Access based tables allow joins and MULTIPLE tables to be updated.
SQL server:
You can have a query (or better a view) that has mutliple tables, but ONLY ONE of the tables can be changed.
In other words, if you edit some columns that belong to MORE then one table, then this is NOT allowed with SQL server tables.
Again:
The view can have multiple tables, but if you edit a column from MORE then one different table, the update does not work with SQL based tables. (it does work with Access based ones).
So, unless you can "disable" some controls on that continues form/datasheet to "limit" or to "ensure" that ONLY ONE BASE table behind will become "dirty", then you can't use that interface. This is a limitation of SQL server, and one that does not (did not) exist when using Access tables as the back end.
SQL server due to transactions, and being ATOMIC does NOT allow a query to have more then one table AND ALSO then udpate in one shot/command.
MS-access tables do allow this!
You can in some cases "kluge" this by using a after update event of the text boxes, and do a me.dirty = false (which forces a write of the record). Since you would be "forcing" a write of the data, then NEVER does more then "one" table in the query get dirty.
So, this is a difference in SQL tables vs Access tables. (only ONE table in the view/query behind can EVER become dirty).
So, by forcing the write of the row data in the text box after update, then you can make this work.
I would thus using a continues form (and not a datasheet) for this purpose (and by continues form, I do mean what is called a multiple-items form.
So, while you can/could/probably get that 2nd query to work (you have to change the inner join to a left join), the NEW issue will be that if you allow editing in that row that will result in more then ONE table becoming dirty, then the update will not work.
So, you either:
Have to be sure ONLY one table behind in that query is ever changed.
Or, do the kluge of adding me.dirty = false in after update of each text box.
or, consider having a button that pops up a form, and only has the columns from one base table behind in that query for allowed edits.
So, just keep in mind that after using up a pot of coffee, and say you REALLY do get that query to become up-dateable?
Keep in mind that rule about ONLY ONE of the tables behind can become dirty at any given time. This means/suggests that while editing could occur in that row, only columns from ONE of the tables behind at a time can become dirty.
So, as noted, I would consider converting that client side query into a view, since that's going to help a WHOLE LOT in operation of that query.
And thus in sql manager, you can then right click on the view, and choose edit. Test it. That way you don't have to go back into ms-access when trying to determine if the query can/does allow updates.
Do keep in mind that you MUST answer the prompt when linking the view to enter the row PK id for access. Keep in mind that if you re-link (point the front end) to a different back end from SQL server? Then the PK row value of the linked view can and will be lost. This issue can be dealt with in a separate question/post, but you do need to keep this additional information in mind.

Related

SQL Views vs. MS Access queries --- Updating data affects multiple base tables

I'm interested in understanding more about using a SQL View vs. a local query in MS Access. I like the fact that a view is basically a query that is stored on the server, and local machines running Access "see" it as a table.
Due to performance reasons, I'll sometimes take a view over a query since it typically makes a form load a lot faster. However, I've run into issues where I can't update the view if I make changes in two different fields that are in different base tables. Even if the view is constructed correctly with the correct joins, etc.
Just wondering if there is a more efficient and proper way to construct a query that can be updated.
A user can never update more than one table at a time. That's a given. You need to construct your form (probably using subforms) to represent the data using either single table views, simple views that are updatable, or tables.
Subforms are basically left joins to the parent form. like
SELECT *
FROM ParentForm P
LEFT JOIN SubForm S
ON P.ParentID <~~Link Master Field
= S.ParentID <~~Link Child Field
So you can recreate your view using subforms.
If your view is too complicated to fit this mold it is probably not updatable and it probably means that the data you DO want to update are in a single table but all the rest of the info in your view are supporting information. i.e. displayed to support the user making a decision.
In this case you should make the Record Source of your form be the table/view (which is updatable) that you want to update. Then in comboboxes/listboxes/controls which support the data going into your updatable table/view you make the Row Source that of your complicated view.
No matter where the view is (in Access or on the server) if it is constructed in such a way that it is impossible to determine which record in which table should be changed, nothing else matters. YOu need to design the whole form differently.

Large inserts from app - performance

I am having to create a custom tool in .NET to data migrate and cleanse an existing system. And it also does some other things.
I have literally a table of around 2000 users and then for each of those users, they can have anything from 0 to 9,000 "customer accounts"
then for each of these users and their accounts, they will need to insert a menu system into another table for that user and account.
So I am having to go through all these objects created within the app, and execute an insert statement. The problem is, the performance is horrible. It took 3.5 hours to complete the inserts and each insert took around 3 seconds to complete.... pretty slow!
With the inserts, I am making sure that I dont insert duplicate data/make sure there isnt any existing duplicate data thus my insert statment:
IF NOT EXISTS (SELECT ID FROM UserWebAccessLevel WHERE UserID = '{0}' AND CustomerID = '{1}' AND MenuID = {2}) BEGIN INSERT INTO [WebAccessLevel] (UserID, CustomerID, MenuID) VALUES ('{0}', '{1}', {2}) END
doing this for each user, for their customers and for each menu item found within the app logic.... yeh, takes a long time.
im wondering how I can better enhance the performance of the insert statement?
Thank you
Without an index on your table, the SELECT ID FROM... could be slowing things down, as SQL Server has to look at each record one by one and compare the IDs in the WHERE clause. By adding an index containing the three IDs, SQL Server will be able to (more-or-less) instantly locate an existing record when a WHERE clause specifies the three IDs.
Assuming you are using SQL Server Management Studio:-
Right-click the table then click "Design".
Right-click where the list of columns has appeared, then click "Indexes/Keys".
in the next dialog that appears, click the Add button
In the list of properties on the r.h. side, find the one called "Columns" and click the box to the right of it. When you see the ellipses button ("...") click that.
In the next dialog that appears, add your three ID columns.
An index doesn't have to be unique. In the "Indexes/Keys" dialog you get the option to specify whether it should be unique or not.
The only other thing I can suggest checking is in your C#/VB code that executes the IF NOT EXISTS... SQL - make sure you aren't opening and closing the connection each time. That will really slow things down!

retrieve data from multiple tables and store them in one table confronted the duplicates

I'm working on some critical data retrieving report tasks and find some difficulties to
proceed. Basically, it's belonging to medical area and the whole data is distributed in
several tables and I can't change the architecture of database tables design. In order to
finish my report, I need the following steps:
1- divide the whole report to several parts, for each parts retrieve data by using
several joins. (like for part A can be retrieved by this:
select a1.field1, a2.field2 from a1 left join a2 on a1.fieldA= a2.fieldA ) then I can
got all the data from part A.
2- the same things happened for part B
select b1.field1, b2.field2 from b1 left join b2 on b1.fieldB= b2.fieldB, then I also
get all the data from part B.
3- same case for part C, part D.....and so on.
The reason I divide them is that for each part I need to have more than 8 joins (medical data is always complex) so I can't finish all of them within a single join (with more than 50 joins which is impossible to finish...)
After that, I run my Spring Batch program to insert all the data from part A and data from part b, part c.. into one table as my final report table. The problem is not every part will have same number of rows which means part A may return 10 rows while part b may return 20 rows. Since the time condition for each part is the same (1 day) and can't be changed so just wondering how can I store all these different part of data into one table with minimum overhead. I don't want to have to many duplicates, thanks for the great help.
Lei
Looks to me like what you need are joins over the "data from part A", "data from part B" & "data from part C". Lets call them da, db & dc.
It's perfectly alright that num rows in da/b/c are different. But as you're trying to put them all in a single table at the end, obviously there is some relation between them. Without better description of that relation it's not possible to provide a more concrete answer. So I'll just write my thoughts, which you might already know, but anyway...
Simplest way is to join results from your 3 [inner] queries in a higher level [outer] query.
select j.x, j.y, j.z
from (
' da join db join dc
) j;
If this is not possible (due to way too many joins as you said) then try one of these:
Create 3 separate materialized views (one each for da, db & dc) and perform the join these views. Materialized is optional (i.e. you can use the "normal" views too), but it should improve the performance greatly if available in your DB.
First run queries for da/b/c, fetch the data and put this data in intermediate tables. Run a join on those tables.
PS: If you want to run reports (many/frequent/large size) on some data then that data should be designed appropriately, else you'll run into heap of trouble in future.
If you want something more concrete, please post the relationship between da/b/c.

The setting 'auto create statistics' causes wildcard TEXT field searches to hang

I have an interesting issue happening in Microsoft SQL when searching a TEXT field. I have a table with two fields, Id (int) and Memo (text), populated with hundreds of thousands of rows of data. Now, imagine a query, such as:
SELECT Id FROM Table WHERE Id=1234
Pretty simple. Let's assume there is a field with Id 1234, so it returns one row.
Now, let's add one more condition to the WHERE clause.
SELECT Id FROM Table WHERE Id=1234 AND Memo LIKE '%test%'
The query should pull one record, and then check to see if the word 'test' exists in the Memo field. However, if there is enough data, this statement will hang, as if it were searching the Memo field first, and then cross referencing the results with the Id field.
While this is what it is appearing to do, I just discovered that it is actually trying to create a statistic on the Memo field. If I turn off "auto create statistics", the query runs instantly.
So my quesiton is, how can you disable auto create statistics, but only for one query? Perhaps something like:
SET AUTO_CREATE_STATISTICS OFF
(I know, any normal person would just create a full text index on this field and call it a day. The reason I can't necessarily do this is because our data center is hosting an application for over 4,000 customers using the same database design. Not to mention, this problem happens on a variety of text fields in the database. So it would take tens of thousands of full text indexes if I went that route. Not to mention, adding a full text index would add storage requirements, backup changes, disaster recovery procedure changes, red tape paperwork, etc...)
I don't think you can turn this off on a per query basis.
Best you can do would be to identify all potentially problematic columns and then CREATE STATISTICS on them yourself with 0 ROWS or 0 PERCENT specified and NORECOMPUTE.
If you have a maintenance window you can run this in it would be best to run without this 0 ROWS qualifier but still leave the NORECOMPUTE in place.
You could also consider enabling AUTO_UPDATE_STATISTICS_ASYNC instead so that they are still rebuilt automatically but this happens in the background rather than holding up compilation of the current query but this is a database wide option.

Is there meta data I can read from SQL Server to know the last changed row/table?

We have a database with hundreds of tables.
Is there some kind of meta data source in SQL Server that I can programatically query to get the name of the last changed table and row?
Or do we need to implement this ourselves with fields in each table called LastChangedDateTime, etc.?
In terms of finding out when a table last had a modification, there is a sneaky way that can work to access this information, but it will not tell you which row was altered, just when.
SQL Server maintains index usage statistics, and records the last seek / scan / lookup and update on an index. It also splits this by user / system.
Filtering that to just the user tables, any insert / update / deletion will cause an update to occur on the index, and the DMV will update with this new information.
select o.name,
max(u.last_user_seek) as LastSeek,
max(u.last_user_scan) as LastScan,
max(u.last_user_lookup) as LastLookup,
max(u.last_user_update) as LastUpdate
from sys.dm_db_index_usage_stats u
inner join sys.objects o on o.object_id = u.object_id
where o.type = 'U' and o.type_desc = 'USER_TABLE'
group by o.name
It is not ideal however, a heap has no index for a start - and I have never considered using it for production code as a tracking mechanism, only as a forensic tool to check obvious alterations.
If you want proper row level alteration tracking you will either have to build that in, or look at the SQL 2008 specific Change Data Capture feature.
The [sys].[tables] view will tell you when the table was created and last modified (in terms of schema, not insert, updates or deletes). To my knowledge there is no built-in information about last modified for each record in the database (it would take up a lot of space anyway, so it's probably nice not to have it). So you should add a last modified field yourself, and maybe have it updated automatically by a trigger.
Depending on the recovery model you might be able to get this from the transaction log using fn_dblog http://www.sqlskills.com/BLOGS/PAUL/post/Search-Engine-QA-6-Using-fn_dblog-to-tell-if-a-transaction-is-contained-in-a-backup.aspx

Resources