I am trying to update a column of a table of a database from a column of different table of different database - sql

update table1
set table1.col1=(select t2.c2 as X from t2#dblink)
where t1.c2=(select t3.c3,t3.c4,t3.c5 from t3#dblink)
but i am getting
ORA-01427: single-row subquery returns more than one row
can someone help me out with this.

As you were told, sample data would help, as well as posting consistent information (you are updating TABLE1 and, later, use T1 which is ... what?).
Anyway, something like this might give you an idea:
update table1 t1 set
t1.c1 = (select t2.c2
from t2#dblink t2
where t2.some_column = t1.some_column --> you miss this
)
where t1.cs in (select t3.c3 from t3#dblink union
select t3.c4 from t3#dblink union
select t3.c5 from t3#dblink
);

The Problem:
Your subquery is returning multiple records and you are checking equality with a single value.
value1 <> {v2,v3,v4,v5,....}
you need to use the in clause (with a union if you intend to compare t1,cs with multiple columns on t2)

Related

ORA-01427 Subquery returns more then one row..Oracle Update Statement

How do you write a update statement with a Sub-Select in an Oracle Environment (SQL Developer)?
Example: UPDATE table SET column = (SELECT....)
Every time I try this it gives me ORA-01427 "Sub select returns more then one row" even if there is no WHERE clause..
Based on the understanding of your question I'd suggest use Merge statement.
Merge into Table1
Using
(SELECT * from table2 where condition) Temp
On (Table1.columname condition Temp.columname)
When matched Then update Set Table1.column_name = Temp.column_name;
Table1 is the table where you want to update the records.
Table2 is the table from which you want to get the data (The sub query which you are talking about )
Using this merge statement you will be able to update n number of rows.
If you want to update multiple rows, you can either use a MERGE statement (as in #jackkds7's answer above) or you can use a filter on your subselect:
UPDATE table t1
SET column = ( SELECT column FROM table2 t2 WHERE t2.key = t1.key );
If there aren't matches in table2 for all the records in table then column will be set to NULL for the non-matches. To avoid that, add a WHERE EXISTS clause:
UPDATE table t1
SET column = ( SELECT column FROM table2 t2 WHERE t2.key = t1.key )
WHERE EXISTS ( SELECT 1 FROM table2 t2 WHERE t2.key = t1.key );
Oh and in the event that key is not unique for table2, you can aggregate (up to you to figure out which function would be best):
UPDATE table t1
SET column = ( SELECT MAX(column) FROM table2 t2 WHERE t2.key = t1.key )
WHERE EXISTS ( SELECT 1 FROM table2 t2 WHERE t2.key = t1.key );
Hope this helps.
I think it would help if you posted your actual query.
In essence, the "inner" select would be executed for each row that would be updated. This inner select query is called a correlated subquery:
UPDATE table t SET t.column = (
select ot.othercolumn from othertable ot
where ot.fk = t.id --This is the correlation part, that finds
--he right value for the row you are currently updating
)
You must ensure the subquery you use will always return just a single row and a single column for every time it runs (that is, for every row that is going to be updated). If needed, you can use MAX(), or ROWNUM to ensure you always only get 1 value
More examples:
Using Correlated Subqueries

works fine in one case / (column ambiguously defined)error in another

I have 2 tables with a column named the same. Column is BAN_KEY
when I run this query
with
t1 as
(
select *
from table1
),
t2 as
(
select *
from table2
)
t3 as
(
select *
from t1, t2
where t1.c1 = t2.c2
)
select * from t3
I get error column ambiguously defined, but when I do it this way
with
t1 as
(
select *
from table1
),
t2 as
(
select *
from table2
)
select *
from t1, t2
where t1.c1 = t2.c2
The result looks like this
BAN_KEY | BAN_KEY_1 | other columns
some values...
What's the reason for this?
First, learn to use proper JOIN syntax. Simple rule: Never use commas in the FROM clause. Always use proper, explicit JOINs.
That has nothing to do with your question. The answer is much simpler. For a CTE (or table), Oracle needs to be able to assign column names to the result so they can be access subsequently. It accepts the column names that you provide, assuming that your intention is correct. Duplicate column names are not allowed because the reference would be ambiguous; hence the error.
Why doesn't this happen for a result set? Oracle does not require that the columns in the result set of a query be unique. For convenience, though, it distinguishes between columns with the same name.

How to select a value that can come from two different tables?

First, SQL is not my strength. So I need help with the following problem. I'll simplify the table contents to describe the problem.
Let's start with three tables : table1 with columns id_1 and value, table2 with columns id_2 and value, and table3 with columns id_3 and value. As you'll notice, a field value appears in all three tables, while ids have different column names. Modifying column names is not an option because they are used by Java legacy code.
I need to set table3.value using table1.value or table2.value according to the fields table1.id_1, table2.id_2 and table3.id_3.
My last attempt, which describes what I try to do, is the following:
UPDATE table3
SET value=(IF ((SELECT COUNT(\*) FROM table1 t1 WHERE t1.id_1=id_3) > 0)
SELECT value FROM table1 t1 WHERE t1.id_1=id_3
ELSE IF ((SELECT COUNT(\*) FROM table2 t2 WHERE t2.id_2=id_3)) > 0)
SELECT value FROM table2 t2 WHERE t2.id_2=id_3)
Here are some informations about the tables and the update.
This update will be included in an XML file used by Liquibase.
It must work with Oracle or SQL Server.
An id from table3.id_3 can be found at most once in table1.id_1 or in table2.id_2, but not in both tables simultaneously.
If table3.id_3 is not found in table1.id_1 nor in table2.id_2, table3.value remains null.
As you can imagine, my last attempt failed. In that case, the IF command was not recognized during the Liquibase update. If anyone has any ideas how to deal with this, I'd appreciate. Thanks in advance.
I don't know Oracle very well, but a SQL Server approach would be the following using COALESCE() and OUTER JOINs.
Update T3
Set Value = Coalesce(T1.Value, T2.Value)
From Table3 T3
Left Join Table2 T2 On T3.Id_3 = T2.Id_2
Left Join Table1 T1 On T3.Id_3 = T1.Id_1
The COALESCE() will return the first non-NULL value from the LEFT JOIN to tables 1 and 2, and if a record was not found in either, it would be set to NULL.
It is Siyual's UPDATE written with MERGE operator.
MERGE into table_1
USING (
SELECT COALESCE(t2.value, t3.value) as value, t1.id_1 as id
FROM table_1 t1, table_2 t2, table_3 t3
WHERE t2.id_2 = t3.id_3 and t1.id_1 = t2.id_2
) t on (table_1.id_1 = t.id)
WHEN MATCHED THEN
UPDATE SET table_1.value = t.value
This should work in Oracle.
In Oracle
UPDATE table3 t
SET value=COALESCE((SELECT value FROM table1 t1 WHERE t1.id_1=t.id_3),
(SELECT value FROM table2 t2 WHERE t2.id_2=t.id_3))
Given your assumption #3, you can use union all to put together tables 1 and 2 without running the risk of duplicating information (at least for the id's of interest). So a simple merge solution like the one below should work (in all DB products that implement the merge operation).
merge into table3
using (
select id_2 as id, value from table2
union all
select id_3, value from table 3
) t
on table3.id_3 = t.id
when matched
then update set table3.value = t.value;
You may want to test the various solutions and see which is most effective for your specific tables.
(Note: merge should be more efficient than the update solution using coalesce, at least when relatively few of the id's in table3 have a match in the other tables. This is because the update solution will re-insert NULL where NULL was already stored when there is no match. The merge solution avoids this unnecessary activity.)

Why SELECT and UPDATE Return Different Record Counts/Affected

I wrote a SELECT query to find out how many records will be impacted with my UPDATE query.
The SELECT and UPDATE returned different record counts.
Here is my SELECT query:
SELECT *
FROM T1
JOIN T2 on T1.ID = T2.ID
WHERE T1.Name IS NULL
AND T2.Status = 'happy'
Here is my UPDATE query:
UPDATE T1
SET T1.Name = T2.Name
FROM T1
JOIN T2 on T1.ID = T2.ID
WHERE T1.Name IS NULL
AND T2.Status = 'happy'
My SELECT returns 19K records, and my UPDATE affects 12K records. Please note that the WHERE clause is exactly the same for both the SELECT and UPDATE.
What is causing the discrepancy in records counts between the SELECT and UPDATE queries?
Can you please help me understand what is happening here?
Thanks in advance!!
That can happen on a one to many join when you're updating the one side. In your case, it looks like there are more than one T2 row for some of your T1 rows, and the server will return the T1 row as many times as there is matches in the T2 table.
Check if this matches your update count:
SELECT count(distinct T1.ID)
FROM T1
JOIN T2 on T1.ID = T2.ID
WHERE T1.Name IS NULL
AND T2.Status = 'happy'
One possible reason: You have instances of ID where more than one T2 record exists for the corresponding T1 record
Instead of select *, try distinct t1.name. Since you are updating name in T1 table, that might give you the exact number of rows being effected in update.
hope that helps,
I had a similar issue, and after looking deeper into the data I found out that the IDs used in the JOIN criteria were not unique (had duplicates) in one of the tables. It looks like when doing SELECT it takes all matching, but when doing UPDATE it only takes the first one out of the duplicates.

SQLite table aliases effecting the performance of queries

How does SQLite internally treats the alias?
Does creating a table name alias internally creates a copy of the same table or does it just refers to the same table without creating a copy?
When I create multiple aliases of the same table in my code, performance of the query is severely hit!
In my case, I have one table, call it MainTable with namely 2 columns, name and value.
I want to select multiple values in one row as different columns. for example
Name: a,b,c,d,e,f
Value: p,q,r,s,t,u
such that a corresponds to p and so on.
I want to select values for names a,b,c and d in one row => p,q,r,s
So I write a query
SELECT t1.name, t2.name, t3.name, t4.name
FROM MainTable t1, MainTable t2, MainTable t3, MainTable t4
WHERE t1.name = 'a' and t2.name = 'b' and t3.name = 'c' and t4.name = 'd';
This way f writing the query kills the performance when size of the table increases as rightly pointed above by Larry.
Is there any efficient way to retrieve this result. I am bad at SQL queries :(
If you list the same table more than once in your SQL statement and do not supply conditions on which to JOIN the tables, you are creating a cartesian JOIN in your result set and it will be enormous:
SELECT * FROM MyTable A, MyTable B;
if MyTable has 1000 records, will create a result set with one million records. Any other selection criteria you include will then have to be evaluated across all one million records.
I'm not sure that's what you're doing (your question is very unclear), but it may be a start on solving your problem.
Updated answer now that the poster has added the query that is being executed.
You're going to have to get a little tricky to get the results you want. You need to use CASE and MAX and, unfortunately, the syntax for CASE is a little verbose:
SELECT MAX(CASE WHEN name='a' THEN value ELSE NULL END),
MAX(CASE WHEN name='b' THEN value ELSE NULL END),
MAX(CASE WHEN name='c' THEN value ELSE NULL END),
MAX(CASE WHEN name='d' THEN value ELSE NULL END)
FROM MainTable WHERE name IN ('a','b','c','d');
Please give that a try against your actual database and see what you get (of course, you want to make sure the column name is indexed).
Assuming you have table dbo.Customers with a million rows
SELECT * from dbo.Customers A
does not result in a copy of the table being created.
As Larry pointed out, the query as it stands is doing a cartesian product across your table four times which, as you has observed, kills your performance.
The updated ticket states the desire is to have 4 values from different queries in a single row. That's fairly simple, assuming this syntax is valid for sqllite
You can see that the following four queries when run in serial produce the desired value but in 4 rows.
SELECT t1.name
FROM MainTable t1
WHERE t1.name='a';
SELECT t2.name
FROM MainTable t2
WHERE t2.name='b';
SELECT t3.name
FROM MainTable t3
WHERE t3.name='c';
SELECT t4.name
FROM MainTable t4
WHERE t4.name='d';
The trick is to simply run them as sub queries like so there are 5 queries: 1 driver query, 4 sub's doing all the work. This pattern will only work if there is one row returned.
SELECT
(
SELECT t1.name
FROM MainTable t1
WHERE t1.name='a'
) AS t1_name
,
(
SELECT t2.name
FROM MainTable t2
WHERE t2.name='b'
) AS t2_name
,
(
SELECT t3.name
FROM MainTable t3
WHERE t3.name='c'
) AS t3_name
,
(
SELECT t4.name
FROM MainTable t4
WHERE t4.name='d'
) AS t4_name
Aliasing a table will result a reference to the original table that exists for the duration of the SQL statement.