Join a table based on condition - sql

I have a situation where I need to join a table only on a particular condition.
Lets take the underwritten scenario
create table [premiumuser] (user_id int, name nvarchar(50));
create table [liteuser] (user_id int, name nvarchar(50));
create table [feature] (id nvarchar(50), user_id int, userkey int);
insert into [premiumuser] select 1, 'stephen';
insert into [premiumuser] select 2, 'roger';
insert into [liteuser] select 1, 'apollo';
insert into [liteuser] select 2, 'venus';
insert into feature select 'Upload content', 1, 1;
insert into feature select 'Create account', 1, 0;
insert into feature select 'View content', 2, 0;
Now I want to join only 1 table at a time from premiumUser or Lite user.
i.e.
select F.*, U.Name from feature
Inner Join
if condition 1
then
LiteUser U on U.User_Id = F.User_ID
else
PremiumUser U on U.User_Id = F.User_ID
end
Is there anyway to achieve this???
I know something like this can be done
select
f.id,
case when userkey=0 then l.name else p.name end as username
from [feature] f
left join [liteuser] l on l.user_id = f.user_id
left join [premium user] p on p.user_id = f.user_id
but since I have huge tables I don't want to join both the tables.
I want to be selective in joining the tables

No, you can't selective join in standard TSQL (definitely true for MS SQL and I'm pretty sure it's true for Oracle and MySQL).
You can however put the condition in the join. This will allow SQL to very quickly ignore the join because it will evaluate the conditions in order:
select
f.id,
case when userkey=0 then l.name else p.name end as username
from [feature] f
left join [liteuser] l
on condition1 = 1
AND l.user_id = f.user_id
left join [premium user] p
on condition1 = 2
and p.user_id = f.user_id

Related

select all from table1 with zero entries in table2 in sqlite?

Consider 2 tables, user and contact:
CREATE TABLE user (
id INT,
name VARCHAR(36)
);
CREATE TABLE contact (
id INT,
phone INT,
userID INT
);
INSERT INTO user (id,name) VALUES
(1,'Frank'),
(2,'Henry'),
(3,'John')
INSERT INTO contact (id,phone,userID) VALUES
(1,911,1),
(2,922,2),
(3,933,2)
I am interested in all user entries that do not have any contact.
The outer join of these tables returns 4 results:
SELECT contact.*, user.*
FROM contact
LEFT JOIN user
ON contact.userID = user.id
UNION ALL
SELECT contact.*, user.*
FROM user
LEFT JOIN contact
ON contact.userID = user.id
WHERE contact.userID IS NULL
How do I select all user where contact.userID is null (1 result, in this example)?
This is your second subquery:
SELECT c.*, u.*
FROM user u LEFT JOIN
contact c
ON c.userID = u.id
WHERE c.userID IS NULL;
It does exactly what you want, although I would remove the c.* from the SELECT.

How to replace LEFT outer join with INNER join in SQL?

