Can I replace Union all with any kind on joins? - sql

I have the below query for negative testing, But I want to replace the union all if possible.
select A.*
from A
join B
on A.COL1=B.COL1
where B.COL3 is null
union all
select A.*
from A
join B
on A.COL2=B.COL4
where B.COL5 is null;
Need to get data from both SQL without using union all

You could combine the two queries into a single join and collapse the where condition into it:
select A.*
from A
join B on (A.COL1 = B.COL1 and B.COL3 is null) or
(A.COL2 = B.COL4 and B.COL5 is null)

Since you're only after data from Table A you don't need the join to table B at all and can re-write this as an Exists...
SELECT A.*
FROM A
WHERE EXISTS (SELECT 1
FROM B
WHERE A.COL1=B.COL1 and B.COL3 is null)
OR EXISTS (SELECT 1
FROM B
WHERE A.COL2=B.COL4 and B.COL5 is null)
But this has likely has two issues:
I'm pretty sure if you look at the execution plan for both; you'll find the union all is more efficient because it operates at a set level instead of a row level ad the OR needed in this is slower.
This will return 1 record from A instead of 2 from that of a union all. had it been a union; this should/would return the same results and avoid the union. But simply put you want the same data from A twice (or more depending on cardinality of joins)

SELECT A.*
FROM A
JOIN B ON (A.COL1 = B.COL1 OR A.COL2 = B.COL4) AND B.COL3 IS NULL;

Related

SQL to Match columns from 2 different tables using a Select statement

I have 2 tables A & B with columns 6 columns in each table.
Table A has lesser rows than table B.
I want to write a Select Statement where if the below condition satisfies
-----A.Col1=B.Col1 and A.Col2=B.Col2 and A.Col3=B.Col3 and A.Col4=B.Col4 and A.Col5=B.Col5 and A.Col6=B.Col6-----
if all conditions are satisfied then in a new column say "Match" else "NoMatch"
How do I do that ?
I would suggest using exists. If you want a new column in A:
select a.*,
(case when exists (select 1
from b
where A.Col1 = B.Col1 and A.Col2 = B.Col2 and
A.Col3 = B.Col3 and A.Col4 = B.Col4 and
A.Col5 = B.Col5 and A.Col6 = B.Col6
)
then 'match' else 'nomatch'
end) as flag
from a;
Note: If you want the new column on B, the logic is the same but the two tables are reversed.
If any of the columns can have NULL values, then you need to take that into account.
You can use CASE statement and I am considering that you need all data from tableB and matching data from tableA as follows:
select b.*, a.*,
case when A.Col1=B.Col1 and A.Col2=B.Col2
and A.Col3=B.Col3 and A.Col4=B.Col4
and A.Col5=B.Col5 and A.Col6=B.Col6
then 'Match'
else 'No match'
end as res
from tableB b
left join TableA a
on A.Col1=B.Col1 and A.Col2=B.Col2
and A.Col3=B.Col3 and A.Col4=B.Col4
and A.Col5=B.Col5 and A.Col6=B.Col6

PostgreSQL comparing null values in case statement

when I wrote a case statement to compare values in tables, it came unstuck with variables that are null. It thinks they are different (note: col1 is a character field).
select a.id,
a.col1 as a_col1,
b.col1 as b.col1,
case when a.col1=b.col1 then 0 else 1 end as chk_col1
from tablea a,
tableb b
where a.id=b.id;
... chk_col1 is always 0 when both col1's are null. I've tried
coalesce(a.col1,'null') as coalesce(b.col1,'null')
but this didn't work either. It still returned 1 for chk_col1.
Postgres supports the null-safe comparison operator is not distinct from. So, try this:
select a.id,
a.col1 as a_col1,
b.col1 as b.col1,
(case when a.col1 is not distinct from b.col1 then 0 else 1 end) as chk_col1
from tablea a join
tableb b
on a.id = b.id;
Personally, I would leave the value as a boolean:
select a.id, a.col1 as a_col1, b.col1 as b.col1,
(a.col1 is distinct from b.col1) as chk_col1
from tablea a join
tableb b
on a.id = b.id;
Also note that I used proper, explicit, standard, readable JOIN syntax.
SOLUTION! :
The variable referenced in the colaesce function must be the calculated one, i.e.
coalesce(a_col1,'null') as coalesce(b_col1,'null')
Another thing I discovered. Let's say col2 is numeric. The above doesn't work, you'd need to use a 0. Or ... more cunningly, you can use '0', i.e.
coalesce(a_col2,'0') as coalesce(b_col2,'0')
This is handy to know if you want to generate some code to compare tables by referencing pg_tables or svv_columns. In this code I had 2 tables that I'd created by reading svv_columns metadata table, and I wanted to created a case statement for each variable, so I'd have the two variables from each table side by side plus a check variable which I'd use for summarising later:
select ' coalesce(a.'||a.column_name||',''0'') as a_'||a.column_name||', coalesce(b.'||b.column_name||',''0'') as b_'||b.column_name||', case when a_'||a.column_name||'=b_'||b.column_name||' then 0 else 1 end as chk_'||a.column_name||','
from tbl_a_vars a,
tbl_b_vars b
where a.column_name=b.column_name;

