SQL UPDATE table1 row-by-row based on values in table2 - sql

I have two tables and I want to UPDATE one table based on the values of another table.
With the help of the following SO-post I write a query:
query = f""" UPDATE table1
SET goal =
(SELECT table2.goal FROM table2
WHERE player = table2.player
AND opponent = table2.opponent
AND date = table2.date
AND competition = table2.competition
AND score = table2.score """
When I execute the query every row of table1 is affected with the same value for goal. However, the desired process is that the query checks row-by-row if there are matching rows and, if so, update the column goal. What am I doing wrong?

You must correlate the subquery with the table that you want to update:
UPDATE table1 AS t1
SET goal = (
SELECT t2.goal
FROM table2 AS t2
WHERE t2.player = t1.player
AND t2.opponent = t1.opponent
AND t2.date = t1.date
AND t2.competition = t1.competition
AND t2.score = t1.score
);
Or:
UPDATE table1 AS t1
SET goal = (
SELECT t2.goal
FROM table2 AS t2
WHERE (t2.player, t2.opponent, t2.date, t2.competition, t2.score) =
(t1.player, t1.opponent, t1.date, t1.competition, t1.score)
);
Note that if a row in table1 does not match any row in table2, the column will be updated to null.
If in this case you don't want the column to be updated use also COALESCE():
UPDATE table1 AS t1
SET goal = COALESCE((
SELECT t2.goal
FROM table2 AS t2
WHERE (t2.player, t2.opponent, t2.date, t2.competition, t2.score) =
(t1.player, t1.opponent, t1.date, t1.competition, t1.score)
), goal);
If your version of SQLite is 3.33.0+, you could use the UPDATE..FROM syntax:
UPDATE table1 AS t1
SET goal = t2.goal
FROM table2 AS t2
WHERE (t2.player, t2.opponent, t2.date, t2.competition, t2.score) =
(t1.player, t1.opponent, t1.date, t1.competition, t1.score);

From what I understand, this query will only affect the table1 if the table2 have the same values. Do you want to check if any row is the same then update the goal value?
Instead of using AND, you could use OR. This modification will make sure the query will go through if any of the values are similar.
query = f""" UPDATE table1
SET goal =
(SELECT table2.goal FROM table2
WHERE player = table2.player
OR opponent = table2.opponent
OR date = table2.date
OR competition = table2.competition
OR score = table2.score )"""

Related

SQL Update values from multiple tables

I have the following sql. I just want to update 2 fields from the table t1 and 1 field from t2. I tried to join the t2 but it doesnt work. Is it possible to update two tables?
And i want to update the t2.field with a const variable.
so that i get something like this
UPDATE test1_00 t1
SET (t1.field,
t1.field,
t2.field = 5)
UPDATE test1_00 t1
SET (t1.field,
t1.field,
t2.field) =
(SELECT test,
test,
test
FROM table(test_function(
02172,
'TEST',
date('2021-07-26'),
'TEST',
5455612)
)
join test1 t1 on t1.id = t2.id
where t2.test = test
and t2.test = test
);
No.
An update can only update one table at a time.

Setting a value in one table depending on count of entries in other table

So I am fairly new to working with databases so my apologies in advance if this is an obvious one.
I have following issue. In my Database I have two tables with entries. In Table1 there are entries which all have an ID and a field in which there is a counter. In Table2 there are entries which have a fied with an ID out of Table1 as foreign key (fk_table1_id).
My Goal is to set the counter for each entry in table1 to the number of entries in table2 that have fk_table_id of the entry from table1.
So basically I thought of something like this (pseudocode)
foreach (entries of table1 as entry$entry) {
update `table1` where `id` = `entry->id` set `count`= {
count `table2`wehere `fk_table1_id` = entry->id
}
}
thanks in advance.
You can use join with an aggregation subquery:
update table1 t1 join
(select fk_table1_id, count(*) as cnt
from table2 t2
group by fk_table1_id
) t2
on t2.fk_table1_id = t1.id
set t1.count = t2.cnt;
Note: This only sets counts where rows exist in the second table. If you want 0 counts, then:
update table1 t1 left join
(select fk_table1_id, count(*) as cnt
from table2 t2
group by fk_table1_id
) t2
on t2.fk_table1_id = t1.id
set t1.count = coalesce(t2.cnt, 0);

update with join in oracle sql

