Updating an Oracle table with respect to another table - sql

I have two tables.
Lets say:
Table T1 with columns id,ref,a1,b1,c1,d1,e1
Table T2 with columns id,ref,a2,b2,c2,d2,e2
I need to update few columns in T2(a2,c2,e2) with respect to values in T1(a1,c1,e1) where T1.ref = T2.ref, given that ref=<certain value>.
For a particular value of ref column there are many records in each table.
I want to update T2 with respect to T1 for one particular value of ref column. Other records will be untouched.
I am currently doing this by dropping all the rows of T2 and inserting the current rows from T1 where ref=<some value>.
For ex: if the ref value=5
then I do these steps.
1. delete from T2 where ref=5;
2. insert into T2 (a2,c2,e2) select a1,c1,e1 from T1 where T1.ref = 5;
Certainly this is not a good method to synchronize the data between the two table.
Please suggest me an efficient solution to achieve this in Oracle.
I think i missed an important point. Both the tables have one column which clearly identifies each record. So I dont want the records to be in T2 which are not present in T1.
Just to reiterate - for a value of column "ref" both the tables returns multiple records, and each record has an unique identifying column. Those records identified in T1 only needs to be present in T2.
Thanks in Advance!

If you don't have exact matching between each row of T1 and T2 then your method is good enough.
But if you have some kind of matching try to implement your logic with merge:
MERGE INTO t2 b
USING (
SELECT *
FROM t1
WHERE t1.ref = <value>) t1
ON (t2.<key> = t1.<key>)
WHEN MATCHED THEN
UPDATE SET <t2.values> = <t1.values>
WHEN NOT MATCHED THEN
INSERT (<t2columns>)
VALUES (<t1values>);

I guess you want something like:
update t2
set (a2,c2,e2) = (select a1,c1,e1 from t1 where t1.ref = 5)
where t2.ref = 5;
But you would have to make sure the nested query produces only one row.

Related

SQL How do I update a single row with the first match from multiple tables

I have three tables to reference in an update statement. I need to move certain details from Table3 to Table1. But there is no ID reference between the two. The PK in Table3 matches the PK in Table2. I need to link based on two conditions. ColumnY from Table2 to PK of Table1 and on specific text found in ColumnP of Table 2. The last portion is where I'm getting stuck. I only need to copy the first matching row found in Table3 to add to Table1.
The script I wrote comes back with an error "ORA-01427: single-row subquery returns more than one row". I haven't been able to figure out how to copy the first row only. I'm looking to return the first matching row only as there aren't many duplicates and can modify manually afterwards. Unless there is a way to exclude the duplicates which also can work.
This is what I've used as a base.
UPDATE Table1
SET Table1.ColumnG = (
SELECT Table3.ColumnH
FROM
Table3,
Table2
WHERE Table3.ID = Table2.ID
and Table2.ColumnY = Table1.ID
and Table2.ColumnP = 'DETAIL'
);
UPDATE Table1
SET Table1.ColumnG =
SELECT * FROM (
SELECT Table3.ColumnH
FROM
Table3,
Table2
WHERE Table3.ID = Table2.ID
AND Table2.ColumnY = Table1.ID
AND Table2.ColumnP = 'DETAIL'
) WHERE rownum <= 1
);

Avoid multiple SELECT while updating a table's column relatively to another table's one

I am quite a newbie with SQL queries but I need to modify a column of a table relatively to the column of another table. For now I have the following query working:
UPDATE table1
SET date1=(
SELECT last_day(max(date2))+1
FROM table2
WHERE id=123
)
WHERE id=123
AND date1=to_date('31/12/9999', 'dd/mm/yyyy');
The problem with this structure is that, I suppose, the SELECT query will be executed for every line of the table1. So I tried to create another query but this one has a syntax error somewhere after the FROM keyword:
UPDATE t1
SET t1.date1=last_day(max(t2.date2))+1
FROM table1 t1
INNER JOIN table2 t2
ON t1.id=t2.id
WHERE t1.id=123
AND t1.date1=to_date('31/12/9999', 'dd/mm/yyyy');
AND besides that I don't even know if this one is faster than the first one...
Do you have any idea how I can handle this issue?
Thanks a lot!
Kind regards,
Julien
The first code you wrote is fine. It won't be executed for every line of the table1 as you fear. It will do the following:
it will run the subquery to find a value you want to use in your UPDATE statement, searching through table2, but as you have stated the exact id from
the table, it should be as fast as possible, as long as you have
created an index on that (I guess a primary key) column
it will run the outer query, finding the single row you want to update. As before, it should be as fast as possible as you have stated the exact id, as long as there is an index on that column
To summarize, If those ID's are unique, both your subquery and your query should return only one row and it should execute as fast as possible. If you think that execution is not fast enough (at least that it takes longer than the amount of data would justify) check if those columns have unique values and if they have unique indexes on them.
In fact, it would be best to add those indexes regardless of this problem, if they do not exist and if these columns have unique values, as it would drastically improve all of the performances on these tables that search through these id columns.
Please try to use MERGE
MERGE INTO (
SELECT id,
date1
FROM table1
WHERE date1 = to_date('31/12/9999', 'dd/mm/yyyy')
AND id = 123
) t1
USING (
SELECT id,
last_day(max(date2))+1 max_date
FROM table2
WHERE id=123
GROUP BY id
) t2 ON (t1.id = t2.id)
WHEN MATCHED THEN
UPDATE SET t1.date1 = t2.max_date
;

