Issue with SQL join and group - sql

I have 4 tables I am trying to join and then group data. The data consists of jobs, invoices and accounts. I want to generate a total of each account in each job.
I have the following tables:
Jobs
| ID | JobNumber |
|----|-----------|
| 1 | J200 |
| 2 | J201 |
Job_Invoices
| ID | InvoiceNumber | JobID |
|----|---------------|-------|
| 10 | I300 | 1 |
| 11 | I301 | 2 |
Invoice_Accounts
| ID | InvoiceId | AccountID | Amount |
|----|-----------|-----------|--------|
| 23 | 10 | 40 | 200 |
| 24 | 10 | 40 | 300 |
| 25 | 10 | 41 | 100 |
| 26 | 11 | 40 | 100 |
Accounts
| ID | Name |
|----|------|
| 40 | Sales|
| 41 | EXP |
I am trying the following:
SELECT
J.JobNumber,
A.Name AS "Account",
SUM(JA.Amount) AS 'Total'
FROM
Job J
LEFT JOIN
Job_Invoices JI ON JI.JobID = J.JobID
INNER JOIN
Invoice_Accounts JA ON JA.InvoiceId = JI.ID
INNER JOIN
Accounts A ON A.ID = JA.AccountID
GROUP BY
J.JobNumber, A.Name, JA.Amount
ORDER BY
J.JobNumber
What I expect:
| JobNumber | Account | Total |
|-----------|-----------|-------|
| J200 | EXP | 100 |
| J200 | Sales | 500 |
| J201 | Sales | 100 |
What I get:
| JobNumber | Account | Total |
|-----------|-----------|-------|
| J200 | EXP | 100 |
| J200 | Sales | 200 |
| J200 | Sales | 300 |
| J201 | Sales | 100 |

You don't need the Job table in the query. The INNER JOINs are to the Job_Invoices table, so the outer join is turned into an inner join anyway.
So, you can simplify this to:
SELECT JI.JobNumber, A.Name AS Account, SUM(JA.Amount) AS Total
FROM Job_Invoices JI JOIN
Invoice_Accounts JA
ON JA.InvoiceId = JI.ID JOIN
Accounts A
ON A.ID = JA.AccountID
GROUP BY JI.JobNumber, A.Name
ORDER BY JI.JobNumber;
Also note that you don't need to escape the column aliases. The just makes the query harder to type.

The problem is you have the JA.Amount in your GROUP BY clause. Try taking it out:
SELECT J.JobNumber, A.Name AS "Account", SUM(JA.Amount) AS 'Total'
FROM Job J
LEFT JOIN Job_Invoices JI ON JI.JobID = J.JobID
INNER JOIN Invoice_Accounts JA ON JA.InvoiceId = JI.ID
INNER JOIN Accounts A ON A.ID = JA.AccountID
GROUP BY J.JobNumber, A.Name
ORDER BY J.JobNumber

You can write a query as:
select sum (IA.Amount) as Amount, J.JobNumber,A.Name
from #Invoice_Accounts IA --as it holds the base data
join #Job_Invoices JI on IA.InvoiceId = JI.ID
join #Jobs J on J.id = JI.JobID
join #Accounts A on A.ID = IA.AccountID
group by J.JobNumber,A.Name
Included the Jobs table as it has the JobNumber column. Sample code here..

Related

Can someone help me figure out if I'm making a mistake in my query?