I want to update a column of a table1 but I should update only records where conditions are true in another table
something like this, but I don't know how to implement this purpose in Oracle SQL
update table1
join table2 on table1.msg_id = table2.id
set table1.index = table1.index-1
where table1.index > 10 and table2.type = 'myType'
I would write this as an exists subquery in any database:
update table1 t1
set index = t1.index - 1
where table1.index > 10 and
exists (select 1
from table2
where t2.id = t1.msg_id and
t2.type = 'myType'
);
The join sort of implies that you are going to use data from table2 in the update of table1. Instead, you simply want to change a value in a row when a particular condition is met.
Oracle does not support this syntax (sigh).
For your use case, you could use a not exists condition with a correlated subquery instead:
update table1
set table1.index = table1.index - 1
where
table1.index > 10
and exists (
select 1 from table2 where table1.msg_id = table2.id and table2.type = 'mytype'
)
Note: make your live easier, do not use index for a column name. This is a reserved work in pretty much all RDBMS.

Table update based on match/nomatch

I have two tables t1 and t2.
t1 has this structure:
yearmonth
account
company
RCV_amount_t1
t2 has this structure:
yearmonth
account
company
billing amount
billing amount CM_1
billing amount CM_2
RCV_amount_t2
I want to join t2 to t1 using yearmonth, account, and company. If they match, I want to update RCV_amount_t2 with the value in RCG_amount_t1. Otherwise, I want to set RCV_amount_t2 to spaces.
In the same manner, I want to join t1 with t2 using yearmonth, account, and company and set values accordingly.
Is it possible to achieve? If so, how do I go about it?
I want to join t2 to t1 using yearmonth, account, and company. If they
match, I want to update RCV_amount_t2 with the value in RCG_amount_t1.
Otherwise, I want to set RCV_amount_t2 to spaces.
This will update the matching rows with the appropriate value, and update the rows with no match to NULL. If the field is numeric, you can't update it to "spaces"; NULL would be the appropriate indicator of no value. If the field is not numeric, then you could do a second update to replace NULL values with whatever you like, but NULL would still seem to me to be the most appropriate indicator of no value.
UPDATE t2 SET rcv_amount_t2 = (
SELECT rcv_amount_t1
FROM t1
WHERE t1.yearmonth = t2.yearmonth
AND t1.account = t2.account
AND t1.company = t2.company
)
You'll want to use a MERGE.
It allows you to join two tables and specify how to update the values if they match.
The general structure of a MERGE statement looks like:
MERGE INTO driver_table
USING other_table
ON
(
driver_table.column1 = other_table.column1
AND driver_table.column2 = other_table.column2
AND ...
)
WHEN MATCHED THEN UPDATE
SET
driver_table.some_column = other_table.some_value,
driver_table.some_flag = 'Y',
...
;
It seems that we cannot resolve it in a single query, we need a merge and a correlated query, It works fine for me:
This will update t2 with values from t1 when matched:
MERGE INTO t2
USING (SELECT yearmonth, account, company, RCV_amount_t1 FROM t1) S
ON (t1.yearmonth = t2.yearmonth and
t1.account = t2.account and
t1.company = t2.company)
WHEN MATCHED THEN UPDATE SET t2.RCV_amount_t2 = S.RCV_amount_t1;
Then a query containg a corrolated subquery to blank when not matched:
update t2 set RCV_amount_t2 = ' ' where yearmonth||account||company not in(
select yearmonth||account||company from t1
where t1.yearmonth = t2.yearmonth and t1.account=t2.account and t1.company=t2.company);

How can I make this SQL update statement more efficient?

I am trying to add count, sum, and average values from one table to another, but I end up querying the same data for each value. I'm using PostgreSQL. I'm turning this over to the experts to learn how to make this update statement more efficient. Here it is:
update "table1" set
"col1" = (SELECT COUNT(*) FROM "table2" WHERE "table2Id" = "table1"."table1Id"),
"col2" = (SELECT AVG("someCol") FROM "table2" WHERE "table2Id" = "table1"."table1Id"),
"col3" = (SELECT SUM("someCol") FROM "table2" WHERE "table2Id" = "table1"."table1Id");
I should be able to run a subquery like this once and access the returned values for the update, correct?
SELECT COUNT(*), AVG("someCol"), SUM("someCol") FROM "table2" WHERE "table2Id" = "table1"."table1Id";
Any help is much appreciated.
Try a subquery:
UPDATE table1
SET col1 = YourCount, col2 = YourAverage, col3 = YourSum
FROM table1 t1
INNER JOIN (
SELECT table2Id, COUNT(*) AS YourCount, AVG(someCol1) YourAverage,
SUM(someCol2) YourSum
FROM table2
GROUP BY table2Id
) t2 ON t1.table1Id = t2.table2Id
I believe in recent (9.0+) versions of Postgresql, it is possible to use a CTE for a cleaner looking query.
WITH calculations AS
(SELECT table2ID, COUNT(*) AS n, SUM(someCol) AS s, AVG(someCol) AS a
FROM table2
GROUP BY table2ID)
UPDATE table1
SET col1=n, col2=s, col3=a
FROM calculations WHERE calculations.table2ID=table1.table1ID;