I'm dealing with two tables which have 2 columns, as listed under.
Table 1: table_snapshot
account_no | balance_due
Table 2: table_ paid
account_no | post_balance | delta_balance
I added a third column to table2 with the following command:
ALTER TABLE table_paid ADD delta_balance number(18);
I'm trying to use the following query, to update the new column ( delta_balance ) with the difference in balances between 1 and 2.
FYI, table_paid is a subset of table_snapshot. i,e., table 2 has only a few accounts present in table 1. I get an error saying : SQL Statement not properly ended. the query i'm using is:
UPDATE table_paid
SET table_paid.delta_balance = table_paid.post_balance - table_snapshot.balance_due
from table_paid, table_snapshot
WHERE table_paid.account_no = table_snapshot.account_no;
Appreciate if someone can correct my query.
Many thanks.
novice.
Oracle doesn't have the UPDATE ... FROM syntax that you're using from MS Sql Server (which, I believe, isn't ANSI anyway). Instead, when you need to do an update on a result set, Oracle has you create the resultset as a kind of inline view, then you update through the view, like so:
UPDATE ( SELECT tp.delta_balance
, tp.post_balance
, ts.balance_due
FROM table_paid tp
JOIN table_snapshot ts
ON tp.account_no = ts.account_no
)
SET delta_balance = post_balance - balance_due;
This is more "correct" than the answers supplied by Babar and palindrom, as their queries will update every row in table_paid, even if there are no corresponding rows in table_snapshot. If there is a 1-1 correspondance, you don't need to worry, but it's safer to do it with the inline view.
It's unclear from your example which table is the parent table, or (as I'm guessing) neither is the parent table and account_no is pointing to the primary key of another table (presumably account, or "table_account" by your naming conventions). In any case, it's clear that there is not a 1-1 correspondence in your table - 15K in one, millions in the other.
This could mean 2 things: either there are many rows in table_snapshot that have no corresponding row in table_paid, or there are many rows in table_snapshot for each row in table_paid. If the latter is true, your query is impossible - you will have multiple updates for each row in table_paid, and the result will be unpredictable; how will you know which of the "post_balance - balance_due" expressions will ultimately determine the value of a given delta_balance?
If you run my query, you will find this out quickly enough - you will get an error message that says, "ORA-01779: cannot modify a column which maps to a non key-preserved table". This error will appear based not on the data in the table (it may be okay), but based on the primary keys you have defined on the two tables. If the join condition you specify doesn't unambiguously result in a 1-1 relationship between the updated table and the rest of the join, based on the defined keys, you will get this error. It's Oracle's way of telling you, "You're about to screw up your data".
In the other answers here, you will only get an error (in that case, ORA-01427: single-row subquery returns more than one row) if you actually have data that would cause a problem; my version is more strict, so it may turn out that you will need to use the other versions.
And, as the others have said, you'll definitely want an index on account_no for the table_snapshot table. One on the table_paid wouldn't hurt either.
Try this
UPDATE table_paid
SET table_paid.delta_balance = table_paid.post_balance -
(SELECT table_snapshot.balance_due from table_snapshot WHERE table_paid.account_no =
table_snapshot.account_no);
UPDATE table_paid
SET table_paid.delta_balance = table_paid.post_balance - ( select balance_due from table_snapshot
WHERE table_paid.account_no = table_snapshot.account_no )
Related
I'm trying to achieve 2 joins. If I run the 1st join alone it pulls 4 lots of results, which is correct. However when I add the 2nd join which queries the same reference table using the results from the select statement it pulls in additional results. Please see attached. The squared section should not be being returned
So I removed the 2nd join to try and explain better. See pic2. I'm trying to get another column which looks up InvolvedInternalID against the initial reference table IRIS.Practice.idvClient.
Your database is simply doing as you tell it. When you add in the second join (confusingly aliased as tb1 in a 3 table query) the database is finding matching rows that obey the predicate/truth statement in the ON part of the join
If you don't want those rows in there then one of two things must be the case:
1) The truth you specified in the ON clause is faulty; for example saying SELECT * FROM person INNER JOIN shoes ON person.age = shoes.size is faulty - two people with age 13 and two shoes with size 13 will produce 4 results, and shoe size has nothing to do with age anyway
2) There were rows in the table joined in that didn't apply to the results you were looking for, but you forgot to filter them out by putting some WHERE (or additional restriction in the ON) clause. Example, a table holds all historical data as well as current, and the current record is the one with a NULL in the DeletedOn column. If you forget to say WHERE deletedon IS NULL then your data will multiply as all the past rows that don't apply to your query are brought in
Don't alias tables with tbX, tbY etc.. Make the names meaningful! Not only do aliases like tbX have no relation to the original table name (so you encounter tbX, and then have to go searching the rest of the query to find where it's declared so you can say "ah, it's the addresses table") but in this case you join idvclient in twice, but give them unhelpful aliases like tb1, tb3 when really you should have aliased them with something that describes the relationship between them and the rest of the query tables
For example, ParentClient and SubClient or OriginatingClient/HandlingClient would be better names, if these tables are in some relationship with each other.
Whatever the purpose of joining this table in twice is, alias it in relation to the purpose. It may make what you've done wriong easier to spot, for example "oh, of course.. i'm missing a WHERE parentclient.type = 'parent'" (or WHERE handlingclient.handlingdate is not null etc..)
The first step to wisdom is by calling things their proper names
I have a stored procedure which is doing the following.
The populated target table data is checked against several similar source tables for a match (based on name and address data). If a match is found in the first table then it updates the target with a flag identifying which source table the match was from. However if it doesn't find a match I need it to look in the next source table and the next until either a match is found or not as the case may be.
Is there an easy way for the UPDATE statement to provide some kind of return value I can query to say whether it updated the target table? I would like to use this return value so that I can skip checking subsequent source tables unnecessarily.
Otherwise will I have to perform the conditional UPDATE then do a separate query to determine if the UPDATE actually updated the flag?
Probably the safest approach is to use the OUTPUT clause. This will return the modified rows into a new table.
You can check the table to see if any rows have been updated.
One advantage of the OUTPUT clause is that you can update multiple rows at the same time.
I like the soulution of Gordon, but I do not think you actualy need it.
Simply run the updates in order:
UPDATE BASE_TABLE
SET FLAG='first_table'
where FLAG IS null AND
EXIST (SELECT 1 FROM first_table f1 where f1.ID = ID)
UPDATE BASE_TABLE
SET FLAG='second_table'
where FLAG IS null AND
EXIST (SELECT 1 FROM second_table f2 where f2.ID = ID)
...
And so on.
You dont need to check every row conditionaly, that would be very slow.
you can put your update in try/catch and insert your result to another table
I need to update a table containing orders depending on customer information. This is how i would have approached it but apparently DB2 doesnt support JOINs in UPDATEs. I am working on an IBM iSeries.
UPDATE lib.orders as o
JOIN lib.customers as c
ON o.cstmrid = c.id
SET o.updatehere = 'NEWVALUE'
WHERE c.info = 'VALUE'
There are allready questions on that topic but none really help me.
Thanks!
Okay so this is really late but in case someones reading this: none of the comments/answers were correct. The important point is that i am working on an iseries which uses db2 udb and does neither support joins on updates nor merge (at least the version we work with).
The only way i figured out will work is a WHERE EXISTS clause.
I would recommend something like:
UPDATE lib.orders
SET updatehere = 'NEWVALUE'
WHERE cstrmid in (
SELECT id
FROM lib.customers
WHERE info = 'VALUE'
)
The reality is that an update statement has four main pieces:
what table is being updated?
possibly with an alias specified
your example used one
my suggestion didn't need one
what field(s) is/are being updated within item 1?
can be a single field, as in your example
can be a tuple, containing multiple fields
what value(s) is/are being populated into item 2?
can be a single value or a tuple of values, depending on item 2
can be the result of a subquery
must return a single value or a tuple, as needed, to match item 2
can join against the table specified in item 1 to ensure different values are output for different rows in the item 1's table
what matching criteria are there for records in item 1?
can be one or more criteria
if you don't specify this, DB/2 will iterate through ALL records in the table
can include a subquery
if you have a subquery in item 3, and a row matching this doesn't match up with any results from that subquery, item 2's fields will be assigned null values
I specified item 1 in line 1 of my suggestion.
I specified items 2 and 3 on line 2 of my suggestion.
I specified item 4 using a subquery in lines 3 - 7
I have, on occasion, written update statements where:
item 2 was a tuple of multiple fields
item 3 was a subquery which returned multiple values, joining against the table specified in item 1
item 4 used a subquery similar to what was used in item 3 but returning join values instead of update values for fields
The result was that DB/2 queried against item 1, using item 4 as criteria and then iterated through the resulting rows, updating fields in item 2 against values returned from item 3. As item 3 was a subquery, the join against the table alias specified in item 1 provided necessary criteria for what was returned from the subquery.
I've written update statements where the subqueries in question were Table Value Constructors. In that fashion, I was able to update a hundreds of records based on a small set of known values.
I have done all of this on DB/2 on an iSeries. I never used a MERGE because, as you've noticed, not all versions of DB/2 support that.
Working with DB2 but guess this applies to SQL in general.
I have two tables; Table1 contains data where the Fix column could be same in multiple rows. Table2 has unique rows and has data I want to add to columns in the first table if one or more matches between the Fix column in Table1 and the Title column in Table2 are found.
I'm getting an issue in that the SQL is returning an error saying: "The same row of target table "xxxxxxx" was identified more than once for an update, delete or insert operation of the MERGE statement.."
Now that is expected ie I know there are multiple rows in the target table that match the criteria and need to have the data from the source table applied to them.
I'm using MERGE but is that just not going to be possible? Been looking at GROUP BY too but can't get it to work.
If I was doing this in another language I'd go through the source table, build a collection of matching records from the source then iterate through the collection updating that with the source data that needed adding. Thinking there is a more efficient way in SQL though?
This code is completely wrong (and returns the error above) but adding it here to help any good person who wants to lend a hand :-)
CREATE PROCEDURE Update_RawData_With_xKey_Data ()
P1: BEGIN
MERGE INTO table1 AS T
USING table2 AS S
ON (T.FIX = S.TITLE)
WHEN MATCHED THEN
UPDATE SET
T.Rating = S.Rating,
T.GSDS = S.GSDS_Date,
ELSE IGNORE ;
END P1
I have a table with 4 columns. The first column is unique for each row, but it's a string (URL format).
I want to update my table, but instead of using "WHERE", I want to update the rows in order.
The first query will update the first row, the second query updates the second row and so on.
What's the SQL code for that? I'm using Sqlite.
Edit: My table schema
CREATE table (
url varchar(150),
views int(5),
clicks int(5)
)
Edit2: What I'm doing right now is a loop of SQL queries
update table set views = 5, click = 10 where url = "http://someurl.com";
There is around 4 million records in the database. It's taking around 16 seconds in my server to make the update. Since the loop update the row in order, so the first query update the first row; I'm thinking if updating the rows in order could be faster than using the WHERE clause which needs to browse 4 million rows.
You can't do what you want without using WHERE as this is the only way to select rows from a table for reading, updating or deleting. So you will want to use:
UPDATE table SET url = ... WHERE url = '<whatever>'
HOWEVER... SqlLite has an extra feature - the autogenerated column, ROWID. You can use this column in queries. You don't see this data by default, so if you want the data within it you need to explicitly request it, e.g:
SELECT ROWID, * FROM table
What this means is that you may be able to do what you want referencing this column directly:
UPDATE table SET url = ... WHERE ROWID = 1
you still need to use the WHERE clause, but this allows you to access the rows in insert order without doing anything else.
CAVEAT
ROWID effectively stores the INSERT order of the rows. If you delete rows from the table, the ROWIDs for remaining rows will NOT change - hence it is possible to have gaps in the ROWID sequence. This is by design and there is no workaround short of re-creating the table and re-populating the data.
PORTABILITY
Note that this only applies to SQLite - you may not be able to do the same thing with other SQL engines should you ever need to port this. It would be MUCH better to add an EXPLICIT auto-number column (aka an IDENTITY field) that you can use and manage.