I'm trying to create a query that returns the names of all people in my database that have less than half of the money of the person with the most money.
These is my query:
select P1.name
from Persons P1 left join
AccountOf A1 on A1.person_id = P1.id left join
BankAccounts B1 on B1.id = A1.account_id
group by name
having SUM(B1.balance) < MAX((select SUM(B1.balance) as b
from AccountOf A1 left join
BankAccounts B1 on B1.id = A1.account_id
group by A1.person_id
order by b desc
LIMIT 1)) * 0.5
This is the result:
+-------+
| name |
+-------+
| Evert |
+-------+
I have the following tables in the database:
+---------+--------+--+
| Persons | | |
+---------+--------+--+
| id | name | |
| 11 | Evert | |
| 12 | Xavi | |
| 13 | Ludwig | |
| 14 | Ziggy | |
+---------+--------+--+
+--------------+---------+
| BankAccounts | |
+--------------+---------+
| id | balance |
| 11 | 525000 |
| 12 | 750000 |
| 13 | 1900000 |
| 14 | 1600000 |
+--------------+---------+
+-----------+-----------+------------+
| AccountOf | | |
+-----------+-----------+------------+
| id | person_id | account_id |
| 301 | 11 | 12 |
| 302 | 13 | 12 |
| 303 | 13 | 14 |
| 304 | 14 | 11 |
| 305 | 14 | 13 |
+-----------+-----------+------------+
What am I missing here? I should get two entries in the result (Evert, Xavi)
I wouldn't approach the logic this way (I would use window functions). But your final having has two levels of aggregation. That shouldn't work. You want:
having SUM(B1.balance) < (select 0.5 * SUM(B1.balance) as b
from AccountOf A1 join
BankAccounts B1 on B1.id = A1.account_id
group by A1.person_id
order by b desc
limit 1
)
I also moved the 0.5 into the subquery and changed the left join to a join -- the tables need to match to get balances.
I would recommend window functions, if your - undisclosed! - database supports them.
You can join and aggregate just once, and then use a window max() to get the top balance. All that is then left to is to filter in an outer query:
select *
fom (
select p.id, p.name, coalesce(sum(balance), 0) balance,
max(sum(balance)) over() max_balance
from persons p
left join accountof ao on ao.person_id = p.id
left join bankaccounts ba on ba.id = ao.account_id
group by p.id, p.name
) t
where balance > max_balance * 0.5

SQL duplicate values of records with multiple joins