I have a view on which I need to provide cluster Indexing the problem is in order to provide cluster indexing the it should not have any of the left or right outer joins , and I want to replace the LEFT outer join with INNER join , one of the ways which I can think of is to insert a dummy value with lets say -1 in the right table and by doing this even if all the Ids from the left table wont match Ids from the right table in INNER JOIN but since we have inserted -1 in the right table and we are using IsNULL(u.UserId,-1) it should return all the values from the left table but somehow this approach is not working.
create table Users(
UserId int,
UserName nvarchar(255)
)
insert into Users values(1,'sid429')
insert into Users values(2,'ru654')
insert into Users values(3,'dick231')
create table managers
(
caseId int,
CaseName nvarchar(255),
UserId int
)
insert into managers values (100,'Case1',1)
insert into managers values (101,'Case2',2)
insert into managers values (-1,NULL,-1)
select username from users u inner join managers m on m.UserId=IsNULL(u.UserId,-1)
Don't talk about indexes, but I think you could replace LEFT JOIN by INNER JOIN + UNION
select username from users u inner join managers m on m.UserId= u.UserId
UNION ALL
select username from users u WHERE NOT EXISTS (SELECT 1 FROM managers m WHERE m.UserId = u.UserId)
IsNull(u.UserId,-1) doesn't seem right - u.UserId is never null, since the absence of data is in the managers table - in this case, u.UserId will always have a value, but m.UserId might not, so IsNull(u.UserId, -1) won't work.
I'm intrigued to see a better answer, but I don't think you can do that - I think you eventually need to substitute the value conditionally to -1 if it doesn't exist in the other table, like this:
select username from users u
inner join managers m on m.UserId =
case when not exists(select * from managers where UserId = u.UserId)
then -1 else u.UserId end
This has the desired effect, but looking at the execution plan, won't help your performance issue.
You can replace a LEFT OUTER JOIN with an INNER JOIN if you add the missing values in the related table.
It has not worked for you because you have added a -1 value. But the not matching value on your INNER JOIN is a 3, not a null or a -1.
You can do so at runtime with an UNION, no need to permanently create those values as you have tried to do (inserting that -1 value) :
with expanded_managers as (
select CaseId, CaseName, UserId
from managers
union
select null, null, UserId
from users
where not exists (select * from managers where managers.UserId = users.UserId)
)
select UserName, CaseName
from users
inner join expanded_managers on expanded_managers.UserId = users.UserId
if you require only username it should be simple:
select distinct username from users u inner join managers m on m.UserId=u.UserId OR ( m.UserId=-1 AND u.userId = u.userId)
I have cleaned-up this part a bit. I had to guess the logical model, given that you did not specify any constraints.
create table Users (
UserId int not null
, UserName nvarchar(255) not null
, constraint pk_users primary key (UserId)
, constraint ak_users unique (UserName)
);
create table Cases (
CaseId int not null
, CaseName nvarchar(255) not null
, UserId int not null
, constraint pk_cases primary key (CaseId)
, constraint ak_cases unique (CaseName)
, constraint fk_cases foreign key (UserId)
references Users (UserId)
);
insert into Users values(1,'sid429') ;
insert into Users values(2,'ru654') ;
insert into Users values(3,'dick231');
insert into Cases values (100,'Case1',1);
insert into Cases values (101,'Case2',2);
This is mostly self-explanatory, but you have to understand that candidate keys (unique) for the result are: {UserID, CaseId}, {UserName, CaseName}, {UserID, CaseName}, {UserName, CaseId}. Not sure if you were expecting that.
with
R_00 as (
select UserId from Users
except
select UserId from Cases
)
select u.UserId
, u.UserName
, c.CaseId
, c.CaseName
from Users as u
join Cases as c on u.UserId = c.UserId
union
select u.UserId
, u.UserName
, (-1) as CaseId
, 'n/a'as CaseName
from Users as u
join R_00 as r on r.UserId = u.UserID
;
Another version of this, similar to other examples in the post.
select u.UserId
, u.UserName
, c.CaseId
, c.CaseName
from Users as u
join Cases as c on u.UserId = c.UserId
union
select u.UserId
, u.UserName
, (-1) as CaseId
, 'n/a' as CaseName
from Users as u
where not exists (select 1 from Cases as c where c.UserId = u.userId)
;

How to let COUNT show zeros in tables with many to many relationship?

the following query does not show the Groups where no users belong to.
I would like to have the shown with a count of 0 too. How do I do this?
Like this should it be
Group A 8
Group B 0
Group C 2
This is it now
Group A 8
Group C 2
SELECT UsersToGroups.GroupID,
groups.Group,
COUNT(UsersToGroups.UserID) AS countUsersPerGroup
FROM users_Groups AS groups
LEFT JOIN AssociationUsersToGroups AS UsersToGroups ON
UsersToGroups.GroupID =
groups.ID
LEFT JOIN users_Users AS users ON
UsersToGroups.UserID =
users.ID
GROUP BY GroupID,
groups.Group
ORDER BY groups.Group ASC
Query will select all groups
SELECT groups.ID,
groups.Group,
FROM users_Groups AS groups
If you add LEFT JOIN AssociationUsersToGroups you should receive groups with number of participants:
SELECT groups.ID,
groups.Group,
COUNT(UsersToGroups.UserID) AS countUsersPerGroup
FROM users_Groups AS groups
LEFT JOIN AssociationUsersToGroups AS UsersToGroups ON
UsersToGroups.GroupID =
groups.ID
GROUP BY groups.ID, groups.Group
First of all i don't see why you need to join the user table?
There's no need to assuming that you have a foreign key relationship between "users" and "users-to-group" association
table with ON DELETE CASCADE
This works for me:
-- setting up test-tables and test-data
create table #Groups
(
ID int,
GroupName varchar(100)
)
create table #UsersToGroup
(
GroupID int,
UserID int
)
insert into #Groups
values (1,'Group A'),(2,'Group B'),(3,'Group C')
insert into #UsersToGroup
values (1,1),(1,2),(1,3),(1,4),(1,5),(1,6),(1,7),(1,8),(3,1),(3,2)
-- the query you want:
select g.ID as GroupID,
g.GroupName,
count(utg.UserID) as countUsersPerGroup
from #Groups g
left join #UsersToGroup utg on g.ID = utg.GroupID
group by g.ID, g.GroupName
order by g.GroupName asc
-- cleanup
drop table #Groups
drop table #UsersToGroup
output:

