left join on MS SQL 2008 R2 - sql

I'm trying to left join two tables. Table A contains unique 100 records with field_a_1, field_a_2, field_a_3. The combination of field_a_1 and field_a_2 is unique.
Table B has multi-million records with multiple fields. field_b_1 is same as field_a_1 and field_b_2 is same as field_a_2.
I join the two tables together like this:
select a.*, b.*
from a
left join b
on field_a_1 = field_b_1
and field_a_2 = field_b_2
Instead of getting 100 records, I get multi-million records. Why is this?

Because table B has multiple rows for each table A entry.
For example:
TableA (ID)
1
2
3
TableB (ID, data)
1 hello
1 world
1 foo
1 bar
2 data
2 words
2 more
3 words
3 boring
If you left join from TableA to TableB, you will get a row for every TableB record that matches a TableA record - ie. all of them.
Can you explain what results you are looking for?

Because a left join returns all of the rows from the first table + all of the matching rows from the second table. Which of the millions of matching rows did you expect to get?

Left join or inner join don't really make a difference. A JOIN will return all rows that match the join condition. So if table b has millions of rows that match the JOIN criteria, then all the rows will be returned.
Depending on what you wish to accomplish you should consider using the DISTINCT keyword or GROUP BY to perform aggregate functions.

Related

Query is not returning the correct count while joining two tables in SQL Server 2017

Table A has 2500 rows
Table B has 12000 rows
Common column between these two tables is Email_ID.
Note: Email ID column may have nulls in table B.
If I join these tables using this query
Select count(*)
from A
left join B on A.Email_ID = B.Email_ID
it is returning a value of more than 2500 records.
But I want only 2500 records from Table A
Please suggest how to do this.
* will return total record count regardless of duplicate or nulls :
Select count(b.Email_ID) -- if you have a duplicate email ids then use `distinct`
from A left join
B
on A.Email_ID = B.Email_ID;
However, you can also use exists, join will return multiple rows if b table has more rows matching email id available in table a. So, count will always be increased.
select count(a.Email_ID)
from a
where exists (select 1 from b where b.Email_ID = a.Email_ID);
If you want only 2,500 hundred records, then why are you joining? That is, these return 2,500:
select count(*)
from A;
select count(distinct A.Email_id)
from A left join
B
on A.Email_ID = B.Email_ID;
If you actually want one row returned from B for each A, then use outer apply rather than left join:
select . . .
from a outer apply
(select top (1) b.*
from b
where b.email_id = a.email_id
) b;
This returns an arbitrary matching row from b. Normally, the subquery would have an order by to provide more control over the matching row. For instance, order by newid() would return a random row.

how to fetch distinct records from 3 tables which should contain all the values from 1 table

I have query which should fetch all the matching records from 3 tables. other records which are not common in three tables, it should union the records from first table..
EX:
select a.x,a.y,a.z
from table1 a,table2 b,table3 c
where a.x=b.x
and b.x=c.x;
above query will fetch common records among all 3 tables.
I need to add the records to my result set which are not present in table2 or table3.
Records should come like below:
1 abc acd
2 xyz xzy
3 pqr prq
4 null null -- incase 4 is not present in either table2 or table3
You need to use an outer join instead:
select a.x,a.y,a.z
from table1 a
left join table2 b on a.x=b.x
left join table3 c on b.x=c.x;
A Visual Explanation of SQL Joins
You state
other records which are not common in three tables, it should union
the records from first table.
Then state
I need to add the records to my result set which are not present in
table2 or table3.
For the first case use a full outer join which will return all rows from all tables. For the second case use left joins which will leave out rows that have no match to table1.

Update records only once with LEFT OUTER JOIN

I am trying to UPDATE Table A with records in Table B based on a LEFT OUTER JOIN based on two columns of data. So, for example, the JOIN from Table A might be USA-4 to match Table B's USA-4. Returning a result of XYZ to a different column. The problem I am having is the Table B contains multiple USA-4's and as a result Table A appears to be updating more than once. Is there a way to fix this? A different JOIN type?
UPDATE tablea a
SET USA-4 = (SELECT distinct USA-4 FROM tableb b WHERE a.id=b.id)

SQL Inner Join and further requirements

I would like to return table entries from an inner join which do not have any matching entries in the second column.
Lets consider the following two tables:
Table one:
Name Number
A 1
A 2
A 4
Table two:
Name ID
A 3
The query should return Name=A ID=3. If ID would be 4, the query should not return anything. Is this even possible in SQL? Thanks for any hints!
Edit:
the joined table would look like this:
Name Number ID
A 1 3
A 2 3
A 4 3
So if I do this query I get no entries in the result set:
SELECT * FROM TABLE_ONE INNER JOIN TABLE_TWO ON TABLE_ONE.NAME=TABLE_TWO.NAME WHERE NUMBER=ID
Exactly in this situation I would like to get the Name returned!
Yes, instead of using an INNER join, use a LEFT or a FULL OUTER join. This will allow null values from the other table to appear when you have a value in one of your tables.
The FULL OUTER JOIN keyword returns all rows from the left table (table1) and from the right table (table2).
The LEFT JOIN keyword returns all rows from the left table (table1), with the matching rows in the right table (table2). The result is NULL in the right side when there is no match. (There is also a RIGHT join, but it does the same thing as the left join, just returning all rows from the RIGHT table instead of the left).
SELECT *
FROM Table2
WHERE NOT EXISTS (
SELECT *
FROM Table1
WHERE Table1.Name = Table2.Name AND Table1.Number = Table2.ID
)
As #rhealitycheck has said, a full outer join would work. I found this blog post helpful in explaining joins. P.S. I can't leave comments (Otherwise I would have).

