JOIN functionality between multiple tables - sql

Tables:
Client
CID Name
1 A
2 B
3 C
4 D
5 E
6..(And so on)
Visit
VID CID type
1 1 Med
2 3 Non
3 2 Non
4 6 Med
5 4 Med
6..(And so on)
Payment
PID CID amount
1 1 10
2 1 20
3 2 30
4 2 40
5 3 50
6..(And so on)
Desired Output:
CID Name type amount
1 A Med 10
1 A Med 20
4 D Med NULL
(And so on..)
Query:
SELECT DISTINCT
C.client_id
,C.name
,V.type
,P.payment
FROM Clients C
INNER JOIN Visit V
ON C.cid=V.cid
LEFT JOIN Payment P
ON V.cid=P.cid
I used a LEFT JOIN because I wanna see who made payments and who didn't.
Question: Do LEFT JOINS (or outer joins) pull in data based on the common id's in the CID of the left table resulting from preceding INNER JOINS(The common result set)? Or do they consider ALL the id's in the left table and ignore the INNER JOINS results altogether?
I only want payment details of the clients existing in the inner joins and not outside it.

The LEFT JOIN will only run on rows that the INNER JOIN returns, or the common data set as you mention.
That is regardless of what the LEFT JOIN will return, the INNER JOIN is going to limit the result set based on Visit V ON C.cid=V.cid
This venn diagram shows the results that will be selected, the blue area is your data that will be returned. The order of the joins does not matter.

Related

SQL get table1 names with a count of table2 and table3

I have three tables, table1 is connected to table2 and table3, but table2 and table3 are not connected. I need an output count of table2 and table3 for each table1 row. I have to use joins and a group by table1.name
SELECT Tb_Product.Name, count(TB_Offers.Prod_ID) 'Number of Offers', count(Tb_Requests.Prod_ID) 'Number of Requests'
FROM Tb_Product LEFT OUTER JOIN
Tb_Requests ON Tb_Product.Prod_ID = Tb_Requests.Prod_ID LEFT OUTER JOIN
TB_Offers ON Tb_Product.Prod_ID = TB_Offers.Prod_ID
GROUP BY Tb_Product.Name
I need to combine these queries:
SELECT Tb_Product.[Name], count(TB_Offers.Prod_ID) 'Number of Offers'
FROM Tb_Product LEFT OUTER JOIN
TB_Offers ON Tb_Product.Prod_ID = TB_Offers.Prod_ID
GROUP BY Tb_Product.[Name]
SELECT Tb_Product.[Name], count(Tb_Requests.Prod_ID) 'Number of Requests'
FROM Tb_Product LEFT OUTER JOIN
Tb_Requests ON Tb_Product.Prod_ID = Tb_Requests.Prod_ID
GROUP BY Tb_Product.[Name]
Results:
Name Number of Offers
Airplane 6
Auto 5
Bike 3
Camera 0
Computer 12
Milk 4
Oil 4
Orange 6
Telephone 0
Truck 6
TV 4
Name Number of Requests
Airplane 1
Auto 5
Bike 0
Camera 2
Computer 6
Milk 4
Oil 5
Orange 6
Telephone 0
Truck 1
TV 5
My results for offers and requests are the same value. I am not sure what I am doing wrong with the joins. Do I need to somehow join product to request and separately join product to offers? This needs to be done in one query.
This is for a class. Explanation would also be appreciated.
The simplest way to do this is to count the distinct values of each column:
SELECT
Tb_Product.Name,
count(distinct TB_Offers.Prod_ID) 'Number of Offers',
count(distinct Tb_Requests.Prod_ID) 'Number of Requests'
FROM
Tb_Product
LEFT OUTER JOIN
Tb_Requests ON Tb_Product.Prod_ID = Tb_Requests.Prod_ID
LEFT OUTER JOIN
TB_Offers ON Tb_Product.Prod_ID = TB_Offers.Prod_ID
GROUP BY
Tb_Product.Name
This is necessary because of the way joins work consecutively to produce a rowset that is a combination of all the input relations. COUNT() normally performs a count of non-null values in a column.
You can also do something like this, which aggregates the counts from the child tables independently and then joins them to the base table:
SELECT
p.Name,
o.cnt as Offer_Count,
r.cnt as Request_Count
FROM
TB_Product p
LEFT OUTER JOIN
(SELECT Prod_ID, COUNT(1) cnt FROM TB_Offers GROUP BY Prod_ID) o
LEFT OUTER JOIN
(SELECT Prod_ID, COUNT(1) cnt FROM TB_Requests GROUP BY Prod_ID) r
More explanation...
Let's say you have two products:
Prod_ID
Name
1
Widget
2
Gizmo
And two offers, one for each product:
Offer_ID
Prod_ID
100
1
200
2
And two requests for each product:
Request_ID
Prod_ID
1001
1
1002
1
2001
2
2002
2
Now you join Product relation to Offer relation on Prod_ID, you get a result like this:
Prod_ID
Name
Offer_ID
Prod_ID
1
Widget
100
1
2
Gizmo
200
2
Now when you join that relation to Requests on Prod_ID, you get something like this:
Prod_ID
Name
Offer_ID
Prod_ID
Request_ID
Prod_ID
1
Widget
100
1
1001
1
1
Widget
100
1
1002
1
2
Gizmo
200
2
2001
2
2
Gizmo
200
2
2002
2
Now when you count any of these columns you get 4 because each column has 4 values.