My query works fine until I add in the estimate tables, where my data duplicates.
Below is my table structure:
Jobs
| ID | JobNumber |
|----|-----------|
| 1 | J200 |
| 2 | J201 |
Job_Invoices
| ID | InvoiceNumber | JobID |
|----|---------------|-------|
| 10 | I300 | 1 |
| 11 | I301 | 2 |
Invoice_Accounts
| ID | InvoiceId | AccountID | Amount |
|----|-----------|-----------|--------|
| 23 | 10 | 40 | 200 |
| 24 | 10 | 40 | 300 |
| 25 | 10 | 41 | 100 |
| 26 | 11 | 40 | 100 |
Estimates
| ID | JobID |
|----|-------|
| 50 | 1 |
Estimate_Accounts
| ID | EstimateID| AccountID | Amount |
|----|-----------|-----------|--------|
| 23 | 50 | 40 | 100 |
| 24 | 50 | 40 | 100 |
Accounts
| ID | Name |
|----|------|
| 40 | Sales|
| 41 | EXP |
I am trying the below:
SELECT
J.JobNumber,
A.Name AS "Account",
SUM(JA.Amount) AS 'Total Invoiced',
SUM(EA.Amount) AS 'Total Estimated',
FROM
Job J
LEFT JOIN
Job_Invoices JI ON JI.JobID = J.ID
LEFT JOIN
Estimates E ON E.JobID = J.ID
LEFT JOIN
Estimate_Accounts EA ON EA.EstimateID = E.ID
INNER JOIN
Invoice_Accounts JA ON JA.InvoiceId = JI.ID
INNER JOIN
Accounts A ON A.ID = JA.AccountID
GROUP BY
J.JobNumber, A.Name, JA.Amount
ORDER BY
J.JobNumber
This is what I am hoping to achieve:
| JobNumber | Account | Total Invoiced | Total Estimated |
|-----------|-----------|----------------|-----------------|
| J200 | EXP | 100 | 0 |
| J200 | Sales | 500 | 200 |
| J201 | Sales | 100 | 0 |
This works fine if before I add the Estimates and Estimate_Accounts tables, my result looks like the above (without the Total Estimate) column.
However, when I try adding the Total Estimated column by joining the Estimates and Estimate_Accounts tables, Total Invoiced and Total Estimated values double, to something like this:
| JobNumber | Account | Total Invoiced | Total Estimated |
|-----------|-----------|----------------|-----------------|
| J200 | EXP | 200 | 0 |
| J200 | Sales | 1000 | 400 |
| J201 | Sales | 200 | 0 |
You want to join invoice totals with esitimated totals. Both are aggregations. So, make these aggregations, then join. With the jobs and accounts thus found, join the job and account tables.
select
j.jobnumber,
a.name as "Account",
inv.total as "Total Invoiced",
est.total as "Total Estimated",
from
(
select e.jobid, ea.accountid, sum(ea.amount) as total
from estimate_accounts ea
join estimates e on e.id = ea.estimateid
group by e.jobid, ea.accountid
) est
full outer join
(
select ji.jobid, ia.accountid, sum(ia.amount) as total
from invoice_accounts ia
join job_invoices ji on ji.id = ia.invoiceid
group by ji.jobid, ia.accountid
) inv using (jobid, accountid)
join jobs j on j.id = jobid
join accounts a on a.id = accountid
order by j.jobnumber, a.name;
If your DBMS doesn't support the USING clause, you must use ON instead:
select
[...]
) inv on inv.jobid = est.jobid and inv.accountid = est.accountid
join jobs j on j.id in (est.jobid, inv.jobid)
join accounts a on a.id in (est.accountid, inv.accountid)
order by j.jobnumber, a.name;
You need to aggregate before joining, because otherwise the JOIN generates a Cartesian product. However, this is complicated by the account information.
So, this approach aggregates the estimates and invoices separately by account and job. It then combines them using UNION ALL and joins in the rest of the information:
SELECT J.JobNumber, A.Name AS Account,
JE.Total_Invoiced, JE.Total_Estimated
FROM Job J LEFT JOIN
((SELECT JI.JobId, JA.AccountId, SUM(JA.Amount) AS Total_Invoiced, NULL as Total_Estimated
FROM Job_Invoices JI JOIN
Invoice_Accounts JA
ON JA.InvoiceId = JI.ID
GROUP BY JI.JobId, JA.AccountId
) UNION ALL
(SELECT E.JobId, EA.AccountId, NULL, SUM(EA.Amount) as Total_Estimated
FROM Estimates E JOIN
Estimate_Accounts EA
ON EA.EstimateID = E.ID
GROUP BY E.JobId, EA.AccountId
)
) JE
ON JE.JobId = J.ID LEFT JOIN
Accounts A
ON A.ID = JE.AccountID
ORDER BY J.JobNumber;
There are two tables where duplication may happen:
Invoice_Accounts has several records per AccountID/InvoiceId tuple, that you want to SUM()
Estimate_Accounts has several records per EstimateID/AccountID tuple. Also I think that you should use column AccountID when joining this table: this requires changing the order of the JOINs, so Estimate_Accounts is joined after Accounts
I think that it would be simpler to move the aggregation to subqueries, and then join them in the outer query.
Consider:
SELECT
J.JobNumber,
A.Name AS Account,
JA.Amount AS Total_Invoiced,
COALESCE(EA.Amount, 0) AS Total_Estimated
FROM
Job J
LEFT JOIN
Job_Invoices JI ON JI.JobID = J.ID
INNER JOIN
(
SELECT AccountID, InvoiceId, SUM(Amount) Amount
FROM Invoice_Accounts
GROUP BY InvoiceId, AccountID
) JA ON JA.InvoiceId = JI.ID
INNER JOIN
Accounts A ON A.ID = JA.AccountID
LEFT JOIN
Estimates E ON E.JobID = J.ID
LEFT JOIN
(
SELECT EstimateID, AccountID , SUM(Amount) Amount
FROM Estimate_Accounts
GROUP BY EstimateID, AccountID
) EA ON EA.EstimateID = E.ID AND EA.AccountID = JA.AccountID
ORDER BY
J.JobNumber, A.Name;
This demo on DB Fiddle with your sample data returns:
| JobNumber | Account | Total_Invoiced | Total_Estimated |
| --------- | ------- | -------------- | --------------- |
| J200 | EXP | 100 | 0 |
| J200 | Sales | 500 | 200 |
| J201 | Sales | 100 | 0 |

