SQL Joins with Five tables - sql

I have a SQL Database containing five tables.
tblExpense : Expense_ID, Expense_Catagory, Expense_Date, Expense_Particular, Expense_Amount
tblExpenseTransaction : Expense_Transaction_ID, Expense_ID, Transaction_ID
tblStudentMain : Student_Main_ID, Student_Name
tblStudentTransaction : Student_Transaction_ID, Student_Main_ID, Transaction_ID
tblTransaction : Transaction_ID, Transaction_Date, Transaction_Amount
Now tblTransaction contains transactions done by students or expenses and are connected by between tables( i.e. tblStudentTransaction and tblExpenseTransaction). The Relations are like tblStudent -> tblStudentTransactions -> tblTransactions and tblExpense -> tblExpenseTransaction -> tblTransaction.
Now My Task is to retrieve all the transactions made only by Students or Expenses.
I tried to do that using this query :
SELECT tblTransaction.Transaction_ID, tblTransaction.Transaction_Date, tblTransaction.Transaction_Amount, tblTransaction.Transaction_Particular, tblTransaction.Transaction_Mode,
tblTransaction.Is_Deposit
FROM tblTransaction INNER JOIN
tblStudentTransaction ON tblTransaction.Transaction_ID = tblStudentTransaction.Transaction_ID INNER JOIN
tblStudentMain ON tblStudentTransaction.Student_Main_ID = tblStudentMain.Student_Main_ID
which gives me result. But when i tried to do that with other set of tables with this :
SELECT tblTransaction.Transaction_ID, tblTransaction.Transaction_Date, tblTransaction.Transaction_Amount, tblTransaction.Transaction_Particular, tblTransaction.Transaction_Mode,
tblTransaction.Is_Deposit
FROM tblTransaction INNER JOIN
tblExpenseTransaction ON tblTransaction.Transaction_ID = tblExpenseTransaction.Transaction_ID RIGHT OUTER JOIN
tblExpense ON tblExpenseTransaction.Expense_ID = tblExpense.Expense_ID
query returned rows with transactions related to student also.
Now i know that, i need to use RIGHT, LEFT or either JOIN but i am confused. Please give me idea if anyone have.

Assuming you need a single query that indicates if the transaction is associated with an expense, a student or neither:
SELECT t.*, CASE
WHEN e.Transaction_ID IS NOT NULL THEN 'Expense'
WHEN s.Transaction_ID IS NOT NULL THEN 'Student'
ELSE 'Other'
END AS Type
FROM tblTransaction t
LEFT JOIN tblExpenseTransaction e ON e.Transaction_ID = t.Transaction_ID
LEFT JOIN tblStudentTransaction s ON s.Transaction_ID = t.Transaction_ID
WHERE Type = 'Expense'
Or you could specify Type = 'Student`` to get all student-related transactions, orType = 'Other'` for others, or leave off the WHERE clause off to get all transactions and their type.

As per my understanding individually you are getting the records for the student and expense using the below queries thus try adding union to the two and you should get the requisite data.
As expense and student are individual entities thus I dont think there would be any overlapping / common records between the two tables thus inner join should work fine for you.
SELECT tblTransaction.Transaction_ID, tblTransaction.Transaction_Date, tblTransaction.Transaction_Amount,
tblTransaction.Transaction_Particular, tblTransaction.Transaction_Mode,
tblTransaction.Is_Deposit
FROM tblTransaction INNER JOIN
tblStudentTransaction ON tblTransaction.Transaction_ID = tblStudentTransaction.Transaction_ID INNER JOIN
tblStudentMain ON tblStudentTransaction.Student_Main_ID = tblStudentMain.Student_Main_ID
union
SELECT tblTransaction.Transaction_ID, tblTransaction.Transaction_Date, tblTransaction.Transaction_Amount,
tblTransaction.Transaction_Particular, tblTransaction.Transaction_Mode,
tblTransaction.Is_Deposit
FROM tblTransaction INNER JOIN
tblExpenseTransaction ON tblTransaction.Transaction_ID = tblExpenseTransaction.Transaction_ID INNER JOIN
tblExpense ON tblExpenseTransaction.Expense_ID = tblExpense.Expense_ID

Related

