15

I have a select query which does some text manipulation to essentially reformat a field so that I can look it up in another table:

If my first table if I have a field like "J1/2" it looks up the ID of a record in a different table with J1 and J2 in the appropriate fields.

This all works well.

Now I want to update the original table so I don't have to do lookups using this string manipulation anymore, but my attempts at update queries end with "Operation must use an updateable query"

Any ideas?

My SELECT statement:

SELECT DISTINCT
t1.DD,
t1.TN,
t1.DD & " J" & MID(t1.TN,2,1) AS CalculatedStart,
t1.DD & " J" & MID(t1.TN,4,1) AS CalculatedEnd,
t2.ID
FROM t1 INNER JOIN t2
ON (t1.DD & " J" & MID(t1.TN,2,1)=t2.StartLink)
AND (t1.DD & " J" & MID(t1.TN,4,1)=t2.EndLink)
WHERE t1.TN Like "J?/?"
AND t1.DD Like "M*";

Recall - this works fine and I get the necessary t2.ID out the other end.

So I want to do something like:

UPDATE t1 SET t2ID = (
    SELECT Query1.ID
    FROM Query1
    WHERE t1.DD=Query1.DD
    AND t1.TN=Query1.TN
    )
WHERE t1.TN Like "J?/?"
AND t1.DD Like "M*";

Only this fails. This is within MS Access itself so I can't imagine an actual permissions problem like most of the "Operation must use an updateable query" problems seem to be.

EDIT: Trying to simplify the case that doesn't work.

This UPDATE query is fine:

UPDATE t1
SET t2ID="Unknown"
WHERE TN LIKE "J?/?"
AND DD LIKE "M*";

This one fails (Thanks Goedke - this example obviously fails because the subquery returns more than 1 result. I had oversimplified to try to find my problem)

UPDATE t1
SET t2ID=(SELECT ID FROM t2)
WHERE TN LIKE "J?/?"
AND DD LIKE "M*";

So do I just have my subquery syntax wrong in some way?

EDIT: This SELECT statement is fine too:

SELECT t1.OA, t1.DD, t1.TN, t1.HATRIS,
    query1.DD, query1.TN, query1.ID
FROM t1 INNER JOIN query1
ON t1.DD=query1.DD
AND t1.TN=query1.TN

Furthermore, using count on the select statement above shows that there is exactly 1 ID being returned per (DD,TN) combination

EDIT:

The simplest case I've now got to - using various SELECT statements I now have a table with just 2 columns - the primary key of t1 and the value I want to insert into t1.

I still can't seem to write

UPDATE t1 SET t1.f2 = (SELECT t2.f2 FROM t2 WHERE t2.f1 = t1.f1)

where t1's primary key is f1. Even adding WHERE t1.f1 IN (SELECT f1 FROM t2) doesn't help. (Added to eliminate the possibility that the subquery returns 0 results)

Juan Mellado
  • 14,973
  • 5
  • 47
  • 54
Paul Smith
  • 1,044
  • 2
  • 13
  • 29
  • Sorry about the edit to your question: I hit the wrong edit button :/ – Godeke Feb 11 '09 at 16:47
  • 2
    BTW, I would highly recommend SQL Express as your back end. The reason I have not encountered this problem is that I always use a SQL engine on the back end, no matter how small the project... Access SQL is apparently broken in this case. – Godeke Feb 11 '09 at 16:50
  • 1
    Jet SQL is not broken at all. Jet SQL has more options for making queries updatable than standard SQL92. My suggestion is to try the Jet-specifict DISTINCTROW predicate. It can often force a non-updatable query to be updatabe. – David-W-Fenton Feb 11 '09 at 21:44
  • 4
    Arguably, ACE/Jet is 'broken' for UPDATE because a) it does not support the SQL-92 scalar subquery syntax at all i.e. exentions to the standard can be OK but SQL-92 entry-level compliance should be considered a minimum... – onedaywhen Feb 12 '09 at 14:31
  • 1
    ... b) by not requiring the result to be a scalar, the engine is free to choose an arbitrary value i.e. results are unpredictable. I know, I know: *you'd* never be ambiguous and always write bug free code etc but the point is the engine should *force* you to choose, rather than choosing arbitrarily. – onedaywhen Feb 12 '09 at 14:35
  • Access/JET fails to follow the SQL standard in an unexpected way: the SELECT form of the query picks up the rows and subquery correctly. It fails to do so for the same query structure in an UPDATE. There is no logical reason for that and DISTINCTROW doesn't apply here. – Godeke Feb 12 '09 at 15:57

9 Answers9

17

I have to weigh in with David W. Fenton's comment on the OP.

This is highly annoying problem with Jet/ACE. But try either:

  1. go to the query properties (click the background of the pane where the tables are displayed) and set 'Unique Records' to 'Yes'
  2. Option 1 is the equivalent of adding the somewhat strange looking DISTINCTROW keyword to the SELECT clause, eg

:

