Oracle NOT IN does not work will nullable fields - sql

I have to compare two tables with identical structure (int not null,int not null, varchar2). In both tables field3 is a nullable.
I have the next SQL:
Select
t1.field1, t1.field2, t1.field3)
From
table1 t1
Where (field1,field2,field3)
not in
(select field1,
field2,field3
from table2 t2)
When the field3 is NULL in any of them (t1 or t2) the query does not return any row. For instance I want to return a row from this data but it returns nothing at all.
Table 1
field1 field2 field3
1 2 <NULL>
Table 2
field1 field2 field3
1 2 'some text data'
There is workaround to fix such thing by using the NVL function: NVL(field3, 'dummytextorwhatever') but I dont want to include such horrible thing into my code. Any idea to solve this problem with nullable fields?
Thank you!

This is known behavior with NOT IN when there are nulls in either the main table or the sub-query's result sets. As #DrCopyPaste puts it so well
"when writing WHERE value NOT IN (x, y, z) this will be internally interpreted as WHERE value != x AND value != y AND value != z, and comparing against NULL (either for equality or unequality) always yields FALSE"
The simple answer is to use NOT EXISTS:
Select
t1.field1, t1.field2, t1.field3)
From
table1 t1
Where not exists
(select null from table2 t2
where t2.field1 = t1.field1
and t2.field2 = t1.field2
and t2.field3 = t1.field3 )
An anti-join will produce the same result
Select
t1.field1, t1.field2, t1.field3)
From
table1 t1
left join table2 t2
on t2.field1 = t1.field1
and t2.field2 = t1.field2
and t2.field3 = t1.field3
where t2.field1 is null
"why do you select a null at the beginning?"
Because with NOT EXISTS it doesn't matter what the sub-query returns. All that matters is that it returns a non-empty result set. It could have been 1 or field1 but it really doesn't matter, so why not null?

Try to use NVL or Coalesce operators, like this
Select
t1.field1, t1.field2, t1.field3
From
table1 t1
Where (nvl(field1,0),nvl(field2,0),nvl(field3,0))
not in
(select nvl(field1,0),nvl(field2,0),nvl(field3,0)
from table2 t2)
but if in tables data there is some data equals 0 select will be return that row, because nvl(field1,0)=nvl(field2,0) when field1=0 and field2=null, so you can use any value(you should be confident
) wich no exists in your tables data for example -99(nvl(field,-99))
or you can use exists/not exists

Try not exists
Select
t1.field1,
t1.field2,
t1.field3
From
table1 t1
where not exists
(select 1
from table2 t2
where
t1.field1=t2.field1
and t1.field2=t2.field2
and t1.field3=t2.field3
)
Sample test
with table1(field1,field2,field3) as
(select 1,2,null from dual),
table2(field1,field2,field3) as
(select 1,2,'something' from dual)
Select
t1.field1,
t1.field2,
t1.field3
From
table1 t1
where not exists
(select 1
from table2 t2
where
t1.field1=t2.field1
and t1.field2=t2.field2
and t1.field3=t2.field3
)
Output
FIELD1 FIELD2 FIELD3
1 2

Based on your query, you are trying to find all the times in table1 that do not exist in table2. Instead of NOT IN, consider using a MINUS...
Select t1.field1, t1.field2, t1.field3
From table1 t1
Minus
select t2.field1, t2.field2, t2.field3
from table2 t2;

Related

Join two tables, but not all keys are available in both

I could not find a solution for this. Maybe also because it is a bit difficult to explain.
I got table1 with key1.
I got table2 with key2, value1.
My Statement:
Select * From table1 As t1
Left Join table2 As t2 On t2.key2 = t1.key1
Where t2.value1 Is Null Or t2.value1 > 123;
Now I have some key1 in table1, but not the corresponding key2 in table2. Because of that I do not have those keys in my result, but I need them in it. If the key in not in table2, then value1 sadly is not null. Is there some 'value' I can check there?
Normally, the condition on the left join would go in the on clause.
I suspect you want:
Select *
From table1 t1 Left Join
table2 t2
On t2.key2 = t1.key1 and t2.value1 > 123;
This will return all rows from t1 along with matching rows -- if any -- from table2.
Your version will return all rows from table1 where there is either a matching row with value1 > 123 or no matching rows at all. In other words, it will filter out rows from table1 where all values in table2 are <= 123. This is only useful on very rare occasions.
Try the below query and check
Select * From table1 As t1 INNER JOIN table2 As t2 On t2.key2 = t1.key1 Where t2.value1 Is NULL Or t2.value1 > 123;
It should work for your logic

select value from another table if record is found else, use the value on current table