LEFT JOIN with condition in WHERE clause

SELECT
supplies.id,
supplierId,
supplies.date,
supplies.commodity,
supplier_payments.date AS paymentDate,
FROM
supplies
INNER JOIN suppliers ON suppliers.id = supplies.supplierId
LEFT JOIN supplier_payments ON supplier_payments.supplyId = supplies.id
WHERE supplier_payments.isDeleted = 0 AND supplierId = 1
What I am trying is to get all records from supplies table and related records from supplier_payments but the supplier_payments.isDeleted should be equal to 0. What happens now that I only get records from supplies that have at least one supplier payment because of the condition. Is there a way to get all supply records and supply payments with condition?
Consider moving the condition on the LEFT JOINed table to the ON clause of the JOIN:
SELECT
sr.id,
se.supplierId,
se.date,
se.commodity,
sp.date AS paymentDate,
FROM supplies se
INNER JOIN suppliers sr ON sr.id = se.supplierId
LEFT JOIN supplier_payments sp
ON sp.supplyId = se.id
AND sp.isDeleted = 0
WHERE se.supplierId = 1
Side notes:
in a multi-table query, always qualify each column with the table it belongs to, to make the query easier to follow and avoid ambiguity
table aliases make the query easier to read and write

how can I get the selected columns fully and the sum column separately

SELECT f_name,l_name,teachers.first_name,teachers.t_id,p_id,paid_amount,family_id,date,sum(payments.paid_amount)
FROM payments
LEFT JOIN family ON family.id = payments.family_id
LEFT JOIN teachers ON family.teacher_id = teachers.t_id
How can I get the selected columns fully and the sum column separately?
because that sum function makes all the selected result one row
SELECT f_name,l_name,teachers.first_name,teachers.t_id,p_id,paid_amount,family_id,date
FROM payments
LEFT JOIN family ON family.id = payments.family_id
LEFT JOIN teachers ON family.teacher_id = teachers.t_id
This query is working fine without the sum column
You didn't tell the database, which column to use for aggregating the data. Don't know which database you are using, but some complain, that there is no GROUP BY statement in the SQL text.
Please try with the following query:
SELECT f_name,l_name,teachers.first_name,teachers.t_id,p_id,paid_amount,family_id,date,sum(payments.paid_amount)
FROM payments
LEFT JOIN family ON family.id = payments.family_id
LEFT JOIN teachers ON family.teacher_id = teachers.t_id
GROUP BY f_name,l_name,teachers.first_name,teachers.t_id,p_id,paid_amount,family_id,date
GROUP BY tells the database, which are the key columns in the aggregation.
If you want all the payments, use a subquery or join:
SELECT f_name, l_name, t.first_name, t.t_id, p.p_id, p.paid_amount, p.family_id, date,
(select sum(p.paid_amount) from payments) as all_paid
FROM payments p LEFT JOIN
family f
ON f.id = p.family_id LEFT JOIN
teachers t
ON f.teacher_id = tetchers.t_id;
SELECT f_name,l_name,t.first_name,t.t_id,p_id,paid_amount,family_id,date,sum(p.paid_amount)
FROM payments p,family f,teachers t where f.id = p.family_id and f.teacher_id = t.t_id
Group by f_name,l_name,teachers.first_name,teachers.t_id,p_id,paid_amount,family_id
You can add date column also in Group by expression based on your requirement. Example:
f_name,l_name,teachers.first_name,teachers.t_id,p_id,paid_amount,family_id,date

SQL query joining four tables

