Query Select with multiple tables and needing Left Join - sql

I am trying to create a Select command to combine 3 tables.
GROUPS, I want to see every record of this table where the table meets the WHERE for the table
CONTACTS, I want to see contacts that meet certain conditions, if there are no contacts I still want to see the GROUP records in the query
GROUPCONTACTS, this table sits between GROUPS and CONTACTS to allow for a many-to-many relationship.
I have tried the following but it shows me every GROUPCONTACTS record instead of just those where there is a related CONTACTS that matches the query. I do not know if SQL allows for what I want.
Azure server running MSSQL server.
SELECT G.GroupID, GC.ContactID, C.ContactID, C.Status, C.Type
FROM Groups G
LEFT JOIN GroupContacts GC
ON GC.GroupID = G.GroupID
JOIN Contacts C
ON C.ContactID = GC.ContactID
AND C.Type = 'Manager'
AND C.Status = 'Active'
WHERE G.Status = 'Active' AND G.Type = 'Physician'
I was hoping to see 1951 results showing 1 record per GROUPS whether or not there was a matching CONTACT. Instead I got 1550 results, excluding all GROUPS that didn't have a matching CONTACT.
I hope I am explaining this well enough How can I have the table Contacts JOIN with GroupContacts and then in turn have the results LEFT JOIN with GROUPS?
Sample source tables
GROUP
GroupID
Type
Name
Status
1
Physician
Drs. Bennett & Stein
Active
2
Physician
Drs. Kogan & Larson
Inactive
6
Physician
Diagnostic Imaging
Active
GROUPCONTACTS
GroupContactID
GroupID
ContactID
13258
2227
124
13259
2305
138
13260
526
251
13261
2900
351
13262
1363
371
13263
2408
460
13264
417
511
CONTACTS
ContactID
Type
First Name
Last Name
Status
375
Physician
Mervyn L.
Elgart
Inactive
376
Physician
Stephen S.
Elgin
Inactive
377
Physician
Oscar
Ellison III
Active
378
Physician
Michael
Emmer
Active
RESULTS (Ideal)
GroupID
ContactID
ContactID
Status
Type
3177
36187
36187
Active
Manager
3178
36188
36188
Active
Manager
3179
36189
36189
Active
Manager
3180
NULL
NULL
NULL
NULL
If a GROUPS doesn't have a matching GROUPCONTACT record just show the last 4 fields as NULL. The reason for the 2 ContactIDs is just for testing purposes.

Next time, please post the CREATE TABLE and INSERT INTO setup statements. I created all those statements only to realize that the data in GroupContacts table doesn't match any of the sample data in Groups or Contacts. Fix the data. Include an example of data that is excluded but you think should be included. Then I'll update my answer.
CREATE TABLE Groups (
GroupID int not null
, Type nvarchar(50) not null
, Name nvarchar(50) not null
, Status nvarchar(50) not null
);
INSERT INTO Groups (GroupID, Type, Name, Status)
VALUES
(1, 'Physician', 'Drs. Bennett & Stein', 'Active')
, (2, 'Physician', 'Drs. Kogan & Larson','Inactive')
, (6, 'Physician', 'Diagnostic Imaging', 'Active')
;
CREATE TABLE GroupContacts (
GroupContactID int not null
, GroupID int not null
, ContactID int not null
);
INSERT INTO GroupContacts (GroupContactID, GroupID, ContactID)
VALUES
(13258,2227,124)
, (13259,2305,138)
, (13260,526,251)
, (13261,2900,351)
, (13262,1363,371)
, (13263,2408,460)
, (13264,417,511)
;
CREATE TABLE Contacts (
ContactID int not null
, Type nvarchar(50) not null
, FirstName nvarchar(50) not null
, LastName nvarchar(50) not null
, Status nvarchar(50) not null
);
INSERT INTO Contacts (ContactID, Type, FirstName, LastName, Status)
VALUES
(375,'Physician','Mervyn L.','Elgart','Inactive')
, (376,'Physician','Stephen S.','Elgin','Inactive')
, (377,'Physician','Oscar','Ellison III','Active')
, (378,'Physician','Michael','Emmer','Active')
;
SELECT G.GroupID, GC.ContactID, C.ContactID, C.Status, C.Type
FROM Groups G
LEFT JOIN GroupContacts GC
ON GC.GroupID = G.GroupID
LEFT JOIN Contacts C
ON C.ContactID = GC.ContactID
AND C.Type = 'Manager'
AND C.Status = 'Active'
WHERE G.Status = 'Active' AND G.Type = 'Physician'
GroupID
ContactID
ContactID
Status
Type
fiddle

