Seeking suggestion about Left join - sql

I have two tables t1 & t2. In t1, there are 1641787 records. In t2, there are 33176007 records. I want to take two columns from table2 and keep everything of t1. When I use left join with t1 to t2, I got more records than t1. I would like to get a similar number of records as t1 after joining. Please give me a suggestion. Here is my code:
SELECT t1.*,
t2.City
FROM t1
LEFT JOIN
t2
ON t1.ID = t2.ID;

You can aggregate and choose an arbitrary value:
select t1.*, t2.city
from t1 left join
(select t2.id, any_value(t2.city) as city
from t2
group by t2.id
) t2
on t1.id = t2.id;

Related

How to join these tables safely?

I have a table Table1. My application code reads from Table1 like this:
Select id, table2_id, table3_id from Table1
I would like it to also return values name from tables Table2 and Table3 by changing my query like this:
select t1.id, t1.table2_id, t1.table3_id, t2.name, t3.name
from table1 t1
left outer join table2 t2 on t1.table2_id = t2.id
left outer join table3 t3 on t1.table3_id = t3.id
However, I don't want to change the behavior of the original query, which returns 1 result per row in Table1.
I believe my changes to the query are safe because they t2.id and t3.id are unique columns, so Table2 and Table3 will contain at most 1 record for each Table3 record. If I was to user inner join, this changes would not be safe, because my query would return no results if Table2 or Table3 happen to not contain the expected record.
For this scenario, are my changes safe and correct? Or is it necessary to write some subqueries to join on?
Your query looks safe:
select t1.id, t1.table2_id, t1.table3_id, t2.name, t3.name
from table1 t1 left join
table2 t2
on t1.table2_id = t2.id left join
table3 t3
on t1.table3_id = t3.id;
You can also phrase this using correlated subqueries (or a lateral join):
select t1.*,
(select t2.name from table2 t2 where t1.table2_id = t2.id) as t2_name,
(select t2.name from table3 t3 where t1.table2_id = t3.id) as t3_name,
from table1 t1;
This is even more of a guarantee that there are no duplicates. If there were, the query would return an error.

Get values from one table that are not in another

The first query works to get a value from t1 that is not in t2.
select t1.*
from table1 t1
where t1.id = '1'
and t1.id2 not in (select t2.id2 from table2 t2 where t2.id='1')
But how do how can I do the same thing using this format instead of an inner select?
select t1.*
from table1 t1, table2 t2
where t1.id = '1'
and t2.id = t1.id
and t1.id2 != t2.id2
You can use a LEFT OUTER JOIN:
select t1.*
from table1 t1
LEFT OUTER JOIN table2 t2
ON t1.id = t2.id
WHERE t2.id IS NULL
LEFT OUTER JOIN says to take all results from the LEFT-most table (table1 here) and only those results from your joined table (table2 here) that match the ON condition. By filtering in your WHERE clause to drop any results where table2.id IS NULL you effectively only leave T1 results that are unmatched to T2.
Also try to stay away from the FROM table1, table2 type of implicit join. It's very old school. INNER JOIN, LEFT OUTER JOIN, RIGHT OUTER JOIN are better options for nearly any situation.

Convert to join query

select t.* from table1 t where t.id NOT IN(
select Id from t2 where usrId in
(select usrId from t3 where sId=value));
I the result i need is like if there are matching id's in t1 and t2 then those id's should be omitted and only the remaining rows should be given to me. I tried converting into join but it is giving me the result i wanted. Below is my join query.
SELECT t.* FROM table1 t JOIN table2 t2 ON t.Id <> t2.Id
JOIN table3 t3 ON t3.Id=t2.Id WHERE t3.sId= :value
This doesn't feth me the correct result. it was returning all the rows, but i want to restrict the result based on the matching id's in table t1 and table t2. Matching id's should be ommited from the result.I will be passing the value for sId.
I believe this to be an accurate refactor of your query using joins. I don't know if we can do away with the subquery, but in any case the logic appears to be the same.
select t1.*
from table1 t1
left join
(
select t2.Id
from table2 t2
inner join table3 t3
on t2.usrId = t3.usrId
where t3.sId = <value>
) t2
on t1.Id = t2.Id
where t2.Id is null
Let's break down and solve problem step by step.
So your query
select t.* from table1 t where t.id NOT IN(
select Id from t2 where usrId in
(select usrId from t3 where sId=value));
on converting the inner query to JOIN will yield
select t.* from table1 t where t.id NOT IN
(SELECT T2.ID FROM T2 JOIN T3 on T2.UsrID =T3.UsrID and T3.sID=value)
which on further converting to JOIN with outer table will be
select t.* from table1 t LEFT JOIN
(SELECT T2.ID FROM T2 JOIN T3 on T2.UsrID =T3.UsrID and T3.sID=value)t4
ON t.id =T4.ID
WHERE t4.ID is NULL
In case you completely want to remove sub-query you can try like this
SELECT t.*
FROM table1 t
LEFT JOIN T2
ON T.ID=T2.ID
LEFT JOIN T3
ON T3.UsrId=T2.UsrID AND T3.sId=value
WHERE T3.UsrID IS NULL