SQL select with three tables and foreign keys

I have three tables :
field:
f_id
f_start
f_end
1
10
20
2
15
25
3
5
10
person :
p_id
p_name
1
Roger
2
John
3
Alicia
affect :
id
fk_field
fk_person
1
2
1
2
1
2
3
3
3
And I would like to select the dates and the names associated to. Like this
p_name
f_start
f_end
Roger
15
25
John
10
20
Alicia
5
10
I'm new to SQL and I don't know if i have to use JOIN or not... Thanks
You must join all 3 tables on their related columns:
SELECT p.p_name, f.f_start, f.f_end
FROM person p
INNER JOIN affect a ON a.fk_person = p.p_id
INNER JOIN field f ON f.f_id = a.fk_field;
Depending on your requirement you may need LEFT instead of INNER joins, but for this sample data the INNER joins will do.

Left join to a table where values do not exist (and are not NULLs)

EDIT (SOLVED): A cross join. One of those joins you never use until you need it. Thanks for the help
Left table: TS , single field with values [1,2,...,365].
Right table: PAYMENT with three fields (ID, TS, AMT)
For each ID, I want to see 365 records from a left join of TS on PAYMENT.
The problem is that "no value" is not the same as a NULL.
If PAYMENT.TS does not exist for a certain value (e.g. PAYMENT.TS=4), then there is no value to join on and the left join does not return a row #4.
I tried using NOT IN / NOT EXISTS as a condition, but this only treats the case where the right table has explicit NULLS and not the case where no value exists.
How can I proceed? Thanks!
(This is a DB2 system)
SELECT * FROM TS LEFT JOIN PAYMENT ON TS = PAYMENT.TS
TS TABLE:
| TS |
----------
1
2
...
365
PAYMENTS TABLE:
| ID | TS | PMT |
-----------------------------
1 1 70
1 2 20
1 5 10
2 3 200
EXPECTED RESULT:
| ID | TS | PMT |
-----------------------------
1 1 70
1 2 20
1 3
1 4
1 5 10
... ...
1 365
2 1
2 2
2 3 200
... ...
2 365
ACTUAL RESULT:
| ID | TS | PMT |
-----------------------------
1 1 70
1 2 20
1 5 10
2 3 200
You need to generate all the rows you want using a cross join and then use left join:
SELECT i.id, ts.ts. p.amt
FROM (SELECT DISTINCT ID FROM PAYMENT) i CROSS JOIN
TS LEFT JOIN
PAYMENT p
ON ts.TS = p.TS AND p.id = i.id;
This will return 365 rows for each id.
You have to join them matching the two common columns in each table. Preferably by the keys(foreign and primary).
Let's say TS table has this one column called 'NUMBERS' and its type is int. The table PAYMENT has the column ID, type of int also. Which means they may have common values. Thus, if you want to join two tables and get the common ones where the PAYMENT.ID exists in TS.NUMBERS then you should do:
SELECT * FROM TS LEFT JOIN PAYMENT ON TS.NUMBERS = PAYMENT.ID
I hope I've been clear.
Note: Also do not forget that if a column or more has the same name in both tables, you have to clarify from which table you want that column for instance if also PAYMENT table had the column named as NUMBERS, then:
SELECT PAYMENT.ID, TS.NUMBERS FROM TS LEFT JOIN PAYMENT ON TS.NUMBERS = PAYMENT.ID