Related

Join a table with another table with columns containing null

I want to join users table with both Groupid and superadmingroupid from group table.
Superadmingroupid may be null
I tried below query but not working
SELECT U.Name, G.Name
FROM Groups G
INNER JOIN USERS U ON G.Groupid = U.Gid
LEFT JOIN USERs U2 On G.superadmingroupid= U.Gid
where U.Name='Mishrsa'
Group table
Groupid Gname SuperAdminGroupId
----- ------ --------
17 A 3
2 B null
3 C null
Users
------
id Name Gid
-- ------- ----
1 mishra 2
2 mishrsa 3
I want to diplay the user with groups that are referenced as groupid or superadmingroupid
Ex: User does not have groupid 17 but superadmingroupid 3 is there in users table so group 17 should come in the output
Output
Name GName
Mishra B
Mishra C
Mishra A
Solution for your problem is:
SELECT U.Name, G.GName
FROM Groups G
INNER JOIN USERS U
ON G.Groupid = U.Gid
OR G.superadmingroupid= U.Gid;
Working example: dbfiddle Link
I believe you should use UNION for that. (Maybe this is not the most elegant way).
The first part will give you the match between Groupid to Gid.
The second part will give you the match between SuperAdminGroupId to Gid.
The order is different then what you mentioned, and I do not know if it is important for you, but please try the below example:
SELECT U.Name, G.Name
FROM Groups G
JOIN Users U ON G.Groupid = U.Gid
UNION
SELECT U.Name, G.Name
FROM Groups G
JOIN Users U ON G.SuperAdminGroupId = U.Gid
Posting this answer just because I'd already written it before Bogner Boy posted their answer.
I changed the table names a touch because GROUP is a reserved word in SQL Server.
Bonger Boy's UNION might be more efficient for larger tables, but for smaller tables you'll be fine to use an OR or an IN:
CREATE TABLE AdminGroup
(
GroupId INTEGER,
Gname CHAR(1),
SuperAdminGroupId INTEGER
);
CREATE TABLE Users
(
Id INTEGER,
Name NVARCHAR(64),
Gid INTEGER
);
INSERT INTO Users (Id, Name, Gid) VALUES (1, 'mishra', 2);
INSERT INTO Users (Id, Name, Gid) VALUES (2, 'mishra',3);
INSERT INTO AdminGroup (GroupId, Gname, SuperAdminGroupId) VALUES (17, 'A', 3);
INSERT INTO AdminGroup (GroupId, Gname, SuperAdminGroupId) VALUES (2, 'B', null);
INSERT INTO AdminGroup (GroupId, Gname, SuperAdminGroupId) VALUES (3, 'C', null);
SELECT U.Name, G.GName
FROM Users U
INNER JOIN AdminGroup G ON U.Gid = G.SuperAdminGroupId OR U.Gid = G.GroupId;
--INNER JOIN AdminGroup G ON U.Gid IN (G.SuperAdminGroupId, G.GroupId);
Here's a DBFiddle:
https://www.db-fiddle.com/f/p1RA4z67SH1DijFMZyKRuA/0

Common records from SQL table

