Conditional SQL Join from several tables - sql

I have three tables that are connected. I need to make a single SQL selection and I know I need to use join of some form but not quite how. I only want the objectID to be selected if the session for userID has access to that object´s area (user gets access if their company has access to the area) The tables I have are:
+----------+--------+ Objects
| objectID | areaID |
+----------+--------+
| 1 | 2 |
| 2 | 2 |
| 3 | 3 |
+----------+--------+
+-----------+--------+ Users
| companyID | userID |
+-----------+--------+
| 1 | 1 |
| 1 | 2 |
| 1 | 3 |
+-----------+--------+
+-----------+--------+ Access
| companyID | areaID |
+-----------+--------+
| 1 | 1 |
| 1 | 2 |
| 1 | 3 |
+-----------+--------+

Selected UserID and corresponding areaIDs in inner query using companyID. Then using this, selected distinct ObjectID from Objects table using inner join again with table formed through inner query
Try this query:-
Select distinct a.objectID
from
Objects a
inner join
(
Select a.*,b.areaID
from
Users a
inner join
Access b
on a.companyID=b.companyID
) b
on a.areaID=b.areaID
Where b.UserID= 1;

If I understand you right, it seems like two INNER JOIN functions would get the results you're looking for:
WITH ONE AS (
SELECT a.companyID, a.userID, b.areaID
FROM Users a
INNER JOIN Access b
ON a.companyID = b.companyID
)
SELECT a.companyID, a.userID, a.areaID, b.objectID
FROM ONE a
INNER JOIN Objects b
ON a.areaID = b.areaID;
EDIT: Reply to comment
If you want to limit the results to a specific userID value, you'll need a WHERE clause:
WITH ONE AS (
SELECT a.companyID, a.userID, b.areaID
FROM Users a
INNER JOIN Access b
ON a.companyID = b.companyID
)
SELECT a.companyID, a.userID, a.areaID, b.objectID
FROM ONE a
INNER JOIN Objects b
ON a.areaID = b.areaID
WHERE a.userID = ${specific_user_id};

Related

Ignore SQL INNER JOIN if specific record exist?

