Replace subqueries in where statement - sql

I've built a query that intends to find products (products table) with both a 'used' offer and a 'new' offer, and get the lowest price for each. A product can have multiple offers (link_prices table). The offer's condition is determined by the name of the merchant (merchants table): a name without used and occasion is a 'new' offer, a name with used is a 'used' offer.
Here's a sample of the tables (PostgreSQL):
merchants
+----+---------------+
| id | name |
+----+---------------+
| 1 | amazon_used |
| 2 | ebay_location |
| 3 | amazon |
| 4 | target |
| 5 | target_used |
+----+---------------+
link_prices
+----+-------------+------------+-------+
| id | merchant_id | product_id | price |
+----+-------------+------------+-------+
| 1 | 1 | 1 | |
| 2 | 1 | 2 | 20 |
| 3 | 4 | 2 | 30 |
| 4 | 5 | 2 | 5 |
| 5 | 2 | 3 | 10 |
| 6 | 1 | 4 | 80 |
| 7 | 1 | 3 | 100 |
+----+-------------+------------+-------+
In this case, I'm expecting my query to return
+------------+----------------+---------------+
| product_id | min_used_price | min_new_price |
+------------+----------------+---------------+
| 2 | 5 | 30 |
+------------+----------------+---------------+
I've got the following query to work but I feel like I shouldn't need to use subqueries to achieve this. I just can't work my head around it. Any help would be appreciated to optimize this query.
SELECT products.id,
MIN(CASE WHEN merchants.name ILIKE '%used%' THEN link_prices.price END) as min_used_price,
MIN(CASE WHEN merchants.name NOT ILIKE '%used%' THEN link_prices.price END) as min_new_price
FROM products
INNER JOIN link_prices ON link_prices.product_id = products.id
INNER JOIN merchants ON merchants.id = link_prices.merchant_id
WHERE
products.id IN (
SELECT products.id
FROM products
INNER JOIN link_prices ON link_prices.product_id = products.id
INNER JOIN merchants ON merchants.id = link_prices.merchant_id
AND merchants.name ILIKE '%used%'
AND link_prices.price IS NOT NULL
AND link_prices.price <> 0
)
AND products.id IN (
SELECT products.id
FROM products
INNER JOIN link_prices ON link_prices.product_id = products.id
INNER JOIN merchants ON merchants.id = link_prices.merchant_id
AND merchants.name NOT ILIKE '%used%'
AND merchants.name NOT ILIKE '%location%'
AND link_prices.price IS NOT NULL
AND link_prices.price <> 0
)
GROUP BY products.id
Thanks a ton!

Your description makes this sound like conditional aggregation:
select lp.product_id,
min(lp.price) filter (where m.name like '%used') as min_used_price,
min(lp.price) filter (where m.name not like '%used') as min_new_price
from merchants m join
link_prices lp
on lp.merchant_id = m.id
group by lp.product_id;
You sample query is much more complicated and has conditions that are not mentioned in the text of the question. But I think this structure will work for what you want to do.

Related

SQL INNER JOIN using CASE and 3 tables