I have 3 tables called CompanyInfo , IDInfo and PersonalInfo.
The objective is to fetch P.[First Name], P.[Last Name], I.PAN, C.DOB.
I can put left join or right join to cater this but the sequence of tables may change as it is an input to some tool and user may enter the table names in any sequence like user1 mentions "CompanyInfo, IDInfo, PersonalInfo", user2 mentions "IDInfo, PersonalInfo, CompanyInfo" and so on.
Table data is as below, Is there a way I can fetch the data through single SQL query (may be union). Say for if I want to fetch the data for ID = 4, I should get:
FirstName LastName PAN DOB
User Four UAN44444 NULL
If ID = 3
FirstName LastName PAN DOB
User THree NULL 1987-12-08
CompanyInfo Table
ID Company OfficialEmail EmpID DOB Department Joining Date Status
1 RBS userone#rbs.co.uk UK222222 1980-11-15 HR 2012-11-20 Inactive
3 Infosys userthree#infy.com IN333333 1987-12-08 Admin 2016-08-18 Inactive
5 IBM userfive#us.ibm.com US55555 1986-03-26 Finance 2014-06-26 Active
10 Samsung userten#samsung.com SK101010 1988-04-04 Admin 2013-04-07 Active
IDInfo Table
ID UAN TIN PAN
2 UAN22222 TIN222222 PAN22222
4 UAN44444 TIN444444 PAN44444
5 UAN55555 TIN555555 PAN55555
PersonalInfo Table
ID FirstName LastName PhoneNumber City Address ZipCode Email
1 User One +44-7432564125 London United Kingdom RG231FDT userone#gmail.com
2 User Two +91-987654321 New Delhi India 110006 usertwo#gmail.com
3 User Three +44-782136425 Guildford United Kingdom GUI74DS userthree#gmail.com
4 User Four +1-230156428 Atlanta United States GA 30337 userfour#gmail.com
5 User Five +1-650324152 Houston United States TX 77077 userfive#gmail.com
6 User Six +91-8885552223 Mumbai India 400012 usersix#gmail.com
7 User Seven +91-9998887771 Bangalore India 560021 userseven#gmail.com
Simple LEFT JOIN will resolve your problem.
SELECT P.FirstName, P.LastName, I.PAN, C.DOB
FROM PersonalInfo P
LEFT JOIN IDInfo I ON I.ID = P.ID
LEFT JOIN CompanyInfo C ON C.ID = P.ID
WHERE P.ID = #YourInput
You can use left join and right join:
SELECT P.FirstName, P.LastName, I.PAN, C.DOB. FROM IDInfo i LEFT JOIN PersonalInfo p ON p.ID = i.ID LEFT JOIN CompanyInfo c ON c.ID = i.ID WHERE i.ID = 3
Creating table variables to hold data of 3 table
DECLARE #CompanyInfo TABLE
(ID INT,Company NVARCHAR(50),OfficialEmail NVARCHAR(50),EmpID NVARCHAR(50),DOB NVARCHAR(50),Department NVARCHAR(50),[Joining Date] NVARCHAR(50),[Status] NVARCHAR(50))
DECLARE #IDInfo TABLE
(ID INT,UAN NVARCHAR(50),TIN NVARCHAR(50),PAN NVARCHAR(50))
DECLARE #PersonalInfo TABLE
(ID INT,FirstName NVARCHAR(50),LastName NVARCHAR(50),PhoneNumber NVARCHAR(50),City NVARCHAR(50),[Address] NVARCHAR(50),ZipCode NVARCHAR(50),Email NVARCHAR(50))
Populate data to the tables
INSERT INTO #CompanyInfo VALUES
(1,'RBS','userone#rbs.co.uk','UK222222','1980-11-15','HR','2012-11-20','Inactive'),
(3,'Infosys','userthree#infy.com','IN333333','1987-12-08','Admin','2016-08-18','Inactive'),
(5,'IBM','userfive#us.ibm.com','US55555','1986-03-26','Finance','2014-06-26','Active'),
(10,'Samsung','userten#samsung.com','SK101010','1988-04-04','Admin','2013-04-07','Active')
INSERT INTO #IDInfo VALUES
(2,'UAN22222','TIN222222','PAN22222'),
(4,'UAN44444','TIN444444','PAN44444'),
(5,'UAN55555','TIN555555','PAN55555')
INSERT INTO #PersonalInfo VALUES
(1,'User','One','+44-7432564125','London','United Kingdom','RG231FDT','userone#gmail.com'),
(2,'User','Two','+91-987654321','New Delhi','India','110006','usertwo#gmail.com'),
(3,'User','Three','+44-782136425','Guildford','United Kingdom','GUI74DS','userthree#gmail.com'),
(4,'User','Four','+1-230156428','Atlanta','United States','GA 30337','userfour#gmail.com'),
(5,'User','Five','+1-650324152','Houston','United States','TX 77077','userfive#gmail.com'),
(6,'User','Six','+91-8885552223','Mumbai','India','400012','usersix#gmail.com'),
(7,'User','Seven','+91-9998887771','Bangalore','India','560021','userseven#gmail.com')
Query to obtain the required output
SELECT
P.FirstName,
P.LastName,
I.PAN,
C.DOB
FROM
#PersonalInfo P LEFT JOIN #IDInfo I ON I.ID = P.ID
LEFT JOIN #CompanyInfo C ON C.ID = P.ID
WHERE
P.ID = 4
OUTPUT :-
FirstName LastName PAN DOB
User Four PAN44444 NULL

Get room members, room's owner and admin at the same time in one query with grouped by id (unique) on PostgreSQL 12