i got two table joined like this
SELECT A.id,B.status
FROM TableA A
INNER JOIN TableB B on (A.TableA_ID=B.TableA_ID)
WHERE B.status not in ('CO','CL');
I want to display results of two table joined but with condition if the status of TableB has anything in ('CO','CL') the whole join with id of TableA would be ignored not just the rows with status not in ('CO','CL').
A.id and A.TableA_ID are different columns
Original result without where condition would be like this:
+------+-----------+
| id | status |
+------+-----------+
| 1000 | RE |
| 1000 | RE |
| 1000 | RE |
| 1000 | CO |
| 2000 | RE |
| 2000 | RE |
+------+-----------+
My Result:
+------+-----------+
| id | status |
+------+-----------+
| 1000 | RE |
| 1000 | RE |
| 1000 | RE |
| 2000 | RE |
| 2000 | RE |
+------+-----------+
What i want:
+------+-----------+
| id | status |
+------+-----------+
| 2000 | RE |
| 2000 | RE |
+------+-----------+
Couldn't figure out how to do eliminate the whole join if the record 'CO' exist.
You could use not exists:
SELECT A.id,B.status
FROM TableA A
INNER JOIN TableB B on (A.TableA_ID=B.TableA_ID)
WHERE NOT EXISTS (
SELECT null
FROM TableB B
WHERE B.TableA_ID=A.TableA_ID
AND B.status in ('CO','CL')
);
Or if you only want to hit the tables once you could use an analytic count of the of the statuses you don't want to see, and eliminate any IDs with a non-zero count:
SELECT id, status
FROM (
SELECT A.id,B.status,
COUNT(case when B.status in ('CO','CL') then 1 end)
OVER (partition by A.id) AS cnt
FROM TableA A
INNER JOIN TableB B on (A.TableA_ID=B.TableA_ID)
)
WHERE cnt = 0;
db<>fiddle
This assumes A.id and A.TableA_ID are different columns; if they're the same then you don't need to look at table A directly at all, if you only want those two columns anyway - all of the information you need is in table B anyway.
here is one way :
SELECT A.id,B.status
FROM TableA A
INNER JOIN TableB B on (A.TableA_ID=B.TableA_ID)
WHERE not exists ( select 1 from TableB B where A.TableA_ID=B.TableA_ID and B.status in ('CO','CL')

Is it possible to filter in a left join?

I have three tables, Clients, Bills and BillsStates. I would like to get always the client and if it has bills, only the bills that can be modified. I am trying something like that:
select * from Clients
left join Bills on Bills.IDClient = Clients.IDClient
left join BillsStates on BillsStates.IDBillState = Bills.IDState
and BillsStates.AllowModify = 1
The problem with that is that I get all the bills of the client, no matter if they can be modified or not.
I have tried to with a right join, but in this case I have not get any result.
Is it possible with joins or perhaps I need some subquery? I would prefer a solution with joins, but if there is no way to do it in this way, I would accept another solution.
select * from Clients
left join Bills
inner join BillsStates on BillsStates.IDBillState = Bills.IDState
on Bills.IDClient = Clients.IDClient
and BillsStates.AllowModify = 1
The problem you have is that you only cause the BillsStates record to be excluded, because your filter is only in its join condition. Instead, you can re-order and move it into Bills's join condition.
Your query simply replaces BillsStates.* with null values where BillsStates.AllowModify = 1 condition fails:
| IDClient | Name | IDClient | IDState | Name | IDBillState | AllowModify |
|----------|------|----------|---------|--------|-------------|-------------|
| 1 | John | 1 | 1 | Bill 1 | NULL | NULL |
| 1 | John | 1 | 2 | Bill 2 | 2 | 1 |
| 2 | Jane | NULL | NULL | NULL | NULL | NULL |
Rearrange the join type and condition to get the desired result:
SELECT *
FROM Clients
LEFT JOIN (Bills
INNER JOIN BillsStates ON BillsStates.IDBillState = Bills.IDState) ON Bills.IDClient = Clients.IDClient AND BillsStates.AllowModify = 1;
| IDClient | Name | IDClient | IDState | Name | IDBillState | AllowModify |
|----------|------|----------|---------|--------|-------------|-------------|
| 1 | John | 1 | 2 | Bill 2 | 2 | 1 |
| 2 | Jane | NULL | NULL | NULL | NULL | NULL |
you can try this.
select * from Clients
left join
( select * from Bills
inner join BillsStates on BillsStates.IDBillState = Bills.IDState
and BillsStates.AllowModify = 1
) B ON B.IDClient = Clients.IDClient
use inner join between bills and BillsStates instead left join and do left join with client
select c.* from Clients c
left join Bills b on b.IDClient = c.IDClient
inner join BillsStates bs
on bs.IDBillState = b.IDState and b.AllowModify = 1
Rather than joining on a subquery, you can also re-arrange the order of joins
SELECT c.*, b.*, bs.* -- Todo: only the relevant columns
FROM Bills b
JOIN BillsStates bs ON BillsStates.IDBillState = Bills.IDState
RIGHT JOIN Clients c ON b.IDClient = c.IDClient

Return multiple columns with 3 distinct fields in SQL query for Access DB

I am trying to make a query that returns multiple fields, keeping the first 3 as distinct columns and returns values for the last modified date. Some of the variables in the query fields should come from more than one table and one of them has a True/False criterion too. The three 3 distinct fields are needed because the combination of these is associated with the other returning parameters.
The tables look roughly as follows...
Table a:
ID | Sc | Country | TechID | VarA | ... | VarX(T/F) | LastModified
1 | 1 | AA | 1 | x | ... | T | 1-1-2017
2 | 1 | AA | 1 | z | ... | T | 1-1-2017
3 | 1 | AA | 2 | y | ... | T | 1-1-2018
4 | 1 | AB | 1 | u | ... | T | 1-1-2017
5 | 2 | AB | 2 | v | ... | T | 1-1-2018
6 | 3 | AB | 1 | w | ... | F | 1-1-2018
Table b:
TechID | TechName | Categ | Units
1 | Tech1 | Cat1 | M
2 | Tech2 | Cat2 | N
3 | Tech3 | Cat3 | P
The idea is that the query returns something like this (when the T/F criterion is met). Where the combination of Sc-Country-Tech shows up only once, with the last modified having presedence:
Sc' | Country' |TechName'| Units | Cat | VarA... | LastModified |
1 | AA | 1 | ... | ... | ... | 1-1-2018
1 | AB | 2 | ... | ... | ... | 1-1-2017
2 | AB | 1 | ... | ... | ... | 1-1-2018
So far I've tried a few SQL lines to no avail. First, with Select DISTINCT but the option was too "all inclusive".
SELECT DISTINCT a.Sc, a.Country, b.TechName, b.Units, b.Cat, a.VarA,..,a.VarX, Max(a.LastModified) AS MaxOfLastModified
FROM a INNER JOIN (b INNER JOIN a ON b.TechName =
a.TechID) ON b.Cat = a.TechID
GROUP BY a.Sc, a.Country, b.TechName, b.Units, b.Cat, a.VarA,..,a.VarX
HAVING (((a.VarX)=True));
Also tried this but it prompts errors related to aggregate functions:
SELECT a.Sc, a.Country, b.TechID, b.Units, b.Cat, a.VarA,..,a.VarX, Max(a.LastModified) AS MaxOfLastModified
FROM a INNER JOIN (b INNER JOIN a ON b.TechName =
a.TechID) ON b.Cat = a.TechID
GROUP BY a.Sc, a.Country, a.TechID
HAVING (((a.VarX)=True));
Any thoughts/suggestions on how to go about this?? Any pointers to previous related answers are also much appreciated.
Thanks in advance! :)
EDIT (2017.09.29):
This certainly cleared things up a bit!
I managed to get the query going with some of the fields, only when calling fields from a single table with the following:
SELECT a.Sc, a.Country, a.Tech, a.LastModified, a.VarA
FROM a INNER JOIN (SELECT Sc, Country, Tech, max(LastModified) AS lm FROM a GROUP BY Sc, Country, Tech) AS dt ON (dt.lm=a.LastModified) AND (dt.Tech=a.Tech) AND (dt.Country=a.Country) AND (dt.Sc=a.Sc)
GROUP BY a.Sc, a.Country, a.Tech, a.LastModified, a.VarA, a.VarX
HAVING (((a.VarX)=Yes));
I'm still running into a syntax error on JOIN when trying to add fields from a lookup table using the INNER JOIN command as suggested. The code I tried looked something like:
SELECT a.Sc, a.Country, a.Tech, a.LastModified, a.VarA b.TechCategory
FROM a INNER JOIN (SELECT Sc, Country, Tech, max(LastModified) AS lm FROM a GROUP BY Sc, Country, Tech) AS dt ON (dt.lm=a.LastModified) AND (dt.Tech=a.Tech) AND (dt.Country=a.Country) AND (dt.Sc=a.Sc)
INNER JOIN b ON Tech.Category=a.Tech
GROUP BY a.Sc, a.Country, a.Tech, b.TechCategory, a.LastModified, a.VarA, a.VarX
HAVING (((a.VarX)=Yes));
Any additional pointers are much appreciated!
Use an aggregate query to get the maximum date for each combination of Sc, Country, and TechID, then use this as a subquery and join it back to tables a and b to get the data in your final query. Something like this:
select
a.Sc, a.Country, b.TechName,
b.Units, b.Category, b.Units, a.VarA, a.LastModified
from
(Table_a as a
inner join (
select Sc, Country, TechID, max(LastModified) as lm
from Table_a
group by Sc, Country, TechID
) as dt on dt.Sc=a.Sc and dt.Country=a.Country and dt.TechID=a.TechID and dt.lm=a.LastModified)
inner join Table_b as b on b.TechID=a.TechID