sql - what's the faster/better way to refer to columns in a where clause with inner joins?

Say I've got a query like this:
select table1.id, table1.name
from table1
inner join table2 on table1.id = table2.id
where table1.name = "parent" and table2.status = 1
Is it true that, since there's an inner join, I can refer the table2's status column even from table1? Like this:
select table1.id, table1.name
from table1
inner join table2 on table1.id = table2.id
where table1.name = "parent" and table1.status = 1
And if yes, what's the best of the two ways?
If I am not mistaken, you are asking that in an inner join, two fields of the same name, data type and length will be one field in the particular query. Technically that is not the case. Regardless of anything, Table1.Status will refer to Table1 and Table2.Status will refer to Table2's condition/value.
The two queries above CAN product different results from each other.
A good rule on this is that you stick your conditions on the base table, or Table1, in this case. If a field is exclusive to another table, that's when you'll use that Table's field.
No, that's not true. By Inner join what you are doing is say if you have table1 with m rows and table two with n rows then the third SET that will be produced by joining the two tables will have m*n rows based on match condition that you have mentioned in where clause. It's not m+n rows or infact columns of the two tables are not getting merged at database level. status column will remain in the table it has been defined.
Hope that helps!!!
You can see this is not the case if you do
CREATE TABLE table1 (id INT, name VARCHAR);
CREATE TABLE table2 (id INT, status INT);
Now if you run your second query you will get an error because you refer to t1.status, and the status column does not existing in table t1.
If there was a status field in both tables the query would run, but likely would not give the results you want e.g. assume status in table1 was always 1, and in table2 was always 0. Now your first query could never return rows, but your second one certainly could return rows.

In SQL Server, how to filter lots of elements across multiple columns

I have a table, t1, with columns such as name, code1, code2,..., code20
There are, say, 100K rows.
I have another look up table, t2, which has one column, code; it has 10k rows and each row has a code. So, totally there are 10K codes in this 1-column table.
I need to filter out all the rows in t1 that have the codes in t2 from any column, i.e. columns code1 to code20. In other words, in each row in t1, once a column has one of the codes in t2, it should be captured.
Is there an easy way to do this? Thanks a lot!
Here is a way to do it using not exists:
select t1.*
from t1
where not exists (select 1
from t2
where t2.code = t1.code1 or
t2.code = t1.code2 or
. . .
t2.code = t1.code20
);
It is tempting to use in as the condition in the nested select, but this behaves in a funky way with NULLs. The sequence of direct comparisons is easier.
That said, having 20 columns with the same type of data is usually a sign of poor table design. More typically, the data would be in some sort of association/junction table, with the 20 columns each appearing in their own row.
Sounds like you need to pivot the data in Table t1 then join on t2.
So instead of t1 where you have name, code1, code2,...Code 20 you would pivot t1 to
just Name and Code columns then join on t2.
Alternatively you could just perform separate joins of t1 on t2 for each of t2's columns Code 1 to 20 and union the result.
That's if I understand your problem correctly.

Update multiple rows in a table from another table when condition exists

I have two tables.
Table1 contains companies whose locations are georeferenced with lat/lng coordinates in a column called the_geom
Table2 also contain the same companies from Table1, not georeferenced, along with hundreds of other companies whose addresses are georeferenced.
All I need to do is insert the_geom lat/lng values from Table1 companies into their corresponding entries in Table 2. The common denominator on which these inserts can be based on is the address column.
Simple question, I am sure, but I rarely use SQL.
Assuming that by
insert "the_geom" lat/lng values
you actually mean to UPDATE existing rows in table2:
UPDATE table2 t2
SET the_geom = t1.the_geom
FROM table1 t1
WHERE t2.address = t1.address
AND t2.the_geom IS DISTINCT FROM t1.the_geom; -- avoid empty updates
Related answer:
How do I (or can I) SELECT DISTINCT on multiple columns?
Also assuming that the address column has UNIQUE values.
Details for UPDATE in the manual.
I had a similar problem, but when I tried the solutions mentioned above, I got an error like
Incorrect syntax near 't2'
The code that worked for me is:
UPDATE table2
SET the_geom = t1.the_geom
FROM table1 as t1
WHERE table2.address = t1.address AND table2.the_geom <> t1.the_geom
I know that my answer is 5 years late, but I hope this will help someone like me, who couldn't find this solution.
If you are a mysql user(like me) and if the above script is not working, here is the mysql equivalent.
UPDATE table2 t2, table1 t1
SET the_geom = t1.the_geom
WHERE t2.address = t1.address
AND t2.the_geom <> t1.the_geom; -- avoid empty updates
All credits to the OP.