I have the following tables and want show only the types that are available, while counting the products in that category.
Types
Type_Id
Name
1
Candy
2
Chocolate Bar
Products
Product_Id
Name
Type_Id
AvailabilityId
1
Chocolate Name 1
1
1
2
Chcoolate Name 1
2
2
3
Candy Name 1
2
2
Availability
Availability_Id
Name
1
Available
2
Reserved
3
Sol d
Desired Result
Type_Id
Name
TotalAvailable
1
Candy
1
2
Chocolate
2
Start with a basic JOIN between all three tables, on the related columns. Note use of table aliases t - Types, p - Products, a - Availability
SELECT *
FROM Types t
INNER JOIN Products p ON p.Type_Id = t.Type_Id
INNER JOIN Availability a ON a.Availability_Id = p.Availability_Id
Next add a WHERE clause to filter results by the availability status:
SELECT *
FROM Types t
INNER JOIN Products p ON p.Type_Id = t.Type_Id
INNER JOIN Availability a ON a.Availability_Id = p.Availability_Id
WHERE a.Name IN ('Available', 'Reserved')
Results:
Type_Id | Name | Product_Id | Name | Type_Id | Availability_Id | Availability_Id | Name
------: | :------------ | ---------: | :------------- | ------: | --------------: | --------------: | :--------
1 | Candy | 1 | Chocolate Name | 1 | 1 | 1 | Available
2 | Chocolate Bar | 2 | Chcoolate Name | 2 | 2 | 2 | Reserved
2 | Chocolate Bar | 3 | Candy Name | 2 | 2 | 2 | Reserved
Finally COUNT(*) the total rows matched, grouping by category (i.e. [Type].[Name])
SELECT t.Type_Id
, t.Name AS Type_Name
, COUNT(*) AS Total_Products
FROM Types t
INNER JOIN Products p ON p.Type_Id = t.Type_Id
INNER JOIN Availability a ON a.Availability_Id = p.Availability_Id
WHERE a.Name IN ('Available', 'Reserved')
GROUP BY t.Type_Id
, t.Name
Type_Id | Type_Name | Total_Products
------: | :------------ | -------------:
1 | Candy | 1
2 | Chocolate Bar | 2
SQL Fiddle
You can use an inner query with inner join to achieve this. Here's a SQL fiddle: http://sqlfiddle.com/#!9/42bd7f/2

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

SQlite join same table twice with different "on" statement

I couldn't find answer for my question, and I don't know if my query is correct and this could be a SQLite issue, please help me solve the problem.
I have two tables in my database:
processTable {id}
taskTable {id, processId, amount, done}
There is a many-to-one relation (one process can have multiple tasks assigned). The "amount" and "done" are integer values that provides task progress information. If "done" >= "amount", the task is done. I need to query database to get something like that:
+---------+-----------+------------+
| process | tasksDone | tasksCount |
+---------+-----------+------------+
| 1 | 1 | 3 |
+---------+-----------+------------+
| 2 | 2 | 5 |
+---------+-----------+------------+
Basing on data that I have in my tables:
processTable
+----+
| id |
+----+
| 1 |
+----+
| 2 |
+----+
tasksTable
+----+-----------+--------+------+
| id | processId | amount | done |
+----+-----------+--------+------+
| 1 | 1 | 10 | 10 | <- this task is done
+----+-----------+--------+------+
| 2 | 1 | 15 | 5 |
+----+-----------+--------+------+
| 3 | 1 | 80 | 5 |
+----+-----------+--------+------+
| 4 | 2 | 25 | 0 |
+----+-----------+--------+------+
| 5 | 2 | 60 | 60 | <- this task is done
+----+-----------+--------+------+
| 6 | 2 | 30 | 15 |
+----+-----------+--------+------+
| 7 | 2 | 40 | 40 | <- this task is done
+----+-----------+--------+------+
| 8 | 2 | 100 | 50 |
+----+-----------+--------+------+
So, I wrote this query:
SELECT processTable.id AS process,
COUNT(tasksTableDone.id) AS tasksDone,
COUNT(tasksTableAll.id) AS tasksCount
FROM processTable
LEFT JOIN tasksTable AS tasksTableAll
ON tasksTableAll.processId = processTable.id
LEFT JOIN tasksTable AS tasksTableDone
ON tasksTableDone.processId = processTable.id
AND
tasksTableDone.done >= tasksTableDone.amount
But what I've got is:
+---------+-----------+------------+
| process | tasksDone | tasksCount |
+---------+-----------+------------+
| 1 | 3 | 3 |
+---------+-----------+------------+
| 2 | 5 | 5 |
+---------+-----------+------------+
I was trying run the query with only one join at a time, and everything was working well.
Query with first join only:
SELECT processTable.id AS process,
COUNT(tasksTableAll.id) AS tasksCount
FROM processTable
LEFT JOIN tasksTable AS tasksTableAll
ON tasksTableAll.processId = processTable.id
Result:
+---------+------------+
| process | tasksCount |
+---------+------------+
| 1 | 3 |
+---------+------------+
| 2 | 5 |
+---------+------------+
Query with second join only:
SELECT processTable.id AS process,
COUNT(tasksTableDone.id) AS tasksDone
FROM processTable
LEFT JOIN tasksTable AS tasksTableDone
ON tasksTableDone.processId = processTable.id
AND
tasksTableDone.done >= tasksTableDone.amount
Result:
+---------+-----------+
| process | tasksDone |
+---------+-----------+
| 1 | 1 |
+---------+-----------+
| 2 | 2 |
+---------+-----------+
How to use this two joins within one query to get proper results? I know that instead of JOIN I could use another SELECT, but I think it would be more expensive in the performance meaning.
You can implement a CASE statement with an aggregate:
Version using SUM()
SELECT p.id AS process,
sum(case when t.amount = t.done then 1 else 0 end) AS tasksDone,
count(p.id) AS tasksCount
FROM processTable p
LEFT JOIN tasksTable t
ON t.processId = p.id
group by p.id
See SQL Fiddle with Demo
Version using COUNT():
SELECT p.id AS process,
count(case when t.amount = t.done then 1 else null end) AS tasksDone,
count(p.id) AS tasksCount
FROM processTable p
LEFT JOIN tasksTable t
ON t.processId = p.id
group by p.id
See SQL Fiddle with Demo
Edit, after your comment you can wrap this in a select to get the progress:
select process,
tasksDone,
tasksCount,
(tasksDone / tasksCount) progress
from
(
SELECT p.id AS process,
count(case when t.amount = t.done then 1 else null end) AS tasksDone,
count(p.id) AS tasksCount
FROM processTable p
LEFT JOIN tasksTable t
ON t.processId = p.id
group by p.id
) src