I want to get room's member list, room's owner member in case of he doesn't exists in other table and admin member at the same time. Currently i fetch them individually.
CREATE TABLE public.room_members (
id bigint NOT NULL,
member_id bigint,
room_id bigint,
group_id bigint
);
CREATE TABLE public.rooms (
id bigint NOT NULL,
member_id bigint,
group_id bigint,
name varchar(128)
);
CREATE TABLE public.members (
id bigint NOT NULL,
group_id bigint,
username varchar(128),
is_admin bool default false
);
CREATE TABLE public.groups (
id bigint NOT NULL,
name varchar(128)
);
-- My Group created
INSERT INTO "groups" (id, name) VALUES (1, 'My Group');
-- Create users for this group. We have 4 users/members
INSERT INTO "members" (id, group_id, username, is_admin) VALUES (1, 1, 'Pratha', true);
INSERT INTO "members" (id, group_id, username) VALUES (2, 1, 'John');
INSERT INTO "members" (id, group_id, username) VALUES (3, 1, 'Mike');
INSERT INTO "members" (id, group_id, username) VALUES (4, 1, 'April');
-- April creates a room and he is owner of this room
INSERT INTO "rooms" (id, group_id, member_id, name) VALUES (1, 1, 4, 'My Room'); -- 4 is April
-- April also adds Mike to the room members. But she does not add herself. As she is owner.
INSERT INTO "room_members" (id, group_id, room_id, member_id) VALUES (1, 1, 1, 3); -- 3 is Mike
What I want is:
room_members list of 'My Room' (Which is only Mike at the moment)
My Room's owner in case of he didn't add himself to room_members table. Because he is the owner of that room (Which is April)
Plus, admin member (Which is Pratha)
And this should be unique. For example, if user add himself to room_members and also owner then it should fetch member one time only.
What I tried so far?
select * from members
left outer join room_members cm on cm.member_id = members.id
left outer join rooms c on c.id = cm.room_id
where c.name = 'My Room' or members.id = 1
I couldn't use group by here either. Also i don't need the all fields. Just room_members table fields only.
See here: https://rextester.com/XWDS42043
Expected output for room_members:
+-------------+------------+------------+
| member_id | group_id | username |
+-------------+------------+------------+
| 1 | 1 | Pratha |
+-------------+------------+------------+
| 3 | 1 | Mike |
+-------------+------------+------------+
| 4 | 1 | April |
+-------------+------------+------------+
Pratha: Because he is ADMIN
Mike: Because he is member of My Room. MEMBER
April: Because she created that room. OWNER
room_members can be many. I just added only Mike but it can have multiple members including admins and owners.
You can address this with UNION:
-- list the admin(s) of the room group
select m.id, m.group_id, m.username
from rooms r
inner join members m on m.group_id = r.group_id and m.is_admin = true
where r.name = 'My Room'
union
-- list the members of the room
select m.id, m.group_id, m.username
from rooms r
inner join room_members rm on r.id = rm.room_id
inner join members m on rm.member_id = m.id
where r.name = 'My Room'
union
-- recover the room owner
select m.id, m.group_id, m.username
from rooms r
inner join members m on r.member_id = m.id
where r.name = 'My Room'
UNION eliminates duplicates accross queries, so if a user is both member and/or group admin and/or owner of the room, they will only appear once.
In your fiddle, this query returns:
id group_id username
1 4 1 April
2 3 1 Mike
3 1 1 Pratha

return rows only if a certain conditions match up with another table