SQL query to add points from two tables within a database

I'm about to have a cow here. I'm designing a hockey pool database, which at the moment consists of 3 tables as follows:
Table 1 Table 2 Table 3
TeamID TeamName GoalieID TeamID Points SkaterID TeamID Points
1 Marlies 1 1 10 1 4 20
2 Colts 2 3 5 2 1 25
3 Sting 3 2 6 3 3 7
4 Steelheads 4 4 7 4 2 12
The actual tables have a lot more players and teams, this is just an example.
I'm looking to create a query that will combine points totals for each player from Table2 and Table3, and display this information right next to the Team Name.
Team Name Total Points
Steelheads 27
Marlies 25
Colts 18
Sting 12
I have no problems summing points from a single table but unfortunately I'm having trouble wrapping my head around combining points that span across multiple tables. Could someone point me in the right direction please?
Do a UNION ALL in a derived table (p) to get all players at once, including goalies. LEFT JOIN with that result to get all teams with players' points. Finally do a GROUP BY and sum the points.
SELECT t1.TeamName, sum(p.Points) as TotalPoints
FROM Table1 t1
LEFT JOIN (select TeamID, Points from Table2
union all
select TeamID, Points from Table3) p
ON t1.TeamId = p.TeamId
GROUP BY t1.TeamName
(Have you considered to have all players in the same table?)
SELECT
Table1.TeamName,
sum(isNull(Table2.Points,0) + isNull(Table3.Points,0)) as TotalPoints
FROM
Table1
LEFT JOIN Table2
ON Table1.TeamId = Table2.TeamId
LEFT JOIN Table3
ON Table1.TeamId = Table3.TeamId
GROUP BY
Table1.TeamName

SQL one to one mapping with select query only

I have two table with name LeftTable and RightTable as below
LeftTable
obsid Name Value
1 Ronak A
2 Aakash B
3 Pankti C
4 Sanket D
RightTable
obsid Name Value
1 Jhone F
2 Isabella M
3 Jonathan L
4 javafx p
Now when I Left join in some condition the output will be as below
ResultTable
obsid Name Value obsid Name Value
-------------------------------------------------------------------------
1 Ronak A 1 Jhone F
1 Ronak A 2 Isabella M
1 Ronak A 3 Jonathan L
1 Ronak A 4 javafx p
2 Aakash B 2 Isabella M
2 Aakash B 3 Jonathan L
2 Aakash B 4 javafx p
3 Pankti C 3 Jonathan L
3 Pankti C 4 javafx p
4 Sanket D 4 javafx p
This Left Join is help me to achieve One-To-Many Relationship in my application.
Left join condition is dynamic in my application. so its just an example I shown here. Suppose Left Join condition in above exampple is LeftTable.Obsid <= RightTable.Obsid
Now I want to achieve One-To-One relationship.
One-To-One relationship means when any of righttable record linked once with any lefttable record then that righttble record can not be linked with any other lefttable record.
For above example Output for one to one is as below
obsid Name Value obsid Name Value
1 Ronak A 1 Jhone F
2 Aakash B 2 Isabella M
3 Pankti C 3 Jonathan L
4 Sanket D 4 javafx p
I have achieve this functionality two way:
Using cursor
fetching one by one record using select query
Database tables are dynamic in my application. so while processing on millions of record takes too much time as fetching one by one record and processing on it takes time.
Is there any way to achieve One-To-One relation in single select query??
I want to avoid fetching one by one record and I am using Mysql database
Please help me.
You can achieve the required output which is shown in the example by just using inner join.
select * from LeftTable L inner join RightTable R
on L.obsid=R.obsid
But I if there are duplicate obsid in the left table you can use the below query
select A.obsid,A.Name,A.Value, R.obsid,R.Name,R.Value from(
select *,row_number() over (partition by obsid order by obsid ) as row_num
from LeftTable)A
inner join RightTable R
on A.obsid=R.obsid
where A.row_num=1
Please let me know this is what you expect