SQL join 3 tables (based on 2 criterias?)

I have 3 tables setup like this (a bit simplified):
time_tracking: id, tr_proj_id, tr_min, tr_type
time_projects: id, project_name
time_tasks: id, task_name
Basically, I want to retrieve either project_name or task_name based on tr_type which can be of value "project" or "task"
An example
time_tracking
+----+------------+--------+---------+
| id | tr_proj_id | tr_min | tr_type |
+----+------------+--------+---------+
| 1 | 3 | 60 | project |
| 2 | 3 | 360 | task |
| 3 | 1 | 120 | project |
| 4 | 2 | 30 | project |
| 5 | 2 | 30 | task |
| 6 | 1 | 90 | task |
+----+------------+--------+---------+
time_projects
+----+------------------------+
| id | project_name |
+----+------------------------+
| 1 | Make someone happy |
| 2 | Start a project |
| 3 | Jump out of the window |
+----+------------------------+
time_tasks
+----+---------------------+
| id | task_name |
+----+---------------------+
| 1 | drink a beer |
| 2 | drink a second beer |
| 3 | drink more |
+----+---------------------+
Desired output
+----+------------------------+------------+--------+---------+
| id | name | tr_proj_id | tr_min | tr_type |
+----+------------------------+------------+--------+---------+
| 1 | Jump out of the window | 3 | 60 | project |
| 2 | drink more | 3 | 360 | task |
| 3 | Make someone happy | 1 | 120 | project |
| 4 | Start a project | 2 | 30 | project |
| 5 | drink a second beer | 2 | 30 | task |
| 6 | drink a beer | 1 | 90 | task |
+----+------------------------+------------+--------+---------+
And being really bad at the whole JOIN thing, here's the only thing I've come up with so far (which doesn't work..):
SELECT tt.tr_proj_id, tt.tr_type, tt.tr_min, pp.project_name, pp.id, ta.task_name, ta.id
FROM time_tracking as tt, time_projects as pp, time_tasks as ta
WHERE ((tt.tr_type = 'project' AND pp.id = tt.tr_proj_id) OR (tt.tr_type = 'task' AND ta.id = tt.tr_proj_id))
AND tt.tr_min > 0
ORDER BY tt.tr_proj_id DESC
If anyone has an idea on how to do this, feel free to share!
Update: Looks like I forgot to specify that I'm using an access database. Which apparently doesn't accept things like CASE or coalesce.. Apparently there is IIF() but I'm not quite sure on how to use it in this case.
Use join clauses and move your join conditions from the where clause into the on clauses:
SELECT
tt.tr_proj_id,
tt.tr_type,
tt.tr_min,
pp.project_name,
pp.id,
ta.task_name,
ta.id
FROM time_tracking as tt
left join time_projects as pp on tt.tr_type = 'project' AND pp.id = tt.tr_proj_id
left join time_tasks as ta on tt.tr_type = 'task' AND ta.id = tt.tr_proj_id
WHERE tt.tr_min > 0
ORDER BY tt.tr_proj_id DESC,tt.tr_day ASC
I've used left join, which gives you a row from the main table even if one doesn't exist for the join (you get nulls from columns in the joined table if there's no join)
A key point here, that many SQL programmers do not realise, is that the ON clause may contain any conditions, even ones not from the joined table (as in this example). Many programmers assume that the conditions must be only those relating to the formal foreign key relationship.
Try this:
SELECT
tt.id,
CASE WHEN tt.tr_type = 'project' THEN pp.project_name
WHEN tt.tr_type = 'task' THEN ta.task_name END as name,
tt.tr_proj_id,
tt.tr_type,
tt.tr_min,
FROM time_tracking as tt
left join time_projects as pp on pp.id = tt.tr_proj_id
left join time_tasks as ta on ta.id = tt.tr_proj_id
WHERE tt.tr_min > 0
ORDER BY tt.tr_proj_id DESC
perform a union on two joins:
select tt.id, tp.project_name name, tt.tr_proj_id, tt.tr_min, tt.tr_type
from time_tracking tt
inner join time_projects tp on tp.id = tt.tr_proj_id
where tt.tr_type = 'project'
union all
select tt.id, tp.project_name name, tt.tr_proj_id, tt.tr_min, tt.tr_type
from time_tracking tt
inner join time_tasks tk on tk.id = tt.tr_proj_id
where tt.tr_type = 'task'
That will give you the exact table results you want
SELECT
time_tracking.id,
time_tracking.tr_min,
time_tracking.tr_type,
coalesce(time_projects.project_name, time_tasks.task_name) as name
FROM time_tracking
LEFT OUTER JOIN time_projects on time_projects.id = time_tracking.tr_proj_id AND time_tracking.tr_type = 'project'
LEFT OUTER JOIN time_tasks on time_tasks.id = time_tracking.tr_proj_id AND time_tracking.tr_type = 'task'
WHERE time_tracking.tr_min > 0
ORDER BY time_tracking.id DESC -- ...
coalesce is MSSQL, there's equivalent ISNULL and such in other database technologies
The idea is you join to the tables and if the join fails, you'll get NULL where the join failed. Then you use COALESCE to pick out the successful join value.

Inner join with multiple tables

I have these four tables:
PRODUCTS
---------
PRODUCT_ID
PRODUCT_TITLE
(other fields)
COLORS
---------
COLOR_ID
COLOR_NAME
MATERIALS
---------
MATERIAL_ID
MATERIAL_NAME
IMAGES
---------
IMAGE_ID
BIG
MED
SMALL
THUMB
SIZE
---------
SIZE_ID
SIZE_NAME
And also:
PRODUCT_COLOR
---------
PRODUCT_ID
COLOR_ID
PRODUCT_MATERIAL
---------
PRODUCT_ID
MATERIAL_ID
PRODUCT_SIZE
---------
PRODUCT_ID
SIZE_ID
PRODUCT_IMAGE
---------
PRODUCT_ID
IMAGE_ID
COLOR_ID (can be null)
MATERIAL_ID (can be null)
All the products can have a different color and/or material. E.g. I can have a product that has one or more material options but no colors associated and vice versa. The output should be something like this:
-----------------------------------------------------------------------------
| PRODUCT_ID | PRODUCT_NAME | COLOR_ID | MATERIAL_ID | IMAGE_ID | SIZE_ID |
-----------------------------------------------------------------------------
| 1 | T-SHIRT | 1 | null | 1 | 1 |
| 1 | T-SHIRT | 1 | null | 1 | 2 |
| 1 | T-SHIRT | 1 | null | 1 | 3 |
| 1 | T-SHIRT | 1 | null | 1 | 4 |
| 2 | JEANS | null | 1 | 2 | 1 |
| 2 | JEANS | null | 1 | 2 | 2 |
| 2 | JEANS | null | 1 | 2 | 3 |
| 2 | JEANS | null | 1 | 2 | 4 |
| 2 | JEANS | null | 1 | 2 | 5 |
| 3 | T-SHIRT VNECK | 2 | 2 | 3 | 1 |
| 3 | T-SHIRT VNECK | 2 | 2 | 3 | 2 |
| 3 | T-SHIRT VNECK | 3 | 2 | 4 | 1 |
| 3 | T-SHIRT VNECK | 3 | 2 | 4 | 2 |
| 3 | T-SHIRT VNECK | 4 | 3 | 5 | 1 |
| 3 | T-SHIRT VNECK | 4 | 3 | 5 | 2 |
-----------------------------------------------------------------------------
I have tried the following statement but it returns 0 rows:
SELECT PRODUCTS.PRODUCT_ID, PRODUCTS.PRODUCT_TITLE, COLORS.COLOR_ID, MATERIALS.MATERIAL_ID, IMAGES.IMAGE_ID, SIZE.SIZE_ID from PRODUCTS
INNER JOIN PRODUCT_COLOR ON (PRODUCTS.PRODUCT_ID = PRODUCT_COLOR.PRODUCT_ID)
INNER JOIN COLORS ON (COLORS.COLOR_ID = PRODUCT_COLOR.COLOR_ID)
INNER JOIN PRODUCT_MATERIAL ON (PRODUCTS.PRODUCT_ID = PRODUCT_MATERIAL.PRODUCT_ID)
INNER JOIN MATERIALS ON (MATERIALS.MATERIAL_ID = PRODUCT_MATERIAL.MATERIAL_ID)
INNER JOIN PRODUCT_IMAGE ON (PRODUCTS.PRODUCT_ID = PRODUCT_IMAGE.PRODUCT_ID)
INNER JOIN IMAGES ON (IMAGES.IMAGE_ID = PRODUCT_IMAGE.IMAGE_ID)
INNER JOIN PRODUCT_SIZE ON (PRODUCTS.PRODUCT_ID = PRODUCT_SIZE.PRODUCT_ID)
INNER JOIN SIZE ON (SIZE.SIZE_ID = PRODUCT_SIZE.SIZE_ID)
ORDER BY PRODUCTS.id_PRODUCT;
Any ideas?
You could do something like this:
select p.product_id,
p.product_name,
c.color_id,
m.material_id,
i.image_id,
s.size_id
from products p
left join product_color pc
on p.product_id = pc.product_id
left join colors c
on pc.color_id = c.colorid
left join product_material pm
on p.product_id = pm.product_id
left join materials m
on pm.material_id = m.material_id
left join product_image pi
on p.product_id = pi.product_id
left join images i
on pi.image_id = i.image_id
or c.color_id = i.color_id
or m.material_id = i.material_id
left join product_size ps
on p.product_id = ps.product_id
left join size s
on ps.size_id = s.size_id
I would advise you reviewing JOINs. There is a great visual explanation of joins online that will help you write these queries.
Well, you need to learn how to build joins, and the way I normally do it is by selecting one table and join the next one and the next one and the next one until I have the result I want.
select product_id, product_name
from products
next I join the first one I need so I go ahead and say
select p.product_id, p.product_name, pc.color_id
from products p
join product_color pc on (pc.product_id = p.product_id)
On the join it is important to figure out if I maybe have nothing to join with and I still want to see the line. So I rather use a left join
select p.product_id, p.product_name, pc.color_id
from products p
left join product_color pc on (pc.product_id = p.product_id)
That way you add each table to join. By the way. Is this homework?
If you just need IDs, keep it simple
select p.product_id,
p.product_name,
pc.color_id,
pm.material_id,
pi.image_id,
ps.size_id
from products p,
PRODUCT_COLOR pc,
product_material pm,
PRODUCT_SIZE ps,
PRODUCT_IMAGE pi
where
p.product_id = pc.product_id(+)
and p.product_id = pm.product_id(+)
and p.product_id = ps.product_id(+)
and p.product_id = pi.product_id(+);