Query that joins two table where match exists and removes result if ID exists in another table

This query gets the the ID, First Name, Date of Birth by joining two tables together where a mutual match exists
Example of mutual match:
Amy likes Mary
Mary likes Amy
SELECT u.ID, u.firstname, u.dob, i.[Image]
FROM [dbo].[User] AS u
INNER JOIN [dbo].[Images] AS i ON u.ID = i.Id
WHERE u.ID IN (
SELECT userB FROM [dbo].[LikesRefined]
WHERE userA = #ID OR userB = #ID
UNION
SELECT userB FROM [dbo].[LikesRefined]
WHERE userA = #ID OR userB = #ID
);
I want to filter this result by using except clause on another table but I keep getting this error
All queries combined using a UNION, INTERSECT or EXCEPT operator must have an equal number of expressions in their target lists.
except
(select paidA
from [dbo].[Matches]
WHERE paidA = #ID
AND (userA = #ID or userB = #ID))
end
I'm still confused, but I think this may do the trick:
SELECT u.ID ,
u.firstname ,
u.dob ,
i.[Image]
FROM [dbo].[User] AS u
INNER JOIN [dbo].[Images] AS i ON u.ID = i.Id
INNER JOIN dbo.[LikesRefined] lr ON (u.ID = lr.userA OR u.ID = lr.userB)
LEFT OUTER JOIN dbo.Matches m ON u.ID = m.paidA
AND ( m.userA = u.ID OR m.userb = u.ID )
WHERE (lr.userA = #ID OR lr.userB = #ID)
AND m.paidA IS NULL
We get rid of the union operator and just check for the ID in either userA or userB in the LikeRefined table. Then we left join to dbo.Matches and just look for records where there is no match.
When using union operator all select statements must have the same number of columns

What is the better way of writing this query?

I have a simple join query as follows.
select *
from user u left join
user_roles ur
on ur.user_id = u.user_id
and ur.created_by = 'Mike'
where u.division = 'some division'
OR
select *
from user u left join
user_roles ur
on ur.user_id = u.user_id
where u.division = 'some division'
and ur.created_by = 'Mike'
The point is here is I have moved the additional filter clause condition from left join to where clause.
Does it make any difference if I join two tables on more than column or put it in where clause?
Yes it makes a big difference.
You are basicalling nullifying the left join and making it an inner join, hiding user roles not created by Mike
Bell Mike
Toe Mike
Bob Jerry
first query returns
Bell Mike
Toe Mike
Bob NULL
second Query returns
Bell Mike
Toe Mike
Yes - it makes an important difference
Any filter on the joined table should be in the join for it to work correctly - ie: the former will work, the second won't.
Try the following (SQL Server syntax) to see how the results differ
declare #u table (user_id int, division varchar(20))
declare #ur table (user_id int, created_by varchar(10))
insert #u values (1,'sales'), (2,'marketing'), (3,'engineering')
insert #ur values (1, 'mike'), (3,'james'), (3,'mike')
select * from #u u
left join #ur ur on ur.user_id = u.user_id and ur.created_by = 'Mike'
select * from #u u
left join #ur ur on ur.user_id = u.user_id
where ur.created_by = 'Mike'