I want an SQL query that returns a list of users, but only if they have a certain link and not another.
Table 1 links to table 2 users.agreementid = agreements.id, it can link multiple times as a user can be linked to different agreements
All the users in table 1 that only have a link to an agreement where the product is AggregationAgreement, if the user has a link to 2+ agreement ids and any of the links are to a ServiceAgreement then this should not be returned
So I have 2 tables:
TABLE 1- USERS
USER AGREEMENTID
--------------------
USER1 1
USER2 3
USER1 3
USER3 3
USER3 4
USER4 1
TABLE 2- AGREEMENTS
ID PRODUCT
-------------------------
1 ServiceAgreement
2 ServiceAgreement
3 AggregationAgreement
4 AggregationAgreement
5 ServiceAgreement
So for my results on the above example i only expect USER2 and USER3 to be returned.
USER 1 has two links but one of those is ServiceAgreement so it shouldn't be returned in the results.
USER 2 has link to just 1 aggregation agreement so this should be returned in the results.
USER 3 has two links but both are to AggregationAgreement so this should be returned in the results.
USER 4 has one link but it's to a ServiceAgreement so this should no be returned in the results.
Hope that all makes sense, as always appreciate any help.
This would return the users with the agreements, excluding ones that have a ServiceAgreement linked.
SELECT USERS.[UserName]
, AGREEMENTS.[AgreementId]
, AGREEMENTS.[Product]
FROM USERS
INNER JOIN AGREEMENTS
ON AGREEMENTS.[AgreementId] = USERS.[AgreementId]
WHERE USERS.[UserName] NOT IN
(
SELECT USERS.[UserName]
FROM USERS
INNER JOIN AGREEMENTS
ON AGREEMENTS.[AgreementId] = USERS.[AgreementId]
WHERE AGREEMENTS.[Product] = 'ServiceAgreement'
)
Try this:
select *
from users u
where exists
(
select 1
from agreements a
where u.agreementid=a.id and a.product='AggregationAgreement'
)
and not exists
(
select 1
from agreements a2
where u.agreementid=a2.id and a2.product<>'AggregationAgreement'
)
You can also use a query like below
select user
from
(
select
user=u.[user],
weight= case a.product ='ServiceAgreement' then -1 else 0 end -- negative weight for undesirable agreement
from
users u join agreements a
on u.AgreementId=a.AgreementId
and a.product in ('ServiceAgreement','AggregationAgreement') -- we only have two agreements of interest here
)t
group by [user]
having sum(weight)=0 -- should not be negative
Using EXCEPT:
declare #Users table ([User] nvarchar(10), [AGREEMENTID] int)
insert into #Users values
('USER1', 1)
, ('USER2', 3)
, ('USER1', 3)
, ('USER3', 3)
, ('USER3', 4)
, ('USER4', 1)
declare #Agreements table ([ID] int, [Product] nvarchar(100))
insert into #Agreements values
(1, 'ServiceAgreement')
, (2, 'ServiceAgreement')
, (3, 'AggregationAgreement')
, (4, 'AggregationAgreement')
, (5, 'ServiceAgreement')
select u.[User] from #Users u inner join #Agreements a on u.AGREEMENTID = a.ID and a.Product = 'AggregationAgreement'
except
select u.[User] from #Users u inner join #Agreements a on u.AGREEMENTID = a.ID and a.Product = 'ServiceAgreement'

SQL Cross Join with null values

I have a table AddressTypes with 4 records (home,office,vacation,hotel) and a table Address that share the filed addresstypeid.
In address table I have 1 record of type "home" and I want a query where I get 4 rows like this:
Type Address1 Address2 City State
home piping 1232 Austin Tx
office null null null null
vacation null null null null
hotel null null null null
Here is an image of tables: http://tinypic.com/view.php?pic=28078xv&s=6
I'm sure is something very easy maybe using cross join but don't get it. Hope someone can guide me. Appreciate in advance.
Left joining AdddressTypes to Addresses will produce desired result:
select at.Type,
a.Address1,
a.Address2,
a.City,
a.State
from AddressTypes at
left join Address a
on at.AddressTypeID = a.AddressTypeID
-- For this query to make sense
-- Filter one person only
and a.PersonID = #PersonID
----------- THIS PART WAS ADDED BY VAAA ------------------------
Nikola, I change to this:
select at.description,
a.Address1,
a.Address2,
a.City
from address_types at
left join Address a
on 1 = 1
-- For this query to make sense
-- Filter one person only
and a.addressid = 24
And then I get the 4 rows, but all of them have the same address info and just the "home" type address is the one with data. So its close...
Use the LEFT OUTER JOIN, which returns one copy of matching records from both the left and right tables and non-matching records from both the tables. Here is the test code I tried:
CREATE TABLE AddressTypes(
AddressType SMALLINT NOT NULL,
[Description] NVARCHAR(50))
ALTER TABLE AddressTypes
ADD CONSTRAINT PK_AddressTypes_AddressType
PRIMARY KEY (AddressType)
CREATE TABLE [Address] (
AddressID INT NOT NULL,
AddressType SMALLINT,
Address1 NVARCHAR(50),
Address2 NVARCHAR(50),
City NVARCHAR(50),
State CHAR(2))
ALTER TABLE [Address]
ADD CONSTRAINT PK_Address_AddressID
PRIMARY KEY (AddressID)
ALTER TABLE [Address]
ADD CONSTRAINT FK_address_addresstypes_addresstype
FOREIGN KEY (AddressType) REFERENCES AddressTypes(AddressType)
INSERT INTO AddressTypes VALUES (1, 'home'), (2, 'office'),
(3, 'vacation'), (4, 'hotel')
INSERT INTO address VALUES (1, 1, 'piping', '1232', 'Austin', 'TX')
-- Here is the query that outputs the result set.
SELECT AT.AddressType AS [Type], A.Address1, A.Address2, A.City, A.State
FROM AddressTypes AT
LEFT OUTER JOIN address A
ON AT.AddressType = A.AddressType