Select query INNER JOIN issue

I have tow tables Requisitions and RequisitionDetails
Requisitions table
+---------------+-----------------+
| RequisitionID | RequisitionDate |
+---------------+-----------------+
| 1 | 2016-08-17 |
| 2 | 2016-08-18 |
| 3 | 2016-08-19 |
+---------------+-----------------+
RequisitionDetails table
+---------------------+---------------+--------+----------+------------------+
| RequisitionDetailID | RequisitionID | ItemID | Quantity | ReceivedQuantity |
+---------------------+---------------+--------+----------+------------------+
| 1 | 1 | 1 | 2 | 1 |
| 2 | 1 | 2 | 3 | 2 |
| 3 | 2 | 3 | 4 | 3 |
+---------------------+---------------+--------+----------+------------------+
I am trying to get Requisition data where Quantity is not equal to ReceivedQuantity.
i have tried the below query but its record with RequisitionID 1 twice.
How can i make the query returns the Requisition data without repeating the requisition data based on items that have Quantity is not equal to ReceivedQuantity.
SELECT
dbo.Requisitions.RequisitionID,
dbo.Requisitions.RequisitionDate
FROM dbo.Requisitions
INNER JOIN dbo.RequisitionDetails
ON dbo.Requisitions.RequisitionID = dbo.RequisitionDetails.RequisitionID
where dbo.RequisitionDetails.Quantity != dbo.RequisitionDetails.ReceivedQuantity
It's returning twice because of the two rows with RequistionID = 1 in the RequistionDetails table. Since the rows returned are exact duplicates you can simply add the DISTINCT keyword to your select to see one of them:
SELECT DISTINCT
dbo.Requisitions.RequisitionID,
dbo.Requisitions.RequisitionDate
FROM dbo.Requisitions
INNER JOIN dbo.RequisitionDetails
ON dbo.Requisitions.RequisitionID = dbo.RequisitionDetails.RequisitionID
where dbo.RequisitionDetails.Quantity!=
dbo.RequisitionDetails.ReceivedQuantity
You should also use some aliases to clean up your query:
SELECT DISTINCT
R.RequisitionID,
R.RequisitionDate
FROM dbo.Requisitions R
INNER JOIN dbo.RequisitionDetails RD ON R.RequisitionID = RD.RequisitionID
WHERE RD.Quantity != RD.ReceivedQuantity
You also can use exists for your case
select
* from requistions rq where exists(
select 1 from RequisitionDetails rd where rd.RequisitionID=rq.RequisitionID
and rd.Quantity!=rd.ReceivedQuantity)
As you don't need columns from the 2nd table you can also switch to EXISTS to avoid DISTINCT:
SELECT req.*
FROM dbo.Requisitions as req
WHERE EXISTS
( SELECT * FROM dbo.RequisitionDetails as req_det
WHERE req.RequisitionID = req_det.RequisitionID
AND Quantity <> ReceivedQuantity
)
Or IN:
SELECT req.*
FROM dbo.Requisitions
WHERE RequisitionID IN
( SELECT RequisitionID
FROM dbo.RequisitionDetails
WHERE Quantity <> ReceivedQuantity
)