How can a LEFT OUTER JOIN return more records than exist in the left table?

I have a very basic LEFT OUTER JOIN to return all results from the left table and some additional information from a much bigger table. The left table contains 4935 records yet when I LEFT OUTER JOIN it to an additional table the record count is significantly larger.
As far as I'm aware it is absolute gospel that a LEFT OUTER JOIN will return all records from the left table with matched records from the right table and null values for any rows which cannot be matched, as such it's my understanding that it should be impossible to return more rows than exist in the left table, but it's happening all the same!
SQL Query follows:
SELECT SUSP.Susp_Visits.SuspReason, SUSP.Susp_Visits.SiteID
FROM SUSP.Susp_Visits LEFT OUTER JOIN
DATA.Dim_Member ON SUSP.Susp_Visits.MemID = DATA.Dim_Member.MembershipNum
Perhaps I have made a mistake in the syntax or my understanding of LEFT OUTER JOIN is incomplete, hopefully someone can explain how this could be occurring?
The LEFT OUTER JOIN will return all records from the LEFT table joined with the RIGHT table where possible.
If there are matches though, it will still return all rows that match, therefore, one row in LEFT that matches two rows in RIGHT will return as two ROWS, just like an INNER JOIN.
EDIT:
In response to your edit, I've just had a further look at your query and it looks like you are only returning data from the LEFT table. Therefore, if you only want data from the LEFT table, and you only want one row returned for each row in the LEFT table, then you have no need to perform a JOIN at all and can just do a SELECT directly from the LEFT table.
Table1 Table2
_______ _________
1 2
2 2
3 5
4 6
SELECT Table1.Id,
Table2.Id
FROM Table1
LEFT OUTER JOIN Table2 ON Table1.Id=Table2.Id
Results:
1,null
2,2
2,2
3,null
4,null
It isn't impossible. The number of records in the left table is the minimum number of records it will return. If the right table has two records that match to one record in the left table, it will return two records.
In response to your postscript, that depends on what you would like.
You are getting (possible) multiple rows for each row in your left table because there are multiple matches for the join condition. If you want your total results to have the same number of rows as there is in the left part of the query you need to make sure your join conditions cause a 1-to-1 match.
Alternatively, depending on what you actually want you can use aggregate functions (if for example you just want a string from the right part you could generate a column that is a comma delimited string of the right side results for that left row.
If you are only looking at 1 or 2 columns from the outer join you might consider using a scalar subquery since you will be guaranteed 1 result.
Each record from the left table will be returned as many times as there are matching records on the right table -- at least 1, but could easily be more than 1.
Could it be a one to many relationship between the left and right tables?
LEFT OUTER JOIN just like INNER JOIN (normal join) will return as many results for each row in left table as many matches it finds in the right table. Hence you can have a lot of results - up to N x M, where N is number of rows in left table and M is number of rows in right table.
It's the minimum number of results is always guaranteed in LEFT OUTER JOIN to be at least N.
If you need just any one row from the right side
SELECT SuspReason, SiteID FROM(
SELECT SUSP.Susp_Visits.SuspReason, SUSP.Susp_Visits.SiteID, ROW_NUMBER()
OVER(PARTITION BY SUSP.Susp_Visits.SiteID) AS rn
FROM SUSP.Susp_Visits
LEFT OUTER JOIN DATA.Dim_Member ON SUSP.Susp_Visits.MemID = DATA.Dim_Member.MembershipNum
) AS t
WHERE rn=1
or just
SELECT SUSP.Susp_Visits.SuspReason, SUSP.Susp_Visits.SiteID
FROM SUSP.Susp_Visits WHERE EXISTS(
SELECT DATA.Dim_Member WHERE SUSP.Susp_Visits.MemID = DATA.Dim_Member.MembershipNum
)
Pay attention if you have a where clause on the "right side' table of a query containing a left outer join...
In case you have no record on the right side satisfying the where clause, then the corresponding record of the 'left side' table will not appear in the result of your query....
It seems as though there are multiple rows in the DATA.Dim_Member table per SUSP.Susp_Visits row.
if multiple (x) rows in Dim_Member are associated with a single row in Susp_Visits, there will be x rows in the resul set.
Since the left table contains 4935 records, I suspect you want your results to return 4935 records. Try this:
create table table1
(siteID int,
SuspReason int)
create table table2
(siteID int,
SuspReason int)
insert into table1(siteID, SuspReason) values
(1, 678),
(1, 186),
(1, 723)
insert into table2(siteID, SuspReason) values
(1, 678),
(1, 965)
select distinct t1.siteID, t1.SuspReason
from table1 t1 left join table2 t2 on t1.siteID = t2.siteID and t1.SuspReason = t2.SuspReason
union
select distinct t2.siteID, t2.SuspReason
from table1 t1 right join table2 t2 on t1.siteID = t2.siteID and t1.SuspReason = t2.SuspReason
The only way your query would return more number of rows than the left table ( which is SUSP.Susp_Visits in your case), is that the condition (SUSP.Susp_Visits.MemID = DATA.Dim_Member.MembershipNum) is matching multiple rows in the right table, which is DATA.Dim_Member. So, there are multiple rows in the DATA.Dim_Member where identical values are present for DATA.Dim_Member.MembershipNum. You can verify this by executing the below query:
select DATA.Dim_Member.MembershipNum, count(DATA.Dim_Member.MembershipNum) from DATA.Dim_Member group by DATA.Dim_Member.MembershipNum
Simply, LEFT OUTER JOIN is the Cartesian product within each join key, along with the unmatched rows of the left table
(i.e. for each key_x that has N records in table_L and M records in table_R the result will have N*M records if M>0, or N records if M=0)