Oracle SQL - Select duplicates based on two columns

I need to select duplicate rows based on two columns in a join, and i can't seem to figure out how that is done.
Currently i got this:
SELECT s.name,administrative_site_id as adm_id,s.external_code,si.identifier_value
FROM suppliers s
INNER JOIN suppliers_identifier si
ON s.id = si.supplier_id
And the output is something along the lines of below:
| Name | adm_id | external_code |identifier_value |
|:-----------|------------:|:------------: |:----------------:|
| Warlob | 66323 | ext531 | id444 |
| Ozzy | 53123 | ext632 | id333 |
| Motorhead | 521 | ext733 | id222 |
| Perez | 123 | ext833 | id111 |
| Starlight | 521 | ext934 | id222 |
| Aligned | 123 | ext235 | id111 |
What i am looking for, is how to simply select these 4 rows, as they are duplicates based on column: adm_id and Identifier_value
| Name | adm_id | external_code |identifier_value |
|:-----------|------------:|:------------: |:----------------:|
| Motorhead | 521 | ext733 | id222 |
| Perez | 123 | ext833 | id111 |
| Starlight | 521 | ext934 | id222 |
| Aligned | 123 | ext235 | id111 |
First group by ADM_ID, IDENTIFIER_VALUE and find groups that has more than one row in it.
Then select all rows that has these couples
SELECT S.NAME
,ADMINISTRATIVE_SITE_ID AS ADM_ID
,S.EXTERNAL_CODE
,SI.IDENTIFIER_VALUE
FROM SUPPLIERS S INNER JOIN SUPPLIERS_IDENTIFIER SI ON S.ID = SI.SUPPLIER_ID
WHERE (ADMINISTRATIVE_SITE_ID, SI.IDENTIFIER_VALUE) IN (SELECT ADMINISTRATIVE_SITE_ID AS ADM_ID, SI.IDENTIFIER_VALUE
FROM SUPPLIERS S INNER JOIN SUPPLIERS_IDENTIFIER SI ON S.ID = SI.SUPPLIER_ID
GROUP BY ADM_ID, IDENTIFIER_VALUE
HAVING COUNT(*) > 1)
Or an alternate way that may perform better on big datasets:
with t as (
SELECT s.name,administrative_site_id as adm_id,s.external_code,si.identifier_value
COUNT(*) OVER (PARTITION BY administrative_site_id ,identifier_value ) AS cnt
FROM suppliers s
INNER JOIN suppliers_identifier si
ON s.id = si.supplier_id)
select name, adm_id, external_code, identifier_value
from t
where cnt > 1

SQL - Join with multiple condition