Update rows where a combination of multiple attributes is IN the table

I want to update the fields of a table WHERE the combination of three other attributes is IN another table. I am having some difficulties with the syntax, so any help is appreciated.
You would normally use EXISTS for this:
SELECT *
FROM a
WHERE EXISTS (
SELECT 1
FROM b
WHERE a.col1 = b.col1 AND a.col2 = b.col2 AND a.col3 = b.col3
)
Convert the above to an UPDATE query.

How to choose a proper filter for an sql join

If I have table A and table B, each with one column:
A:
col1
1
2
3
1
B:
col1
1
1
4
and I want all rows from A and the matching rows from B, only when the column has non null value in both tables, which one should I use?
select * from A left join B on A.col1 = B.col1 and A.col1 is not null AND B.col1 is not null;
select * from A left join B on A.col1 = B.col1 where A.col1 is not null OR B.col1 is not null;
select * from A left join B on A.col1 = B.col1 and (A.col1 is not null OR B.col1 is not null;)
My guess is that the first and the third are the same and will provide the desired output.
If you want to skip null values and you want to link both tables only on existing values you should use an INNER JOIN, the null check is redundant:
SELECT A.*
FROM A INNER JOIN B ON A.col1 = B.col1
NULL will never match any other value (not even NULL itself), unless the join condition explicitly uses the IS NULL or IS NOT NULL predicates.
In a comment you said you are checking for more than nulls in this case I would probaly take thederived table or CTE approach. Dervied table shown below as you did not specify which database backend, so I don't know if you can use CTEs.
select
from
(select from tablea where test is not null or test <>'' or test<>'N/A') a
JOin
(select from tableb where test is not null or test <>'' or test<>'N/A')b
ON a.col1 = b.col1
You just need
select * from A left join B on A.col1 = B.col1
NULL will never match anything (when not compared with IS NULLand the like), therefore NULL in A won't match anything in B.
Since you want all the rows from A, below query should work:
select * from A left outer join B on A.col1 = B.col1 where A.col1 is not null and A.col1<>'N/A' and A.col1<>''
http://sqlfiddle.com/#!2/98501/14

Restricting a LEFT JOIN

I have a table, let's call it "a" that is used in a left join in a view that involves a lot of tables. However, I only want to return rows of "a" if they also join with another table "b". So the existing code looks like
SELECT ....
FROM main ...
...
LEFT JOIN a ON (main.col2 = a.col2)
but it's returning too many rows, specifically ones where a doesn't have a match in b. I tried
SELECT ...
FROM main ...
...
LEFT JOIN (
SELECT a.col1, a.col2
FROM a
JOIN b ON (a.col3 = b.col3)) ON (a.col2 = main.col2)
which gives me the correct results but unfortunately "EXPLAIN PLAN" tells that doing it this way ends up forcing a full table scan of both a and b, which is making things quite slow. One of my co-workers suggested another LEFT JOIN on b, but that doesn't work because it gives me the b row when it's present, but doesn't stop returning the rows from a that don't have a match in b.
Is there any way to put the main.col2 condition in the sub-SELECT, which would get rid of the full table scans? Or some other way to do what I want?
SELECT ...
FROM ....
LEFT JOIN ( a INNER JOIN b ON .... ) ON ....
add a where (main.col2 = a.col2)
just do a join instead of a left join.
What if you created a view that gets you the "a" to "b" join, then do your left joins to that view?
Select ...
From Main
Left Join a on main.col2 = a.col2
where a.col3 in (select b.col3 from b) or a.col3 is null
you may also need to do some indexing on a.col3 and b.col3
First define your query between table "a" and "b" to make sure it is returning the rows you want:
Select
a.field1,
a.field2,
b.field3
from
table_a a
JOIN table_b b
on (b.someid = a.someid)
then put that in as a sub-query of your main query:
select
m.field1,
m.field2,
m.field3,
a.field1 as a_field1,
b.field1 as b_field1
from
Table_main m
LEFT OUTER JOIN
(
Select
a.field1,
a.field2,
b.field3
from
table_a a
JOIN table_b b
on (b.someid = a.someid)
) sq
on (sq.field1 = m.field1)
that should do it.
Ahh, missed the performance problem note - what I usually end up doing is putting the query from the view in a stored procedure, so I can generate the sub-queries to temp tables and put indexes on them. Suprisingly faster than you would expect. :-)