Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Use inner join if record exists otherwise use left join

I have the following table structure:

dbo.Owner

OwnerID   OwnerName  
  1        John   
  2        Marie
  3        Alex

and dbo.Pet

PetID PetTag Status OwnerID
  1    A341  Active    1  
  2    A342  Inactive  1  
  3    A343  Active    2
  4    A345  Active    2

I need to return all owners who have only Active pets or no pets.

So in this example above I need to Return Owner 2 (All pets are active) and Owner 3 (No pets)

I will be pulling data in C# using Entity Framework but plain SQL will be sufficient.

Here's what I came up with so far:

select mi.* from Owner o
join Pet p
on o.OwnerID= p.OwnerID
where o.Status='Active'
union select * from Owner
where OwnerID not in (select OwnerID from Pet)

Now, this query above works but it includes OwnerID = 1. and Also I was wondering if there's a way to do this in 1 query without union.

like image 850
user194076 Avatar asked Nov 03 '17 03:11

user194076


People also ask

Can I use LEFT join instead of inner join?

INNER JOIN vs LEFT JOIN? Actually, that is not the question at all. You'll use INNER JOIN when you want to return only records having pair on both sides, and you'll use LEFT JOIN when you need all records from the “left” table, no matter if they have pair in the “right” table or not.

Can inner join and left join giving same results?

The reason why LEFT JOIN and INNER JOIN results are the same is because all the records of table branch has at least one match on table user_mast . The main difference between INNER JOIN and LEFT JOIN is that LEFT JOIN still displays the records on the the LEFT side even if they have no match on the RIGHT side table.

Is left join and left inner join same?

There really is no difference between a LEFT JOIN and a LEFT OUTER JOIN. Both versions of the syntax will produce the exact same result in PL/SQL.


3 Answers

If your only values for Status are "Active" and "Inactive", you can actually simplify your query. When you say:

I need to return all owners who have only Active pets or no pets.

This would then actually translate to:

I need to return all owners who have no Inactive pets.

Then your query becomes much easier.

In an Entity Framework query:

owners = context.Owners
    .Where(o => !o.Pets.Any(p => p.Status == "Inactive"))
    .ToList();

The SQL query generated by this is:

SELECT 
    [Extent1].[OwnerID] AS [OwnerID], 
    [Extent1].[OwnerName] AS [OwnerName]
    FROM [dbo].[Owners] AS [Extent1]
    WHERE  NOT EXISTS (SELECT 
        1 AS [C1]
        FROM [dbo].[Pets] AS [Extent2]
        WHERE ([Extent1].[OwnerID] = [Extent2].[OwnerID]) AND (N'Inactive' = [Extent2].[Status])
    )

Or to remove the clutter:

SELECT 
    OwnerID,
    OwnerName
    FROM Owners o
    WHERE  NOT EXISTS (SELECT 
        1
        FROM Pets p
        WHERE (o.OwnerID = p.OwnerID AND p.Status = 'Inactive')
    )

If you have more values for Status, you could use (Entity Framework):

owners = context.Owners
    .Where(o => o.Pets.Any(p => p.Status == "Active") || !o.Pets.Any())
    .Where(o => !o.Pets.Any(p => p.Status == "Inactive" /* || p.Status == "Lost" and any other values */))
    .ToList();

which would generate the SQL query:

SELECT 
    [Extent1].[OwnerID] AS [OwnerID], 
    [Extent1].[OwnerName] AS [OwnerName]
    FROM [dbo].[Owners] AS [Extent1]
    WHERE (( EXISTS (SELECT 
        1 AS [C1]
        FROM [dbo].[Pets] AS [Extent2]
        WHERE ([Extent1].[OwnerID] = [Extent2].[OwnerID]) AND (N'Active' = [Extent2].[Status])
    )) OR ( NOT EXISTS (SELECT 
        1 AS [C1]
        FROM [dbo].[Pets] AS [Extent3]
        WHERE [Extent1].[OwnerID] = [Extent3].[OwnerID]
    ))) AND ( NOT EXISTS (SELECT 
        1 AS [C1]
        FROM [dbo].[Pets] AS [Extent4]
        WHERE ([Extent1].[OwnerID] = [Extent4].[OwnerID]) AND (N'Inactive' = [Extent4].[Status])
    ))

You'd want to test that for performance and there may well be better ways, but it gives the desired result. It does assume you have foreign key/navigation property though.

like image 91
Tone Avatar answered Oct 12 '22 11:10

Tone


Try the following query:

select o.*
from dbo.owner o
where not exists(
  select *
  from dbo.pet p
  where p.ownerid=o.ownerid and
    p.status='Inactive'
);
like image 37
Andrei Odegov Avatar answered Oct 12 '22 11:10

Andrei Odegov


SELECT  OwnerID, OwnerName 
FROM Owner 
WHERE OwnerID NOT IN (
SELECT OwnerID from Pet
WHERE Status='Inactive'

This simple query will do the thing.

OwnerId        OwnerName 
2              Marie
3              Alex

And if you want to select owner with atleast one ACTIVE or NO PET then use the below query.

SELECT o.OwnerID o.OwnerName
FROM Owner o 
LEFT JOIN Pet p 
ON o.OwnerID= p.OwnerID 
AND (p.Status='Active' 
OR p.OwnerID is NULL)

OwnerId        OwnerName
1              John
2              Marie
3              Alex

This query will return OWNER name until that Owner's all pets are INACTIVE

Now for another case..

If there is a chance for your table to have OwnerId as NULL in Pets Table. Kindly use the below Query. (Mysql)

SELECT OwnerID, OwnerName 
FROM Owner 
WHERE OwnerID NOT IN (
   SELECT IFNULL(OwnerID,0) from Pet
   WHERE Status='Inactive');

ADDED IFNULL() in subquery.

SQLFIDDLE

like image 3
Faraz PV Avatar answered Oct 12 '22 11:10

Faraz PV