SQL - Inner join on two columns from table 1 with one column from table 2?

I have a table that has two columns that each contain a member id that is a foreign key to a members table that contains their name. I want to select the first table with names instead of member IDs. I'm not sure of a way to do this. I feel like there's certainly a way involving an INNER JOIN, but I can't think of how to pick two names from one table in one INNER JOIN. Any ideas?
Thanks in advance!
Match table
|------|-------|
| user | match |
|------|-------|
| 1 | 4 |
| 2 | 1 |
| 3 | 2 |
|------|-------|
Members table
|------|-------|
| user | name |
|------|-------|
| 1 | Joe |
| 2 | Kyle |
| 3 | John |
| 4 | Nate |
|------|-------|
Desired output
|------|-------|
| user | match |
|------|-------|
| Joe | Nate |
| Kyle | Joe |
| John | Kyle |
|------|-------|
You should join members table for twice.
SELECT M1.NAME , M2.NAME
FROM MEMBERS M1
INNER JOIN MATCH M
ON M1.USER = M.USER
INNER JOIN MEMBERS M2
ON M2.USER = M.MATCH
Be carefull with lower and upper identifiers if your database is mysql.
You can also do this with an Outer Apply: http://sqlfiddle.com/#!3/8f932/5
SELECT me.name as 'User',
mem.name as 'Match'
FROM Match m
OUTER APPLY(
SELECT me.name
FROM Members me
WHERE me.id = m.match
)mem
LEFT JOIN Members me on me.id = m.id
Or: http://sqlfiddle.com/#!3/8f932/6
SELECT me.name as 'User',
mem.name as 'Match'
FROM Match m
LEFT JOIN Members mem on mem.id = m.match
LEFT JOIN Members me on me.id = m.id
You can do two joins:
SELECT u1.name, u2.name
FROM members u1
INNER JOIN match m
ON u1.user = m.user
INNER JOIN members u2
ON u2.user = m.match