Return 0 quantity when SKU not found - sql

I have in my database 2 tables: warehouse and warehouse_inventory.
My warehouse table looks like:
name | warehouse_id |
--------+----------------|
name_1 | warehouse_id_1 |
name_2 | warehouse_id_2 |
name_3 | warehouse_id_3 |
My warehouse_inventory table looks like:
warehouse_id | SKU | quantity |
----------------+-------------+----------|
warehouse_id_1 | item_sku_1 | 100 |
warehouse_id_2 | item_sku_1 | 100 |
And now, I am looking for a solution which gets me result like this:
name | warehouse_id | SKU | quantity |
--------+-----------------------------+----------|
name_1 | warehouse_id_1 | item_sku_1 | 100 |
name_1 | warehouse_id_1 | item_sku_2 | 0 |
name_2 | warehouse_id_2 | item_sku_1 | 100 |
name_2 | warehouse_id_2 | item_sku_2 | 0 |
name_3 | warehouse_id_3 | item_sku_1 | 0 |
name_3 | warehouse_id_3 | item_sku_2 | 0 |
when I select with where condition in SQL query:
SELECT * FROM [what's here?] WHERE warehouse_inventory.SKU IN ('item_sku_1', 'item_sku_2')

You can't do this if you don't have a table with all the SKU you have on your database.
So, assuming that you have a table called [item] for all your item master data, you should do that:
SELECT W0.name, W0.warehouse_id, I0.SKU, ISNULL(W1.quatity, 0) as 'Quantity'
FROM [item] I0
CROSS JOIN [warehouse] W0
LEFT JOIN [warehouse_inventory] W1 ON W1.warehouse_id = W0.warehouse_id AND W1.SKU = I0.SKU
On SAP Business One we do this:
SELECT W0.WhsName, W0.WhsCode, I0.ItemCode, ISNULL(W1.OnHand, 0) as 'Quantity'
FROM OITM I0
CROSS JOIN OWHS W0
LEFT JOIN OITW W1 ON W1.WhsCode = W0.WhsCode AND W1.ItemCode = I0.ItemCode
ORDER BY 1 DESC

You can use CROSS JOIN with LEFT JOIN:
select w.name, w.warehouse_id, wii.SKU, coalesce(i.quantity, 0) as quantity
from warehouse w cross join
warehouse_inventory wii left join
warehouse_inventory i
on i.warehouse_id = w.warehouse_id and i.SKU = wii.SKU
order by w.name;

Related

Issue with SQL join and group

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..

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

Can't show all records with the same id while join in oracle xe 11g

I'm getting this message while using this query, is there anything wrong?
SELECT t.tanggal_transaksi, o.nama_lengkap, SUM(td.harga * td.qty) total
FROM transaksi t, transaksi_detail td, operator o
WHERE td.transaksi_id = t.transaksi_id AND o.operator_id = t.operator_id
GROUP BY t.transaksi_id
Updated :
After using the answer from #Barbaros Özhan using this query :
SELECT t.tanggal_transaksi, o.nama_lengkap, SUM(td.harga * td.qty) total
FROM transaksi t
INNER JOIN transaksi_detail td ON ( td.transaksi_id = t.transaksi_id )
INNER JOIN operator o ON ( o.operator_id = t.operator_id )
GROUP BY t.tanggal_transaksi, o.nama_lengkap;
the data is successfully displayed. but, there are few problems that occur, the value of the same operator_id cannot appear more than 1 time. Here is the sample data :
+--------------+-------------+-------------------+
| TRANSAKSI_ID | OPERATOR_ID | TANGGAL_TRANSAKSI |
+--------------+-------------+-------------------+
| 1 | 5 | 09/29/2018 |
| 2 | 3 | 09/29/2018 |
| 3 | 3 | 09/29/2018 |
| 4 | 1 | 09/29/2018 |
| 5 | 1 | 09/29/2018 |
+--------------+-------------+-------------------+
After use the query command, the output is :
+-------------------+------------------+--------+
| TANGGAL_TRANSAKSI | NAMA_LENGKAP | TOTAL |
+-------------------+------------------+--------+
| 09/29/2018 | Lina Harun | 419800 |
| 09/29/2018 | Titro Kusumo | 484000 |
| 09/29/2018 | Muhammad Kusnadi | 402000 |
+-------------------+------------------+--------+
When viewed from the operator table, there are 2 data with the same operator_id that is unreadable
+-------------+------------------+
| OPERATOR_ID | NAMA_LENGKAP |
+-------------+------------------+
| 1 | Muhammad Kusnadi |
| 3 | Lina Harun |
| 5 | Tirto Kusumo |
+-------------+------------------+
You need to include the columns in the SELECT-list t.tanggal_transaksi, o.nama_lengkap, also in the GROUP BY-list but not the others like t.transaksi_id. So, you might use the following without any issue :
SELECT t.tanggal_transaksi, o.nama_lengkap, SUM(td.harga * td.qty) total
FROM transaksi t
INNER JOIN transaksi_detail td ON ( td.transaksi_id = t.transaksi_id )
INNER JOIN operator o ON ( o.operator_id = t.operator_id )
GROUP BY t.tanggal_transaksi, o.nama_lengkap;
Or this one :
SELECT t.transaksi_id, SUM(td.harga * td.qty) total
FROM transaksi t
INNER JOIN transaksi_detail td ON ( td.transaksi_id = t.transaksi_id )
GROUP BY t.transaksi_id;
P.S. Prefer using ANSI-92 JOIN standard rather than old-style comma-type JOIN.

Joining two tables + third table as a conditional column

If i have three tables called warehouse , warehouse_order and table called warehouse_fulfillment. A warehouse order is created by warehouse admin, initially has 0 fulfillment records, can have many warehouse_fulfillment records with failed/rejected statuses, and only one success state (it's done):
-- Warehouse
+---------------------------------------+-----------+----------+
| id | name | location |
+---------------------------------------+-----------+----------+
| 9bcae08e-ad36-4d97-b9ec-4857714e902a | "big" | "MLB" |
+---------------------------------------+-----------+----------+
| b442e783-4725-41e9-af83-f75004ee1b38 | "bigger" | "MLB" |
+---------------------------------------+-----------+----------+
| 986d5aa9-0523-42d8-b183-dfd546d3e682 | "biggest" | "MLB" |
+---------------------------------------+-----------+----------+
-- Warehouse_order Table
+---------------------------------------+--------------------------------------+--------+----------+
| id | warehouse_id | type | quantity |
+---------------------------------------+--------------------------------------+--------+----------+
| 9cb99fd9-9e5e-4240-8162-d28747be01cd | b442e783-4725-41e9-af83-f75004ee1b38 | BN_100 | 100 |
+---------------------------------------+-------------------------------------+--------+-----------+
| eceb0b5a-5afa-40e4-ac62-efb686e3bdae | 9bcae08e-ad36-4d97-b9ec-4857714e902a | BN_200 | 400 |
+---------------------------------------+--------------------------------------+--------+----------+
| 13370467-cf0c-47f2-8fea-a215500607e6 | 986d5aa9-0523-42d8-b183-dfd546d3e68 | BN_300 | 10 |
+---------------------------------------+--------------------------------------+--------+----------+
-- Warhouse_fulfillment Table
+---------------------------------------+---------------------------------------+------------+
| id | order_id | status |
+---------------------------------------+---------------------------------------+------------+
| 8a69edde-2346-48b8-96d0-6c4e25527f38 | 9cb99fd9-9e5e-4240-8162-d28747be01cd | "FAILLED" |
+---------------------------------------+---------------------------------------+------------+
| a2006a64-9bdc-4bfa-ba14-a44769aeb4a2 | 9cb99fd9-9e5e-4240-8162-d28747be01cd | "REJECTED" |
+---------------------------------------+---------------------------------------+------------+
| bf0aa1fc-6dfc-4fd0-ba20-be101b1985d1 | 9cb99fd9-9e5e-4240-8162-d28747be01cd | "FAILED" |
+---------------------------------------+---------------------------------------+------------+
| 48c7d747-2f9b-4535-8f27-210a43cf5c30 | 9cb99fd9-9e5e-4240-8162-d28747be01cd | "SUCCESS" |
+---------------------------------------+---------------------------------------+------------+
| 7f8e18c9-4322-428a-9370-9ecd1c5ef286 | 13370467-cf0c-47f2-8fea-a215500607e6 | "FAILED" |
+---------------------------------------+---------------------------------------+------------+
I want to query the above records in such a way that result looks like so:
+--------------------------------------+-----------+----------+---------------------------------------+------------+----------------+--------------------------------------+
| id | name | location | order_id | order_type | order_quantity | fulfillment_id |
+--------------------------------------+-----------+----------+---------------------------------------+------------+----------------+--------------------------------------+
| 9bcae08e-ad36-4d97-b9ec-4857714e902a | "big" | "MLB" | eceb0b5a-5afa-40e4-ac62-efb686e3bdae | "BN_100" | 100 | NULL |
+--------------------------------------+-----------+----------+---------------------------------------+------------+----------------+--------------------------------------+
| b442e783-4725-41e9-af83-f75004ee1b38 | "bigger" | "MLB" | 9cb99fd9-9e5e-4240-8162-d28747be01cd | "BN_200" | 400 | 48c7d747-2f9b-4535-8f27-210a43cf5c30 |
+--------------------------------------+-----------+----------+---------------------------------------+------------+----------------+--------------------------------------+
| 986d5aa9-0523-42d8-b183-dfd546d3e682 | "biggest" | "MLB" | 13370467-cf0c-47f2-8fea-a215500607e6 | "BN_300" | 10 | NULL |
+--------------------------------------+-----------+----------+---------------------------------------+------------+----------------+--------------------------------------+
I couldn't do this without the repeated rows in cases where an order has multiple failed statuses.
Did you try SELECT DISTINCT? Because you don't have the status column (which causes the duplicates) in the select list, this should work.
SELECT DISTINCT W.id, W.name, W.location, WO.id order_id, WO.type order_type, WO.quantity order_quantity, WF.id fulfillment_id
FROM warehouse W
LEFT JOIN warehouse_order WO ON W.id = WO.warehouse_id
LEFT JOIN warehouse_fulfillment WF on WF.order_id = WO.id
Otherwise, I would need to know what DBMS the SQL is for, but every flavor I've worked with has some way to rank/order results with a partition so that you can take just the first record based on some key, for example:
SELECT id, name, location, order_id, order_type, order_quantity
FROM (
SELECT W.id, W.name, W.location, WO.id order_id, WO.type order_type, WO.quantity order_quantity, WF.id fulfillment_id, ROW_NUMBER() OVER (PARTITION BY WO.id ORDER BY WF.ID) rNum
FROM warehouse W
LEFT JOIN warehouse_order WO ON W.id = WO.warehouse_id
LEFT JOIN warehouse_fulfillment WF on WF.order_id = WO.id
) A
WHERE rNum = 1
It would be better to order by date DESC or something like that to get the most recent record.
Without some reliable method within the fulfillment table to determine "latest status", such as a timestamp, you will need to choose some arbitrary method to arrive at a priority order amongst the possible status values. Below I have used a case expression within the over clause so that "success" will be get the row number 1 if that status exists for an order. Adjust the case expression as you see fit for the other possible values of that column.
When the subquery containing the row number is joined to the main query that join includes and rn=1 so only fulfillment row per order will be possible.
Please note that in the sample data there is a missing warehouse row so I had to use a left join, but I expect it would be an inner join in the real db.
SQL Fiddle Demo
CREATE TABLE Warehouse
(ID varchar(36), Name varchar(9), Location varchar(5))
;
INSERT INTO Warehouse
("id", "name", "location")
VALUES
('9bcae08e-ad36-4d97-b9ec-4857714e902a', 'big', 'MLB'),
('b442e783-4725-41e9-af83-f75004ee1b38', 'bigger', 'MLB'),
('986d5aa9-0523-42d8-b183-dfd546d3e682', 'biggest', 'MLB')
;
CREATE TABLE Warehouse_order
(ID varchar(36), Warehouse_id varchar(36), type varchar(6), quantity int)
;
INSERT INTO Warehouse_order
("id", "warehouse_id", "type", "quantity")
VALUES
('9cb99fd9-9e5e-4240-8162-d28747be01cd', 'b442e783-4725-41e9-af83-f75004ee1b38', 'BN_100', 100),
('eceb0b5a-5afa-40e4-ac62-efb686e3bdae', '9bcae08e-ad36-4d97-b9ec-4857714e902a', 'BN_200', 400),
('13370467-cf0c-47f2-8fea-a215500607e6', '986d5aa9-0523-42d8-b183-dfd546d3e68', 'BN_300', 10)
;
CREATE TABLE Warehouse_fulfillment
(ID varchar(36), Order_id varchar(36), Status varchar(10))
;
INSERT INTO Warehouse_fulfillment
("id", "order_id", "status")
VALUES
('8a69edde-2346-48b8-96d0-6c4e25527f38', '9cb99fd9-9e5e-4240-8162-d28747be01cd', 'FAILLED'),
('a2006a64-9bdc-4bfa-ba14-a44769aeb4a2', '9cb99fd9-9e5e-4240-8162-d28747be01cd', 'REJECTED'),
('bf0aa1fc-6dfc-4fd0-ba20-be101b1985d1', '9cb99fd9-9e5e-4240-8162-d28747be01cd', 'FAILED'),
('48c7d747-2f9b-4535-8f27-210a43cf5c30', '9cb99fd9-9e5e-4240-8162-d28747be01cd', 'SUCCESS'),
('7f8e18c9-4322-428a-9370-9ecd1c5ef286', '13370467-cf0c-47f2-8fea-a215500607e6', 'FAILED')
;
Query 1:
select
o.*, w.name, s.status, s.rn
from Warehouse_order o
left join Warehouse w on o.Warehouse_id = w.id
left join (
select id, order_id, status
, row_number() over(partition by order_id
order by case when status = 'SUCCESS' then 1
when status = 'FAILED' then 2
when status = 'REJECTED' then 3
else 4 end) as rn
from Warehouse_fulfillment
) s on o.id = s.Order_id and rn=1
Results:
| id | warehouse_id | type | quantity | name | status | rn |
|--------------------------------------|--------------------------------------|--------|----------|--------|---------|--------|
| eceb0b5a-5afa-40e4-ac62-efb686e3bdae | 9bcae08e-ad36-4d97-b9ec-4857714e902a | BN_200 | 400 | big | (null) | (null) |
| 9cb99fd9-9e5e-4240-8162-d28747be01cd | b442e783-4725-41e9-af83-f75004ee1b38 | BN_100 | 100 | bigger | SUCCESS | 1 |
| 13370467-cf0c-47f2-8fea-a215500607e6 | 986d5aa9-0523-42d8-b183-dfd546d3e68 | BN_300 | 10 | (null) | FAILED | 1 |
I'm not entirely sure on this, but it sounds like you just want a left join on the order_fulfillment table with the "success" condition in the join:
select
w.id, w.name, w.location,
o.id as order_id, o.type as order_type,
o.quantity as order_quantity,
f.id as fulfillment_id
from
warehouse w
join warehouse_order o on
w.id = o.warehouse_id
left join warhouse_fulfillment f on
o.id = f.order_id and
f.status = 'SUCCESS'
Since you don't seem to care about non-successful records, and there is guarantee that there will only be one 'SUCCESS' fulfillment record, this should avoid any duplicates.

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