UPDATE DISTINCTROW tblClient 
       INNER JOIN qryICMSClientCMFinite 
          ON tblClient.ClientID = qryICMSClientCMFinite.ClientID
   SET tblClient.ClientCMType = "F";

This solves so many problems involving this error message that it is almost ridiculous.

That's MS Access in a nutshell - if you don't know the trade-secret workaround for problem x, you can take days trying to find the answer. To know the 10,000 workarounds IS to program Access. Is that enough of a warning for the uninitiated ?

Ben

David-W-Fenton
  • 22,871
  • 4
  • 45
  • 58
Ben McIntyre
  • 1,972
  • 17
  • 28
  • I do believe that Jet 3.x was more forgiving of updatability, but in ways that were out of synch with other SQL implementations. I think the Jet 4 changes to updatability (which annoyed the hell out of us experienced Access developers) were actually a step in the direction of making Jet SQL less weird, even if it still has some things that don't behave the way other SQL dialects do. – David-W-Fenton Nov 17 '09 at 04:00
  • And, BTW: In Access 2, the default for SELECT queries was DISTINCTROW instead just SELECT. I can always tell when I encounter an app with SELECT DISTINCTROW all over the place that is was likely created way back in Access 2. – David-W-Fenton Nov 17 '09 at 04:01
  • @David W. Fenton: "'Jet/ACE' ... applies to historic versions of Jet before the term 'Access Database Engine' existed in MS's documentation" -- You've misunderstood: the term 'Access Database Engine' is the collective term for Jet and ACE (and whatever comes next). If you take a look at the Access2007 documentation, MS use the term 'Access Database Engine' retrospectively, even for features specific to Jet (e.g. ULS). So are merely 'correcting' MS's collective term to use the your own collective term. – onedaywhen Nov 19 '09 at 07:05
  • I spent hours trying to find this "trade-secret workaround," and now things work. Thanks. – Patrick Feb 18 '12 at 23:44
7

This worked for me (Access 2000)

UPDATE DISTINCTROW T1 inner join T2 on T2.f1 = T1.f1  SET f2 = f2;
David Leigh
  • 1
  • 1
  • 1
7

A subquery of (SELECT ID FROM t2) can't work unless there is only one record in t2. Which ID are you expecting to be used?

The error message that is being reported normally occurs when you have joins and are not including all of the primary keys necessary to update back to tables in a data bound form (for example, your original DISTINCT destroys information about keys, so if it was bound to a form, the form would not be able to save back).

The fact you are using DISTINCT there would make me suspicious that the sub query is returning more than one row in your more complex example. This is probably the most common problem with assigning out of a sub query result: under-constraining the where clause.

Another problem I have seen with assigning out of a subquery is if the syntax of the inner query is incorrect. At least with SQL 2000 and 2005 back ends, the query processor will silently fail and return NULL in such cases. (This is, as far as I can tell, a bug: I see no reason why something that will return an error at the top level would be silently permitted in a subquery... but there it is.)

EDIT: Just to ensure that neither Paul or I wasn't going crazy, I created the following tables:

t1 | ID, FK, Data
t2 | ID2, Data2

I did not put any constraints except a primary key on ID and ID2. All fields were text, which is different from what I normally use for IDs, but should be irrelevant.

t1:

ID  FK  Data
Key1        Data1
Key2        Data2
Key3        Data3

t2:

ID2 Data2
Key1    DataA
Key2    DataB
Key3    DataC

A query of the form:

UPDATE t1 SET t1.FK = (select ID2 from t2 where t2.ID2 = t1.ID);

Failed with the same message Paul got.

select *, (select ID2 from t2 where t2.ID2 = t1.ID) as foreign from t1, 

works as expected, so we know the subquery syntax is not to blame.

UPDATE t1 SET t1.FK = 'Key1'

also works as expected, so we don't have a corrupt or non updateable destination.

Note: if I change the database backend from native to SQL 2005, the update works! A bit of googling around, and I find Access MVPs suggesting DLOOKUP to replace a subquery:

http://www.eggheadcafe.com/software/aspnet/31849054/update-with-subquerycomp.aspx

Apparently this is a bug in Access SQL, one that is avoided when using a SQL Express 2000 or higher back end. (The google results for "access update subquery" support this theory).

See here for how to use this workaround: http://www.techonthenet.com/access/functions/domain/dlookup.php