Original query is joining customer table and contract table and Extra Service History, this all works.
However I'm having trouble adding 4th table which should apply some further criteria.
Current working query (no changes needed) :
select b.*
from SubscribersFIN b
inner join (select Id, Account_Number, ContractNumber, BackendId
from Contract) e on b.c_id='FI_' + e.Account_Number
left join (select Contract
from Extra_Service_History
where Service_Name='debit_plan') d on e.Id=d.Contract
where COUNTRY='fi'
and NO_SMS = 0
and d.Contract is null
Goal is to filter the set that came from the big query that only records that had Paid status in Invoice to show.
right join (select Contract
from Invoice
where Status = 'PAID') i on e.Id=i.Contract
This one does not seem to do the trick, so I'm not able to figure out what sort of a join-type or logic is required here.
You have a few options:
INNER JOIN
Depending on the particular type of outer join, they return rows where no match is found (either left, right, or both sides of the join). Based on your description this is not what you want. Simply use:
inner join (select Contract
from Invoice
where Status = 'PAID') i on e.Id=i.Contract
It shouldn't matter where this occurs in the FROM clause; provided the join between these 2 tables is INNER. The query engine is free to rearrange for performance provided it doesn't change semantics. (But personally I find it tidier to put INNER JOINs at the top.)
IN filter
What you've described is a filter.
Goal is to filter the set that came from the big query that only records that had Paid status in Invoice to show.
So it's clearer to implement this as a filter in the WHERE clause. E.g.
where e.Id in (select Contract
from Invoice
where Status = 'PAID')
and ...
EXISTS filter
Similar to the above, but using an EXISTS subquery instead.
where exists (select *
from Invoice i
where Status = 'PAID'
and i.Contract = e.Id)
and ...
Rather than mixing LEFT and RIGHT joins, just place it as an INNER join higher up in your query:
select b.*
from SubscribersFIN b
inner join (select Id, Account_Number, ContractNumber, BackendId
from Contract) e on b.c_id='FI_' + e.Account_Number
inner join (select Contract
from Invoice
where Status = 'PAID') i on e.Id=i.Contract
left join (select Contract
from Extra_Service_History
where Service_Name='debit_plan') d on e.Id=d.Contract
where COUNTRY='fi'
and NO_SMS = 0
and d.Contract is null
Based on my understanding i just re arranged the query. Try this. If your where condition columns are coming from any of the LEFT JOIN tables, join them at the on clause.
select b.* from SubscribersFIN b
inner join Contract e on b.c_id='FI_' + e.Account_Number
left join Extra_Service_History d on e.Id=d.Contract and d.Service_Name='debit_plan' and d.Contract is null
left join invoice i on e.Id=i.Contract and i.Status = 'PAID'
where COUNTRY='fi' and NO_SMS = 0

How to get the right output from this query

Im creating a sql query that outputs the licensePlate from dbo.car when there is any stock of the car. Which means i will have to look under dbo.Sell and dbo.Rent to make sure that a car is not sold or under rental.
I need to have a output with the following:
LicensePlateNo
--------------
SGK5556A
But the output will always be blank.
I have the following statement:
SELECT
LicensePlateNo
FROM car
INNER JOIN CarTransaction ON
car.ChassisNo = CarTransaction.ChassisNo
INNER JOIN Sell on
CarTransaction.TransactionID = Sell.TransactionID
INNER JOIN Rent on
CarTransaction.TransactionID = Rent.TransactionID
WHERE
Car.Make = 'Toyota' AND
Sell.TransactionID IN (SELECT TransactionID FROM Sell) AND
Rent.TransactionID IN (SELECT TransactionID FROM Rent)
I cant seem to get the right query. The only moment when the query works is when i did not specify the query for dbo.Rent. But i need to validate whether if the car is available through the rental history
The inner joins are not needed and the car is never in sell and rent at the same time, query can be written as:
SELECT
LicensePlateNo
FROM
Car
INNER JOIN
CarTransaction
ON
Car.ChassisNo = CarTransaction.ChassisNo
WHERE
Car.Make = 'Toyota' AND
CarTransaction.TransactionID NOT IN (SELECT TransactionID FROM Sell) AND
CarTransaction.TransactionID NOT IN (SELECT TransactionID FROM Rent)
;
This query will only produce results if you have a car that has been sold and has been under rental. That is what INNER JOINs are for - they only give results when a record is found in both tables.
I guess you want a SQL statement like this:
SELECT LicensePlateNo FROM car
LEFT JOIN CarTransaction ON car.ChassisNo = CarTransaction.ChassisNo
LEFT JOIN Sell ON CarTransaction.TransactionID = Sell.TransactionID
LEFT JOIN Rent ON CarTransaction.TransactionID = Rent.TransactionID
WHERE Car.Make = 'Toyota'
AND Sell.TransactionID IS NULL
AND Rent.TransactionID IS NULL

SQL include entries with no data