I'm trying to join my users table with my jobs table based on a mapping table users_jobs:
Here is what the users table looks like:
users
|--------|------------------|
| id | name |
|--------|----------------- |
| 1 | Ozzy Osbourne |
| 2 | Lemmy Kilmister |
| 3 | Ronnie James Dio |
| 4 | Jimmy Page |
|---------------------------|
jobs table looks like this:
|--------|-----------------|
| id | title |
|--------|-----------------|
| 1 | Singer |
| 2 | Guitar Player |
|--------------------------|
And users_jobs table looks like this:
|--------|-------------|-------------|---------------|-------------|
| id | user_id | job_id | column3 | column4 |
|--------|-------------|-------------|---------------|-------------|
| 1 | 1 | 1 | 0 | 1 |
| 2 | 2 | 1 | 1 | 0 |
| 3 | 3 | 1 | 0 | 1 |
| 4 | 4 | 2 | 1 | 0 |
|----------------------|-------------|---------------|-------------|
For example, let's say the ozzy does a query.
Here is what should expect:
|--------|------------------|------------|--------- |
| id | name | column3 | column4 |
|--------|----------------- |------------|----------|
| 1 | Ozzy Osbourne | 0 | 1 |
| 2 | Lemmy Kilmister | 1 | 0 |
| 3 | Ronnie James Dio | 0 | 1 |
|---------------------------|------------|----------|
Basically, he can only see the job in which he is registered (role) and the users included.
I tried to do this:
SELECT u1.*, uj1.colum3, uj1.column4
FROM users AS u1
JOIN users_jobs AS uj1 ON uj1.user_id = 1
JOIN jobs AS j1 ON j1.id = up1.job_id
WHERE uj1.job_id = 1
Any help would be great!
Looks like you need INNER JOIN Try this :
select u.id, u.column3 , u.column4 from users u
inner join user_jobs uj on u.id=uj.user_id
inner join jobs j on j.id=uj.job_id
where uj.job_id=1;
If you need by certain user_id
select u.id, u.column3 , u.column4 from users u
inner join user_jobs uj on u.id=uj.user_id
inner join jobs j on j.id=uj.job_id
where uj.job_id=1
and u.id=1;
I found a solution.
Using #stackFan approach adding an EXISTS clause to make sure that the user is in.
SELECT u.id, u.column3 , u.column4
FROM users u
INNER JOIN user_jobs uj on u.id = uj.user_id
INNER JOIN jobs j on j.id = uj.job_id
WHERE uj.job_id = <job-ID>
AND
EXISTS (
SELECT *
FROM users_jobs AS uj
WHERE uj.job_id = <job-ID>
AND uj.user_id = <user-ID>
);
Try LEFT JOIN. It will display all users, whether they have job or not.
SELECT u.id, u.name, uj.colum3, uj.column4
FROM users AS u
LEFT JOIN users_jobs uj ON uj.user_id = u.id
LEFT JOIN jobs j ON j.id = uj.job_id

Joining 6 tables into single query?