i'm trying to get the values from another table2 if a match exist, else, select the value in table1 but it's taking a long time to execute the query.
select table1.field1, table1.field2,
case
when exist (select top 1
from table2
where table2.field1=table1.field3
and table2.field2 is not null
order by date desc)
then (select top 1
from table2
where table2.field1=table1.field3
and table2.field2 is not null
order by date desc)
else table1.field3
end
from table1
any other way to re-write this query?
please help! n00b here :(
Yes. Use outer apply:
select t1.field1, t1.field2, coalesce(t2.??, t1.field3)
from table1 t1 outer apply
(select top 1 t2.*
from table2 t2
where t2.field1= t1.field3 and t2.field2 is not null
order by t2.date desc
) t2;
It is unclear what field you are talking about, because it is missing from the question; hence, the ??.
SELECT t1.field1, t1.field2, coalesce(t2.field2,t1.field3) as field3
FROM table1 t1
LEFT JOIN table2 t2
on t2.field1=t1.field3
and t2.date = (select max(date) from table2 t2e where t2.field1=t2e.field2)
SELECT
table1.field1,
table1.field2,
COALESCE(table2.field2,table1.field3)
FROM
table1
LEFT OUTER JOIN
table2
ON
table2.field1=table1.field3
COALESCE skips all the null values in the list.
LEFT OUTER JOIN returns all values from the first table, but puts null values where there is no corresponding row in the second table.

Proper way to query from 2 tables while giving hierarchy to one table.

I am currently trying to obtain data from two tables that have the same columns. The values for primary key "ID" of both tables may exist in one or both tables. Even with same primary keys, the values in different columns may not be the same for both tables. My question is given I have an ID testID, how do I query where in I first check table1 if it exists. If it exists in table1 I use the details found in table1, otherwise check table2 and use details in table2 if it exists in table2.
Either use a FULL OUTER JOIN:
select
case when t1.id is not null then t1.field1 else t2.field1 end as field1,
case when t1.id is not null then t1.field2 else t2.field2 end as field2,
...
from table1 t1
full outer join table2 t2 on t2.id = t1.id
where t1.id = :testid or t2.id = :testid;
Or UNION ALL in combination with NOT EXISTS:
select field1, field2, ...
from table1
where id = :testid
union all
select field1, field2, ...
from table2
where id = :testid and not exists (select * from table1 where id = :testid);
The possible way is to use FULL OUTER JOIN
SELECT t1.id,
t2.id,
CASE
WHEN t1.id IS NOT NULL
AND t2.id IS NOT NULL
THEN 'ID in both sources'
WHEN t1.id IS NULL
THEN 'ID in T2 only'
WHEN t2.id IS NULL
THEN 'ID in T1 only'
END source_key
FROM t1
FULL OUTER JOIN t2
ON t1.id = t2.id
WHERE t1.id = 1 -- your test_id here
OR t2.id = 1; -- your test_id here
Cheching if T1.ID/ T2.ID is not NULL you get the information if the record is defined in the respective source table.

Differences between two tables

Data is like so, (table1 links up to table2) on table1.col2 = table2.col2
Based on that criteria,
Employee 5 below assigned to Area 1 in first table, however in second table that employee is not assigned to Area 1, so the result that would return would only be the first record of the first table (emp5, a1)
Example below
Table1
Col1 Col2
-------------
emp5 A1
emp6 A1
emp5 A2
Table2
Col1 Col2
--------------
emp7 A1
emp6 A1
emp5 A2
You can use MINUS, it is more intuitive. The syntax can be different in SQL Server, MySQL or Oracle, like you can see http://blog.sqlauthority.com/2008/08/07/sql-server-except-clause-in-sql-server-is-similar-to-minus-clause-in-oracle/
But I like MINUS, for instance
select
t1.Col1,
t1.Col2
from table1 t1
MINUS
select
t2.Col1,
t2.Col2
from table2 t2
This way, you can think like sets (math)!
This is tricky. You need employees who are in both tables. Then you need to check that col2 is different on one of the rows.
The following does this comparison using union all:
select col1, col2, max(which)
from ((select col1, col2, 1 as which
from table1 t1
where exists (select 1 from table2 t2 where t2.col1 = t1.col1)
) union all
(select col1, col2, 2 as which
from table2 t2
where exists (select 1 from table1 t1 where t2.col1 = t1.col1)
)
) tt
group by col1, col2
having count(*) = 1
This will also tell you which table has the extra row.
select table1.*
from table1
left join table2
on table1.col1 = table2.col1
and table1.col2 = table2.col2
where table2.col1 is null
if you only want those from table 1 that are also assigned to another project then
select distinct table1.*
from table1
join table2 as t2not
on t2not.col1 = table1.col1
and t2not.col2 <> table1.col2
left join table2
on table1.col1 = table2.col1
and table1.col2 = table2.col2
where table2.col1 is null

SQL command usage of in / or

I have an sql command similar to below one.
select * from table1
where table1.col1 in (select columnA from table2 where table2.keyColumn=3)
or table1.col2 in (select columnA from table2 where table2.keyColumn=3)
Its performance is really bad so how can I change this command? (pls note that the two sql commands in the paranthesis are exactly same.)
Try
select distinct t1.* from table1 t1
inner join table2 t2 ON t1.col1 =t2.columnA OR t1.col2 = t2.columnA
This is your query:
select *
from table1
where table1.col1 in (select columnA from table2 and t2.keyColumn = 3) or
table1.col2 in (select columnA from table2 and t2.keyColumn = 3);
Probably the best approach is to build an index on table2(keyColumn, columnA).
It is also possible that in has poor performance characteristics. So, you can try rewriting this as an exists query:
select *
from table1 t1
where exists (select 1 from table2 t2 where t2.columnA = t1.col1 and t2.keyColumn = 3) or
exists (select 1 from table2 t2 where t2.columnA = t2.col1 and t2.keyColumn = 3);
In this case, the appropriate index is table2(columnA, keyColumn).
Assuming you're doing this in VFP, use SYS(3054) to see how the query is being optimized and what part is not.
Are the main query and subqueries fully Rushmore-optimisable?
Since the subqueries do not appear to be correlated (i.e. they don't refer to table1 then as long as everything is fully supported by indexes you should be fine.