Here is my sql statement in Oracle that I am working with:
SELECT FACILITY.fac_name
, SUM(FEE_LOG.fee_amount) AS TOTAL_FEES
FROM FACILITY
, BOOK_DETAIL
, TRANS_LOG
, FEE_LOG
, TRANS_TYPE
WHERE
FACILITY.fac_id = BOOK_DETAIL.fac_id
AND BOOK_DETAIL.bkdt_id = TRANS_LOG.bkdt_id
AND TRANS_LOG.trans_id = FEE_LOG.trans_id
AND TRANS_LOG.trans_type_id = TRANS_TYPE.trans_type_id
AND
(
TRANS_TYPE.trans_type_desc = 'LOST'
OR TRANS_TYPE.trans_type_desc = 'CHECKIN'
)
GROUP BY FACILITY.fac_name;
It outputs something similar to this:
FACILITY TOTAL_FEES
Facility 1 8.45
Facility 2 4.23
Facility 3 5.23
I have 2 other facilities but they do not have any fees associated with them. I want to show them as 0
So the output would be like:
FACILITY TOTAL_FEES
Facility 1 8.45
Facility 2 4.23
Facility 3 5.23
Facility 4 0
Facility 5 0
ER Diagram
Use LEFT JOIN instead of implicit INNER JOIN for the FEE_LOG table
SELECT FACILITY.fac_name
, SUM(FEE_LOG.fee_amount) AS TOTAL_FEES
FROM FACILITY
JOIN BOOK_DETAIL ON FACILITY.fac_id = BOOK_DETAIL.fac_id
JOIN TRANS_LOG ON BOOK_DETAIL.bkdt_id = TRANS_LOG.bkdt_id
LEFT JOIN FEE_LOG ON TRANS_LOG.trans_id = FEE_LOG.trans_id
JOIN TRANS_TYPE TRANS_LOG.trans_type_id = TRANS_TYPE.trans_type_id
WHERE TRANS_TYPE.trans_type_desc IN ('LOST', 'CHECKIN')
GROUP BY FACILITY.fac_name;
Use outer joins and the on clause:
SELECT FACILITY.fac_name, SUM(nvl(FEE_LOG.fee_amount,0)) AS TOTAL_FEES
FROM FACILITY
left join BOOK_DETAIL
on FACILITY.fac_id = BOOK_DETAIL.fac_id
left join TRANS_LOG
on BOOK_DETAIL.bkdt_id = TRANS_LOG.bkdt_id
left join FEE_LOG
on TRANS_LOG.trans_id = FEE_LOG.trans_id
left join TRANS_TYPE
on TRANS_LOG.trans_type_id = TRANS_TYPE.trans_type_id
and TRANS_TYPE.trans_type_desc in ('LOST', 'CHECKIN')
GROUP BY FACILITY.fac_name;
Note: If these facilities you are referring to, that do not have any fees, have rows on BOOK_DETAIL or TRANS_LOG you can replace the outer join with an inner join (for just those tables). Any table at which point there may or may not be a related record you have to use an outer join.
Try this:
SELECT f.fac_name
, coalesce(SUM(fl.fee_amount),0) AS TOTAL_FEES
FROM FACILITY AS f
INNER JOIN BOOK_DETAIL AS bd
ON bd.fac_id = f.fac_id
INNER JOIN TRANS_LOG AS tl
ON tl.bkdt_id = bd.bkdt_id
LEFT OUTER JOIN FEE_LOG AS fl
ON fl.trans_id = tl.trans_id
INNER JOIN TRANS_TYPE AS tt
ON tt.trans_type_id = tl.trans_type_id
WHERE tt.trans_type_desc in ('LOST' , 'CHECKIN')
GROUP BY f.fac_name;
NB: when you list tables after from with commas between them you're effectively using a cross join.
When you add a condition to your where statement matching a field from one table to a field from another, the join becomes an inner join.
To select all records from one table along with any matches which may or may not exist from another you need an outer join.
There are 3 types:
left outer join where you want all records from the first table listed and any matching records from the second.
right outer join for all records from the second with only matches from the first.
full outer join is all records from both tables, alongside one another where there's a match.
You should always specify the type of join rather than implying it through where clauses / commas, as this makes your intentions clearer and thus your code more readable.