i have 2 tables of the following structure
Table1
Id(long), Name(nvarchar), ValuesExist(bit)
Table2
Id(long), Table1Id(long), Value(int)
with foreign key constraint on Table.Id and Table2.Table1Id
Now i want to update Table1 according to the following logic
if (there are values in Table2 corresponding to a row Table1 AND Atleast one the values is > 0 )
then Table1.ValuesExist = 1
else Table1.ValuesExist = 0
i have looked and tried many examples given on SO abt how to update a column using join but cudnt find any example in which updation is being done through logic or some ComputedColumn
UPDATE T1
SET ValuesExist =
CASE
WHEN T2.Value > 0 THEN 1
ELSE 0
END
FROM Table1 T1
LEFT JOIN Table2 T2 ON T1.Id = T2.Table1Id
Here is one method. It uses a correlated subquery in the update and is very close to how you stated the problem:
update table1 t1
set ValuesExist = (case when exists (select 1
from Table2 t2
where t2.Table1Id = t1.Id and t2.Value > 0
)
then 1 else 0
end);
This will perform best if you have an index on Table2(Table1Id, Value).
Related
I have two tables: table1 and table2:
table1 has columns id and integer
table2 has columns id and boolean
table2 can have multiple rows with the same id
I want to update the integer column of table1 by looking at all rows with the same id in table2 and seeing if any of the boolean values are true. If so I want table1.integer to be 1, else I want it to be 0.
I have tried something like this:
UPDATE table1,
(
SELECT table2.id, Sum(table2.boolean) > 0
) AS 'condition'
from table2
WHERE 1
GROUP BY table2.id) table3
SET table1.integer =IF(table3.condition, 1, 0) where table1.id = table3.id
And it seems to work, but I wanted to ask if there is a nicer/cleaner/more succinct way of updating the rows of table1 according to multiple rows of table2.
I would recommend EXISTS:
UPDATE table1 t1
SET t1.integer = (EXISTS (SELECT 1
FROM table2 t2
WHERE t2.id = t.id AND
t2.boolean
)
);
This can take advantage of an index on table2(id, boolean). With such an index, it should be faster than an approach that uses JOIN and AGGREGATION.
The syntax of your query is MySql like, so you can do a join like this:
UPDATE table1 t1 INNER JOIN (
SELECT id, MAX(boolean) maxboolean
FROM table2
GROUP BY id
) t2 ON t2.id = t1.id
SET t1.integer = t2.maxboolean
If there are ids in table1 without a corresponding id in table2 and you want the integer column for them to be updated to 0 then use a LEFT join:
UPDATE table1 t1 LEFT JOIN (
SELECT id, MAX(boolean) maxboolean
FROM table2
GROUP BY id
) t2 ON t2.id = t1.id
SET t1.integer = COALESCE(t2.maxboolean, 0)
In T1 I have a 50 ID'ss. I'm trying to find which ones are in T2. And add row name "test" next to ID with info about occurrence in T2. But when I put my code I receive only ID's which are included in T2. What I`m doing wrong?
SELECT DISTINCT t1.id, CASE WHEN t1.id IS NULL THEN 0 ELSE 1 END AS test
FROM t2
JOIN t1 ON t2.id = t1.id
You can use outer join:
SELECT DISTINCT t1.id, CASE WHEN t2.id IS NULL THEN 0 ELSE 1 END AS test
FROM t1 LEFT JOIN t2 ON t2.id = t1.id
Use exists:
select t1.*,
(case when exists (select 1 from t2 where t2.id = t1.id) then 1 else 0 end) as flag
from t1;
Note that select distinct is not needed. That construct just slows down queries, if it is not needed.
Use Left Join to get the unmatched rows as well.
select t1.id
, case when t2.id is null then 0 else 1 end as test
from t1
left outer join t2
on t1.id = t2.id
I have two tables: table1 and table2 both with one column for ID. I want to create a column in table1 that displays 'Y' if ID in table1 is in table2 and 'N' if it is not.
Currently, I am using:
Select id, case when id in (table2) then 'Y' else 'N' end as in_table2
from table1
However, since both tables are very big, the query is taking forever. Is there a more efficient way of doing this?
Thanks
Use exists:
Select t1.id,
(case when exists (select 1 from table2 t2 where t2.id = t1.id)
then 'Y' else 'N'
end) as in_table2
from table1 t1;
This should be much quicker and efficient than using exists/subqueries:
SELECT t1.id ,
CASE WHEN t2.id IS NULL
THEN 'N'
ELSE 'Y'
END AS in_table2
FROM table1 t1
LEFT JOIN TABLE2 t2 ON t1.id = t2.id;
By left joining you maintain visibility of the records on table2, and if the ID is null, you know it exists on table1 but not on table2, so you can safely use a case statement to show Y or N based on t2.id.
A query with left join is not returning records, although the where clause from the left table should find a single record. In this case, it should return a record with the fields from the left table containing values and from the right table null, since there is no match between them.
Apparently there is a problem with the use of case that references the right table on the join expression.
In SQL Server the same query worked as expected.
select
t1.Description, t2.Description
from
A t1
left join
B t2
on
t1.Id = t2.Id and
1 = case when (
t2.Id = t2.Id and
(select t3.Flag from C t3 where t3.ID_B = t2.Id) = 'S'
) then 1 else 0
end
where t1.Id = 1
Result: no rows returned.
Then I moved the expression t2.Id = t2.Id (that is here only to demonstrate the problem and should always return true, apparently) out of the case expression.
select
t1.Description, t2.Description
from
A t1
left join
B t2
on
t1.Id = t2.Id and
t2.Id = t2.Id and
1 = case when (
(select t3.Flag from C t3 where t3.ID_B = t2.Id) = 'S') then 1 else 0
end
where t1.Id = 1
Result: one row returned.
The queries above only serve to demonstrate the problem, are not useful in a real situation and not optimized.
I want to know if anyone knows any limitation of Oracle related to this case. So far we believe it is a bug.
Data used:
A: Id=1, Description=Item A1;
B: Id=1, Description=Item B1;
C: Id=1, Id_B=2, Flag=S.
CREATE TABLE t1 AS (SELECT 1 ID FROM dual);
CREATE TABLE t2 AS (SELECT 2 ID FROM dual);
CREATE TABLE t3 AS (SELECT 2 id_b, 's' flag FROM dual);
SELECT t1.*
FROM t1 LEFT JOIN t2
ON t1.ID = t2.ID
AND 1 = CASE WHEN t2.id = t2.id and (SELECT flag FROM t3 WHERE t3.id_b = t2.ID) = 's' THEN 1 ELSE 0 END
where t1.id = 1;
The output: no rows selected
The result looks strange, I suppose it can be a bug.
Oracle documentation only states
https://docs.oracle.com/cd/B28359_01/server.111/b28286/queries006.htm#SQLRF52337
You cannot compare a column with a subquery in the WHERE clause of any
outer join, regardless which form you specify.
By looking on the plan of the above query I can see that this condition:
AND 1 = CASE WHEN t2.id = t2.id and (SELECT flag FROM t3 WHERE t3.id_b = t2.ID) = 's' THEN 1 ELSE 0 END
Is interpreted as:
CASE WHEN (T2.ID(+)=T2.ID(+) AND (SELECT FLAG FROM T3 T3 WHERE T3.ID_B=:B1)='s') THEN 1 ELSE 0 END =1
and is calculated after the join.
I suppose that Oracle cannot calcuate the CASE until the join is performed (because of T2.ID(+)=T2.ID(+))
Your assumption that t2.id = t2.id is always true is wrong. If the value were NULL that would be treated as false. I don't believe that is relevant for this particular example, but just to clarify.
The question is how is a left join processed. The idea is simple. The on clause is processed. If there are no matches, then the row from the first table is kept. This is regardless of what is in the on clause. (This is a functional description; there are many possible implementations.)
Based on your sample data, Oracle is incorrect. One row should be returned. The SQL Server example should also return one row. I suspect that the data might be subtly different; I personally have never had issues with left joins in SQL Server (or Oracle).
Using SQLFiddle Oracle 11g R2 (thanks to Shannon Severance) your first query gives Record Count: 0 but by simply removing the CASE we get Record Count: 1. (Note the renaming of t2Description.)
create table A (ID number(38), Description varchar(10));
create table B (ID number(38), Description varchar(10));
create table C (ID number(38), ID_B number(38), Flag varchar(10));
insert into A values(1, 'Item A1');
insert into B values(2, 'Item B1');
insert into C values(1, 2, 'S');
select
t1.Description, t2.Description as t2d
from
A t1
left join
B t2
on
t1.Id = t2.Id and
t2.Id = t2.Id and
(select t3.Flag from C t3 where t3.ID_B = t2.Id) = 'S'
where t1.Id = 1
This suggests that it has something to do with CASE being miscalculated.
Note that in the ON t2.Id is at least sometimes (correctly) taken to be the value from the FROM cross product, not NULL which it is after the ON:
select
t1.Description, t2.Description as t2d
from
A t1
left join
B t2
on
-- for above data t2.id should be 1 here
t2.id is null
where t1.Id = 1
-- for above data t2.id should be null here
DESCRIPTION T2D
Item A1 (null)
I found this link: Outer Join Bug in Oracle 12c?
I want to update a column of table based on a condition. It should check if the value exists in other table, if exists then value from other table will be used else value from same table will be used.
Update table1
Set column1=(select t2.alias||’#email.com’ as new_name
From table2 t2, table1 t1, table3 t3
Where t1.id=t2.id
And t1.id=t3.id
Else if
Select t2.alias is null or t2.id is null
then column1= select t1.id||’#email.com’ as new_name
Any suggestions on this??
Thanks in advance.
Does this do what you want?
Update table1
Set column1 = (select (case when t2.alias is not null and t2.id is not null then t2.alias
else t1.id
end) ||'#email.com' as new_name
From table1 t1 left outer join
table2 t2
on t1.id=t2.id
);
I removed table3 because it does not seem to be used. With left join is won't even be filtering any results.
By leaving out the "insert" half of the merge statement, you can make it into a strictly update statement:
MERGE INTO table1 t
USING(
SELECT t2.id, t2.Alias
FROM table2 t2
JOIN table1 t1
ON t1.id = t2.id
AND t1.Alias <> t2.Alias
) tu
on( tu.id = t.id )
WHEN MATCHED THEN
update set t.Alias = tu.Alias;
The query will return only those existing values from table2 that differ from table1. To make it exactly match your requirement (update table1 if any value exists in table2) then remove the line AND t1.Alias <> t2.Alias, but why update a field to the same value it already has?