Godeke
  • 16,131
  • 4
  • 62
  • 86
  • Are you sure that your less simplified queries are returning single results? – Godeke Feb 11 '09 at 15:20
  • I'm just checking that out - having said that the original query should be returning a single result. – Paul Smith Feb 11 '09 at 15:22
  • The original query only returns a single result – Paul Smith Feb 11 '09 at 15:25
  • Your original query uses DISTINCT (if you mean the top of the question...) but your sub query is not (nor should it). The "AND t2.TN=Query.TN" looks very odd to me: where is T2? – Godeke Feb 11 '09 at 15:29
  • In my original attempt at the UPDATE query, the subquery returns a single result. It has to, because the subqueries WHERE clause uses the fields which are DISTINCT in the source query. i.e., the combination (DD,TN) is unique in the first query – Paul Smith Feb 11 '09 at 15:30
  • Updated the subquery - I think I typoed when trying to type the code out – Paul Smith Feb 11 '09 at 15:31
  • You might try adding a TOP 1 to the subquery to see if that ques Access in to the fact that only one row will be returned. – JohnFx Feb 11 '09 at 15:32
  • Anyway, I'm out of ideas. If you are sure that "DD+TN" returns a single row in all cases, and there is no join on the update side to block it... I just ran a nearly exact clone in 2007. – Godeke Feb 11 '09 at 15:39
  • As a last grasp at straws: are the IDs the same datatype (i.e., we don't have a numeric and an integer?) – Godeke Feb 11 '09 at 15:41
  • Actually the ID fields are Text with a field size of 25. For some reason the incoming IDs use characters – Paul Smith Feb 11 '09 at 15:51
  • PS How can I give Godeke rep for being so helpful without actually solving my problem (which may of course be my fault!) – Paul Smith Feb 11 '09 at 15:52
  • No worries, I have all the REP I need. One last idea: could your destination "table" actually be a view/query? – Godeke Feb 11 '09 at 16:04
  • The destination table is definitely as actual table. I'm using Microsoft Access 2003 SP3 by the way, but I can't imagine this would be an issue? – Paul Smith Feb 11 '09 at 16:09
  • 2003 works fine for me... I use 2007 only on a few machines; most are 2003. Would t1.f2 happen to be part of the primary key or some other constraint? – Godeke Feb 11 '09 at 16:12
  • It was part of another constraint, but removing that constraint doesn't seem to help – Paul Smith Feb 11 '09 at 16:17
  • OK, I have reproduced your problem... testing what's going on. – Godeke Feb 11 '09 at 16:19
  • Well, now I know something I didn't know before: subqueries on Access SQL are bugged for update queries. See my edit for details. – Godeke Feb 11 '09 at 16:43
  • Sadly I don't have the luxury of changing the backend - this really is just a small Access database and I'm using an UPDATE query to try to avoid having to do lots of manual data entry! Many many thanks for your investigations! – Paul Smith Feb 11 '09 at 17:17
6

I havent't read the whole thread, but this is the solution that I am using:

update (select * from t1 inner join t2 on t1.key = t2.key) set t1.field1 = t2.field2

and that works fine in MS Access for me.

Jason Plank
  • 2,336
  • 5
  • 31
  • 40
Sandip
  • 1
  • 1
  • 1
1

My solution was to change my sql on that way.

  update (select o.pricein, g.pricein from operations o left join goods g on g.id = o.goodid where o.opertype = 4 and o.acct = 1) 
  set o.pricein = g.pricein
David-W-Fenton
  • 22,871
  • 4
  • 45
  • 58
Plamen
  • 1
  • 1
  • You're saying that SQL was executed by Jet/ACE? I'm surprised, as it looks to me like it's missing the table after UPDATE. And whether an update works with a JOIN in the SELECT depends entirely on what tables you're joining and the indexes on the joined fields. – David-W-Fenton Mar 04 '10 at 19:32
1

I had the same error ("Operation must use an updateable query") using Access 2010 and I was performing a simple update query with an inner join. All I did was add a primary key to the table I was joining on (already had one, of course, on the table I was updating) and everything worked.

0

For this one: UPDATE t1 SET t1.f2 = (SELECT t2.f2 FROM t2 WHERE t2.f1 = t1.f1)

UPDATE t1 INNER JOIN t2 ON t1.f1 = t2.f1 SET t1.f2 = [t2].[f2];
JeffO
  • 7,957
  • 3
  • 44
  • 53
  • FWIW I worked around this by copying "t2" which was actually a query, into a temporary table, giving that temporary table a primary key on f1 (the join column) – Paul Smith Nov 03 '09 at 10:49
0

the solution to that is to give the permissions to the folder that contains the acces database that is inside the disk c

  • 1
    Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Dec 12 '21 at 01:02
0

I was tripped up by this same issue and none of the other strategies helped, though the DLookup gave me an insight that did work.

In my situation I want to find the nearest match, my SQL was:

UPDATE scrTelemetry TAR 
SET TAR.idUnit = (
  Select top 1 idUnit 
  from dbo_Unit CHK 
  where CHK.cdIP = TAR.cdIP 
  order by dtReplaced desc)  
WHERE TAR.cdIP <> '' AND TAR.cdMAC = '' AND TAR.idUnit = 0

A DLOOKUP wouldn't work as there's no way to control which record it stumbles across; DISTINCTROW (another solution) likewise proved a slip.

Instead, I wrote a function and call it in the SQL:

UPDATE DISTINCTROW scrTelemetry 
SET idUnit = GetLastKnownUnitForAnIP (cdIP) 
WHERE cdIP <> '' AND cdMAC = '' AND idUnit = 0
Eric Hofer
  • 65
  • 7