barker@ems.MN.ORG (Bob W. Barker) (08/11/87)
Ok, I'm relatively new to this SQL buiness but I thought I could figure this one out. So far, no luck. I'm sure this is childs play to some of you. Say I've got a Unify database that contains 2 record types - cusstat and adjust. cusstat contains status info on customer accounts and adjust contains adjustments to customers account balances as well as a flag field to indicate that the adjustment has or hasn't been posted to cusstat. Both cusstat and adjust contain fields with customer names. I'm trying to whip up an SQL that will update the cusstat record of each customer that has corresponding adjustment records where the posted field = N (for NO). I'd also like to change the posted field to 'Y' after doing this but this isn't a real problem so I won't address it here. This SQL doesn't quite work: update cusstat set CS_balance = select CS_balance + A_dollars from cusstat, adjust where CS_custname = A_custname and A_posted = 'N'/ It updates the balances of all customers in cusstat. Now, I imagine I need a 'where' clause to match the update (ie. update cusstat where CS_custname = A_custname or something) but I can't for the life of me get the syntax right! Any help/pointers/suggestions etc. would be appreciated. Thank you! -Bob Barker -------------------------------------------------------------------------------- Bob Barker UUCP: {ihnp4|rutgers|amdahl}!{dayton|meccts}!ems!barker EMS/McGraw-Hill ATT: (612) 829-8200 9855 West 78th Street FAX: (612) 829-1839 Eden Prairie, MN 55344
eric@rtech.UUCP (Eric Lundblad) (08/13/87)
From article <464@ems.MN.ORG>, by barker@ems.MN.ORG (Bob W. Barker): > I'm trying to whip up an SQL that > will update the cusstat record of each customer that has corresponding > adjustment records where the posted field = N (for NO). I'd also like to > change the posted field to 'Y' after doing this but this isn't a real problem > so I won't address it here. > > This SQL doesn't quite work: > > update cusstat > set CS_balance = > select CS_balance + A_dollars from cusstat, adjust > where CS_custname = A_custname and A_posted = 'N'/ > > It updates the balances of all customers in cusstat. I believe that you want to execute something like: update cusstat set CS_balance = (select CS_balance + A_dollars from adjust where CS_custname = A_custname and A_posted = 'N') The difference here is that cusstat is not referenced on the from list of the subselect. The cusstat fields that are used in the subselect (CS_balance and CS_custname) would be correlated values to the outer context (ie. the update statement). In other words, for each row in cusstat, the subselect is executed using the current correlated values. If the subselect returns zero rows, then the current cusstat row isn't updated and the process in continued with the next row. If the subselect returns one value, then you change the cusstat row and go on to the next. The $64 question is: what happens when the subselect returns more than one values? Does the DBMS generate an error? Use the first value? The last value? All of them? Note that I'm not an expert on the supported syntax for Informix or Unify (I can't remember which one was referred to in the original article). However, assuming a moderate level of orthoginality (something that SQL isn't noted for) the above query should work. eric P.S. Damn, how do you spell orthoginality and where's my dictionary? -- Eric Lundblad ucbvax!mtxinu!rtech!eric
larry@xanadu.uucp (Larry Rowe) (08/18/87)
In article <464@ems.MN.ORG> barker@ems.MN.ORG (Bob W. Barker) writes: > ..... > >This SQL doesn't quite work: > >update cusstat >set CS_balance = > select CS_balance + A_dollars from cusstat, adjust > where CS_custname = A_custname and A_posted = 'N'/ > >It updates the balances of all customers in cusstat. Now, I imagine I need >a 'where' clause to match the update (ie. update cusstat where CS_custname = >A_custname or something) but I can't for the life of me get the syntax right! SORRY FOLKS, this query is not expressable in SQL. the left-hand side of a set stmt in an update can be an expression involving constants and refereences to attributes in the same record. i believe this limitation is in the langauge to avoid something known as the ``halloween problem'' where the order of scanning the table to be updated can change the semantics of the query. this arises because System-R (the folks who brought you SQL) does direct update (i.e., when scanning the table to be updated, the value is updated in the data page). QUEL allows this query to be written, as follows: replace cusstat(CS_balance=CS_balance+adjust.A_dollars) where cusstat.CS_balance=adjust.A_custname and adjust.A_posted='N' you've hit on one of the reasons folks say that QUEL is a better query language than SQL. i expect that all SQL vendors will extend their products with a construct to handle this case. however, it may take awhile because the implementation consequences are serious. btw, INGRES uses deferred update so the semantics can't depend on the scan order. interesting point about the way you write queries. you use implicit tuple variables (i.e., CS_balance refers implicitly to the cusstat table) but then you encode a tuple variable into the attribute name (i.e., CS_). larry
garyp@cognos.uucp (Gary Puckering) (08/21/87)
In article <464@ems.MN.ORG> barker@ems.MN.ORG (Bob W. Barker) writes: >I'm trying to whip up an SQL that >will update the cusstat record of each customer that has corresponding >adjustment records where the posted field = N (for NO). I'd also like to >change the posted field to 'Y' after doing this but this isn't a real problem >so I won't address it here. > >This SQL doesn't quite work: > >update cusstat >set CS_balance = > select CS_balance + A_dollars from cusstat, adjust > where CS_custname = A_custname and A_posted = 'N'/ > >It updates the balances of all customers in cusstat. This problem came up in March. You are not the only one to fall into this trap. Basically, SQL is rather counter-intuitive for these kinds of problems. It is easier to express a solution from the point of view of the adjustment transactions as the "driver" rather than the customer as the "driver". But, unfortunately, SQL doesn't work like that. (Pity!) Here's a copy of a similar situation reported by Stu Heiss, and my response: |Article: 103 of comp.databases |>From: garyp@cognos.UUCP (Gary Puckering) |Newsgroups: comp.databases |Subject: Re: need help with sql problem (informix) |Keywords: SQL, Informix |Date: 25 Mar 87 15:10:39 GMT |Reply-To: garyp@smokey.UUCP (Gary Puckering) |Organization: Cognos Incorporated, Ottawa, Canada | |In article <276@jpusa1.UUCP> stu@jpusa1.UUCP (Stu Heiss) writes: |>I'm trying to do what should be a simple update and can't believe the |>abysmal performance. Maybe I'm missing something obvious. The scenario |>is a 3 table database (name,address,accounts) and I'm updating a batch |>from a fourth table. Here's the code to do the name: |> update name_table |> set name = (select name from change_table where |> name_table.id = change_table.id) |> where name_table.id in |> (select id from change_table); |>This seems overly complex when all I want to do is find the entries in |>the name_table whose id match those in the change_table and update the |>name column (field). | |Wow! This is scarey! I hope someone can come up with an elegant SQL |request for this. I can't think of one. However, I can offer a suggestion |and a comment: | |1. You *must* have an index on the id column of change_table. Otherwise, | a sequential scan of change_table will be required for every row | read from name_table. | |2. The strategy SQL has forced you into is a bad one. You should be | able to drive the update request from the change_table, rather than | from the name_table. For examplem, in QTP, our 4GL transaction | processor, you would code this request as: | | access change_table link to name_table | output name_table update | item name final name of change_table | go | | For this request to execute efficiently, there need only be an index | on id of name_table. Essentially, this request does a natural join of | change_table and name_table and then it updates only the name_table row. | | Unfortunately, I can't see any way to do this in interactive SQL. | |3. This above strategy could be accomplished within a program by using | cursor. For example: | | sql declare get_changes cursor for | select id.change_table name.change_table | into :xid :xname | from change_table, name_table | where id.change_table = id.name_table | | Now you can code a loop which fetches the appropriate id's and names | into host variables xid and xname. Within the loop you code an SQL | update request like: | | sql update name_table | set name = :xname | where id = :xid | | |Like I say, I hope someone else can come up with a decent solution which |can be coded in interactive SQL. If not, it just goes to show you that |SQL is a lousy excuse for a 4GL. It leads to counter-intuitive requests. -- Gary Puckering 3755 Riverside Dr. Cognos Incorporated Ottawa, Ontario {allegra,decvax,ihnp4,linus,pyramid} (613) 738-1440 CANADA K1G 3N3 !utzoo!dciem!nrcaer!cognos!garyp