Multiple joins in a sql query - which is best option

I want to use a sql query with multiple joins similar to the example below.
SELECT t1.column1, t1.column2, t1.column3
FROM
table1 t1
LEFT JOIN table2 t2 ON (t1.id1 = t2.id)
LEFT JOIN table3 t3 ON (t1.id1 = t3.id)
JOIN table4 t4 ON t1.id2 = t4.id
WHERE
...
Would this give different results than the following query:
SELECT t1.column1, t1.column2, t1.column3
FROM
table1 t1
LEFT JOIN table2 t2 ON (t1.id1 = t2.id)
LEFT JOIN table3 t3 ON (t2.id = t3.id)
JOIN table4 t4 ON t1.id2 = t4.id
WHERE
...
If they are both 'correct' is the second more efficient than the first?
Thanks
The queries are different, so this isn't a performance issue. The difference are these lines:
LEFT JOIN table3 t3 ON (t1.id1 = t3.id)
and
LEFT JOIN table3 t3 ON (t2.id1 = t3.id)
For the first, t3.id needs to only match t1.id. For the second, it needs to match t2.id1, which in turn must also match t1.id. In other words, the second version requires that the id be in both t1 and t2.
This is because of the LEFT JOIN. The queries would be equivalent if they used INNER JOIN.
The second one is more efficient because it will always return the same or less amount of data.
In the first query you are asking for all the records that are both in table1 and table4 + records from table2 if they exists in table1 + records from the table3 if they exists in table1.
In the second query you are asking for all the records that are both in table1 and table4 + records from table2 if they exists in table1 + records from the table3 if they exists BOTH in in table1 and in table2

join with the on supplied later

I have a query in the following format
select
*
from
Table1 t1
inner join Table2 t2
inner join Table3 t3 on t2.ID = t3.ID
on t3.ID = t1.ID
What I do know:
Not providing the last on condition results in an error.
Additionally changing the first join condition from on t2.ID = t3.ID to on t1.ID = t2.ID results in an error that t1.ID could not be bound.
Obviously the above examples are arbitrary and may not actually produce a practically useful result. However, an explanation of what providing the on later is actually doing would be great.
Thanks
EDIT
I'm not trying to change the question to something that works but to understand what MSSQL is doing when I provide it.
You can use the format you specified (presuming the correct table aliases), if you use parenthesis.
Select ... -- never use Select *
From (Table1 As T1
Join Table2 As T2
On T2.ID = T1.ID)
Join Table3 As T3
On T3.ID = T1.ID
However, with equi-joins (inner joins) it really makes no difference and it is easier to read if you do not use parenthesis. However, this format is very useful with outer joins. Take the following two examples:
Example 1
Select ...
From Table1 As T1
Left Join Table2 As T2
On T2.T1_ID = T1.ID
Join Table3 As T3
On T3.T2_ID = T2.ID
Example 2
Select ...
From Table1 As T1
Left Join (Table2 As T2
Join Table3 As T3
On T3.T2_ID = T2.ID)
On T2.T1_ID = T1.ID
Suppose in this situation, that T3.T2_ID is a non-nullable foreign key to Table2. In Example1, the Inner Join to Table3 will effectively filter out rows that would have been null because the given T2.T2_ID does not exist in Table1. However, in the second example, the join between Table2 and Table3 is done before the Left Join to Table1 is processed. Thus, we'll get the same rows from Table1 and Table2 as:
Example 3
Select ...
From Table1 As T1
Left Join Table2 As T2
On T2.T1_ID = T1.ID
Assuming you meant t1 rather than t, then your query:
select
*
from
Table1 t1
inner join Table2 t2
inner join Table3 t3 on t2.ID = t3.ID
on t3.ID = t1.ID
...can be made rather more clear by the addition of the brackets it doesn't really need:
select
*
from
Table1 t1
inner join
(Table2 t2 inner join Table3 t3 on t2.ID = t3.ID) on t3.ID = t1.ID
Effectively, you're explicitly saying "join t2 to t3, then join t1 to that."
Does that help?
First off--you don't define what t is
Table1 is aliased t1
Table2 is aliased t2
Table3 is aliased t3
But there is no plain t.
Second, you are not doing a join of t1 to t2, but of t1 to t3 and then t3 to t2. That will work. If there is a relation between t1 and t2 (t1.ID=t2.ID) then that "on" statement should directly follow the inner join statement for t2:
select
*
from
Table1 t1
inner join Table3 t3 on t1.ID = t3.ID
inner join Table2 t2 on t3.ID = t2.ID
UPDATE (based on your update)
are t1.ID, t2.ID, and t3.ID all the same data type?