Hey can anyone help me join the 5 tables below into a single query? I currently have the query below but is doesn't seem to work as if there are two products with the same ID inside the hires table all of the products are returned form the products table which is obviously wrong.
SELECT products.prod_id, products.title, products.price, product_types.name,
listagg(suppliers.name, ',') WITHIN GROUP(ORDER BY suppliers.name) suppliers
FROM products
INNER JOIN product_suppliers ON products.prod_id = product_suppluer.prod_id
INNER JOIN product_types ON product_types.type_id = products.type_id
INNER JOIN suppliers ON product_suppliers.supp_id = suppliers.supp_id
LEFT OUTER JOIN hires ON hires.prod_id = products.prod_id
WHERE (hires.hire_end < to_date('21-JAN-13') OR hires.hire_start > to_date('26-JAN-13'))
OR hires.prod_id IS NULL
GROUP BY products.prod_id, products.title, products.price, product_types.name
Table data:
PRODUCTS
--------------------------------------------
| Prod_ID | Title | Price | Type_ID |
|------------------------------------------|
| 1 | A | 5 | 1 |
| 2 | B | 7 | 1 |
| 3 | C | 3 | 2 |
| 4 | D | 3 | 3 |
|------------------------------------------|
PRODUCT_TYPES
----------------------
| Type_ID | Type |
|--------------------|
| 1 | TYPE_A |
| 2 | TYPE_B |
| 3 | TYPE_C |
| 4 | TYPE_D |
|--------------------|
PRODUCT_SUPPLIERS
-------------------------
| Prod_ID | Supp_ID |
|-----------------------|
| 1 | 1 |
| 1 | 2 |
| 2 | 2 |
| 3 | 3 |
| 4 | 4 |
|-----------------------|
SUPPLIERS
----------------------
| Supp_ID | Name |
|--------------------|
| 1 | SUPP_A |
| 2 | SUPP_B |
| 3 | SUPP_C |
| 4 | SUPP_D |
|--------------------|
HIRES
---------------------------------------------------------------
| Hire_ID | Prod_ID | Cust_ID | Hire_Start | Hire_End |
|-----------------------|------------|------------------------|
| 1 | 1 | 1 | 22-Jan-13 | 23-Jan-13 |
| 2 | 2 | 2 | 27-Jan-13 | 29-Jan-13 |
| 3 | 1 | 3 | 30-Jan-13 | 31-Jan-13 |
|-----------------------|------------|------------|-----------|
PRODUCTS
--------------------------------
| Cust_ID | Name | Phone |
|------------------------------|
| 1 | Cust_A | 555-666 |
| 2 | Cust_B | 444-234 |
| 3 | Cust_C | 319-234 |
| 4 | Cust_D | 398-092 |
|------------------------------|
The output from the query at the moment looks like this:
-------------------------------------------------------------
| Prod_ID | Title | Price | Type_ID | Suppliers |
|------------------------------------------|----------------|
| 1 | A | 5 | Type_A | SUPP_A,SUPP_B |
| 2 | B | 7 | Type_B | SUPP_B |
| 3 | C | 3 | Type_C | SUPP_C |
| 4 | D | 3 | Type_D | SUPP_D |
|------------------------------------------|----------------|
When it should look like this surely? as Prod_ID '1' is hired out between the dates in the query
-------------------------------------------------------------
| Prod_ID | Title | Price | Type_ID | Suppliers |
|------------------------------------------|----------------|
| 2 | B | 7 | Type_B | SUPP_B |
| 3 | C | 3 | Type_C | SUPP_C |
| 4 | D | 3 | Type_D | SUPP_D |
|------------------------------------------|----------------|
If anyone can help modify the query to output as suggested i would be really grateful. Because my understanding is that it should work as written?
Your issue is that Prod_Id 1 is both in and out of those date ranges. So instead, use a subquery to filter out which Prod_Id are in those ranges, and exclude those.
This is a much simplified version of your query:
SELECT P.Prod_ID
FROM Products P
LEFT JOIN (
SELECT Prod_ID
FROM Hires
WHERE hire_end >= To_Date('20130121', 'yyyymmdd') AND hire_start <= To_Date('20130126', 'yyyymmdd')
) H ON P.Prod_ID = H.Prod_ID
WHERE h.prod_id IS NULL
And the SQL Fiddle.
Assuming I copied and pasted correctly, this should be your query:
SELECT products.prod_id, products.title, products.price, product_types.name,
listagg(suppliers.name, ',') WITHIN GROUP(ORDER BY suppliers.name) suppliers
FROM products
INNER JOIN product_suppliers ON products.prod_id = product_suppluer.prod_id
INNER JOIN product_types ON product_types.type_id = products.type_id
INNER JOIN suppliers ON product_suppliers.supp_id = suppliers.supp_id
LEFT JOIN (
SELECT Prod_ID
FROM Hires
WHERE hire_end >= To_Date('20130121', 'yyyymmdd') AND hire_start <= To_Date('20130126', 'yyyymmdd')
) H ON products.Prod_ID = H.Prod_ID
WHERE H.Prod_ID IS NULL
GROUP BY products.prod_id, products.title, products.price, product_types.name
Hope this helps.
Your left outer join will return null values when there is no match, meaning you still have a row (with no HIRE table data) when the results of this join query are Null:
LEFT OUTER JOIN hires ON hires.prod_id = products.prod_id
WHERE (hires.hire_end < to_date('21-JAN-13')
OR hires.hire_start > to_date('26-JAN-13'))
OR hires.prod_id IS NULL
Try adding a select from the hires table (eg. hire.Hire_Start) to see this happening, then switch it to an inner join as well and I think your problem will be solved.
OR add a WHERE clause on the full query with something like hire.Hire_Start is not null
EDIT
If you change your original query to:
SELECT hires.Hire_Start, products.prod_id, products.title, products.price, product_types.name,
listagg(suppliers.name, ',') WITHIN GROUP(ORDER BY suppliers.name) suppliers
FROM products
INNER JOIN product_suppliers ON products.prod_id = product_suppluer.prod_id
INNER JOIN product_types ON product_types.type_id = products.type_id
INNER JOIN suppliers ON product_suppliers.supp_id = suppliers.supp_id
LEFT OUTER JOIN hires ON hires.prod_id = products.prod_id
WHERE (hires.hire_end < to_date('21-JAN-13') OR hires.hire_start > to_date('26- JAN-13'))
OR hires.prod_id IS NULL
GROUP BY products.prod_id, products.title, products.price, product_types.name
What comes back in the Hire_Start column?
Then if you add it to the where clause do you get the expected result:
SELECT hires.Hire_Start, products.prod_id, products.title, products.price, product_types.name,
listagg(suppliers.name, ',') WITHIN GROUP(ORDER BY suppliers.name) suppliers
FROM products
INNER JOIN product_suppliers ON products.prod_id = product_suppluer.prod_id
INNER JOIN product_types ON product_types.type_id = products.type_id
INNER JOIN suppliers ON product_suppliers.supp_id = suppliers.supp_id
LEFT OUTER JOIN hires ON hires.prod_id = products.prod_id
WHERE (hires.hire_end < to_date('21-JAN-13') OR hires.hire_start > to_date('26- JAN-13'))
OR hires.prod_id IS NULL
WHERE hires.Hire_Start is not null
GROUP BY products.prod_id, products.title, products.price, product_types.name
Finally, dropping the Outer Join altogether, does this work as expected?
SELECT hires.Hire_Start, products.prod_id, products.title, products.price, product_types.name,
listagg(suppliers.name, ',') WITHIN GROUP(ORDER BY suppliers.name) suppliers
FROM products
INNER JOIN product_suppliers ON products.prod_id = product_suppluer.prod_id
INNER JOIN product_types ON product_types.type_id = products.type_id
INNER JOIN suppliers ON product_suppliers.supp_id = suppliers.supp_id
INNER JOIN hires ON hires.prod_id = products.prod_id
WHERE (hires.hire_end < to_date('21-JAN-13') OR hires.hire_start > to_date('26- JAN-13'))
GROUP BY products.prod_id, products.title, products.price, product_types.name
And note: is the OR Hires.prod_ID sopposed to indicate that if the result returns no hire information it is available, in which case you need to write the query more like the other answer provided.
Here is some code that may help you:
SELECT L.V_PRODUCT_ID "PROD_ID" , L.TITLE "TITLE" , L.PRICE "PRICE" , L.TYPE "TYPE" , S.NAME "SUPPLIERS"
FROM
(SELECT V_PRODUCT_ID , TITLE , PRICE , TYPE , SUPPLIER_ID FROM
((select p.prod_id v_product_id , p.title TITLE , p.price PRICE , t.type TYPE
from products p , products_types t
where p.type_id = t_type_id) A
JOIN
(SELECT PROD_ID VV_PRODUCT_ID , SUPP_ID SUPPLIER_ID
FROM PRODUCTS_SUPPLIERS) H
ON (A.V_PRODUCT_ID = H.VV_PRODUCT_ID))) L
JOIN
SUPLLIERS S
ON (L.SUPPLIER_ID = S.SUPP_ID);
SELECT Emp.Empid, Emp.EmpFirstName, Emp.EmpLastName, Dept.DepartmentName
FROM Employee Emp
INNER JOIN Department dept
ON Emp.Departmentid=Dept.Departmenttid