compare suppliers from 2 tables in SQL Oracle - sql

I have 2 tables
in both tables are suppliers with items. In the table supplier_with_awards are suppliers which can deliver an item. for 1 item there could be several suppliers. in the table suppliers_with_incomming_goods are suppliers that actually supply the items. There is a situation that non-awarded supplier supplies an item.
case 1
I need to check if the item is in both tables and pick up only those with different suppliers
case 2
same as case 1 but pick up non-awarded suppliers as well.
my data
CREATE TABLE suppliers_with_awards ( supplier, item ) AS
SELECT 'supplier1', 'item1' FROM DUAL UNION ALL
SELECT 'supplier2', 'item1' FROM DUAL UNION ALL
SELECT 'supplier3', 'item2' FROM DUAL UNION ALL
SELECT 'supplier4', 'item3' FROM DUAL ;
CREATE TABLE suppliers_with_incoming_goods ( supplier, item ) AS
SELECT 'supplier1', 'item1' FROM DUAL UNION ALL
SELECT 'supplier2', 'item1' FROM DUAL UNION ALL
SELECT 'supplier5', 'item2' FROM DUAL UNION ALL
SELECT 'supplier6', 'item4' FROM DUAL ;
with simple join we get for item1 unnecessary combinations supplier1-supplier2 and vica versa but in reality supplier1 got award and supplier1 delivers, the same goes for supplier2. So I used row_number to exclude such cross combo if you have better solution let me know.
with award as (
select supplier, item, row_number() over (partition by item order by supplier) r
from suppliers_with_awards
),
goods as (
select supplier, item, row_number() over (partition by item order by supplier) r
from suppliers_with_incoming_goods
)
select a.supplier,a.item,g.supplier from award a join goods g on a.item=g.item and a.r=g.r and a.supplier<>g.supplier;
SUPPLIER ITEM SUPPLIER
supplier3 item2 supplier5
this query finds the item2 because there are different suppliers as I want (case 1).again if there is better solution for this , please ....
But I need somehow to get the non-awarded supplier6 with item4 as well (case 2)
thanks

The current query may not return all results with inserted different values such as supplier7 and supplier8 for item1 inserted into the table suppliers_with_awards. I don't recommend use analytic function in this case, rather you can convert the query into the following which includes NOT EXISTS. And use UNION ALL, since you may need to return more than two supplier which already should be independently listed each unique one into one seperate line
--# Case 1
WITH item_supplier AS
(
SELECT g.item AS item,
a.supplier AS supplier_a,g.supplier AS supplier_g
FROM suppliers_with_awards a
JOIN suppliers_with_incoming_goods g
ON a.item = g.item
)
SELECT DISTINCT item, supplier_a AS supplier
FROM item_supplier i
WHERE NOT EXISTS ( SELECT 0
FROM item_supplier
WHERE supplier_g = i.supplier_a)
UNION ALL
SELECT DISTINCT item, supplier_g
FROM item_supplier i
WHERE NOT EXISTS ( SELECT 0
FROM item_supplier
WHERE supplier_a = i.supplier_g)
for the second case just convert the INNER JOIN to RIGHT or FULL JOIN, and filter out the NULL values of item and supplier in the main query such as
--# Case 2
WITH item_supplier AS
(
SELECT g.item AS item,
a.supplier AS supplier_a,g.supplier AS supplier_g
FROM suppliers_with_awards a
RIGHT JOIN suppliers_with_incoming_goods g
ON a.item = g.item
), its AS
(
SELECT DISTINCT item, supplier_a as supplier
FROM item_supplier i
WHERE NOT EXISTS ( SELECT 0
FROM item_supplier
WHERE supplier_g = i.supplier_a)
UNION ALL
SELECT DISTINCT item, supplier_g
FROM item_supplier i
WHERE NOT EXISTS ( SELECT 0
FROM item_supplier
WHERE supplier_a = i.supplier_g)
)
SELECT *
FROM its
WHERE item IS NOT NULL
AND supplier IS NOT NULL
Demo

Related

Fetch rows with same id and different prod_id

I have two tables: tbltest1 and tbltest2
I want all the distinct rows of both tables, except the ones that have null in prod_id unless there is not any row in both tables with the same id with a not null prod_id
I tried to make a set with all the values then DISTINCTed to take only the unique ones and after used ROWNUMBER() OVER().:
with p as(
select t.*
from tbltest1 as t
union all
select d.*
from tbltest2 as d
),
s as (
select distinct colb, num,
ROW_NUMBER() OVER (PARTITION BY num ORDER BY colb DESC) as rnk
from p
)select *
from s
where rnk = 1
How can I achieve that? Is there also any other more efficient way to do it instead of this logic?
Use UNION for the 2 tables to remove the duplicates (if any) and then NOT EXISTS:
WITH cte AS (
SELECT prod_id, dn FROM tbltest2
UNION
SELECT prod_id1, dn1 FROM tbltest1
)
SELECT c1.*
FROM cte c1
WHERE c1.prod_id IS NOT NULL
OR NOT EXISTS (SELECT 1 FROM cte c2 WHERE c2.dn = c1.dn AND c2.prod_id IS NOT NULL)
See the demo.

Getting MAX datetime event from multiple tables, and outputing a simple list of most recent events by ID

I have a table:
and multiple other tables - consider them purchases, in this example:
And would like an output table to show the most recent purchase (NB that there may be multiple instances of a purchase within each table), by id from the main table:
The id can be a customer number, for example.
I've tried using OUTER APPLY on each purchase table, getting the TOP 1 by datetime desc, then getting the max value from the OUTER APPLY tables, but I would not get the table name - eg. Apples, just the datetime.
Another idea was to UNION all of the purchase tables together in a join with the main table (by id), and pick out the top 1 datetime and a table name, but I don't think this would be very efficient for a lot of rows:
SELECT MT.id, MT.gender, MT.age,
b.Name as LastPurchase, b.dt as LastPurchaseDateTime
FROM MainTable MT
LEFT JOIN (
SELECT id, Name, MAX(dt) FROM
(
SELECT id, 'Apples' as Name, ApplesDateTime as dt FROM ApplesTable
UNION
SELECT id, 'Pears' as Name, PearsDateTime as dt FROM PearsTable
UNION
SELECT id, 'Bananas' as Name, BananasDateTime as dt FROM BananasTable
)a
GROUP BY etc
)b
Does anyone have a more sensible idea?
Many thanks in advance.
I would go for a lateral join:
select m.*, x.*
from maintable m
outer apply (
select top (1) x.*
from (
select id, 'apples' as name, applesdatetime as dt from applestable
union all select id, 'pears', pearsdatetime from pearstable
union all select id, 'bananas', bananasdatetime from bananastable
) x
where x.id = m.id
order by dt desc
) x
I would suggest apply:
SELECT MT.id, mt.gender, mt.age, p.*
FROM MainTable MT OUTER APPLY
(SELECT p.name, p.dt
FROM (SELECT id, 'Apples' as Name, ApplesDateTime as dt FROM ApplesTable
UNION ALL
SELECT id, 'Pears' as Name, PearsDateTime as dt FROM PearsTable
UNION ALL
SELECT id, 'Bananas' as Name, BananasDateTime as dt FROM BananasTable
) p
WHERE p.id = mt.id
ORDER BY dt DESC
) p

Select Customer ID who hasnt purchased product X

I have a table of customer IDs and Products Purchased. A customer ID can purchase multiple products over time.
customerID, productID
In BigQuery I need to find the CustomerID for those who have not purchased product A.
I've been going around in circles trying to do self joins, inner joins, but I'm clueless.
Any help appreciated.
select customerID
from your_table
group by customerID
having sum(case when productID = 'A' then 1 else 0 end) = 0
and to check if it only contains a name
sum(case when productID contains 'XYZ' then 1 else 0 end) = 0
Below is for BigQuery Standard SQL
#standardSQL
SELECT CustomerID
FROM `project.dataset.yourTable`
GROUP BY CustomerID
HAVING COUNTIF(Product = 'A') = 0
You can test / play with it using dummy data as below
#standardSQL
WITH `project.dataset.yourTable` AS (
SELECT 1234 CustomerID, 'A' Product UNION ALL
SELECT 11234, 'A' UNION ALL
SELECT 4567, 'A' UNION ALL
SELECT 7896, 'C' UNION ALL
SELECT 5432, 'B'
)
SELECT CustomerID
FROM `project.dataset.yourTable`
GROUP BY CustomerID
HAVING COUNTIF(Product = 'A') = 0
how would I adjust this so it could be productID contains "xyz"
#standardSQL
WITH `project.dataset.yourTable` AS (
SELECT 1234 CustomerID, 'Axyz' Product UNION ALL
SELECT 11234, 'A' UNION ALL
SELECT 4567, 'A' UNION ALL
SELECT 7896, 'Cxyz' UNION ALL
SELECT 5432, 'B'
)
SELECT CustomerID
FROM `project.dataset.yourTable`
GROUP BY CustomerID
HAVING COUNTIF(REGEXP_CONTAINS(Product, 'xyz')) = 0
If you have a customer table, you might want:
select c.*
from customers c
where not exists (select 1 from t where t.customer_id = c.customer_id and t.proectID = 'A');
This will return customers who have made no purchases as well as those who have purchased all but product A. Of course, the definition of a customer in your data might be that the customer has made a purchase, in which case I like Juergen's solution.

Case on union of multiple unions and issue with alias

I have 2 series of unions which I wish to join by another union. In the first one, I have 3 Selects and in the second one I have 2 different Selects.
Select id, min(value)
from table1 t1
join (Select id, value
Union
Select id, value
Union
Select id, value) as foo
on foo.id=t1.id
Group by id
Select id, max(value)
from table1 t1
join (Select id, value
Union
Select id, value) as bar
on bar.id=t1.id
Group by id
I tried to do a union between these two, but it made things pretty complicated. My biggest issue is with my alias. My second is with the case linked to my value columns, which I wish to name value.
Select (alias).id,
Case
When foo.value= 0 or bar.value=1 THEN 1
Else 0
End as value
from table1 t1
Join (Select id, min(value)
from table1 t1
join (Select id, value
Union
Select id, value
Union
Select id, value) as foo
on foo.id=t1.id
Group by id
UNION
Select id, max(value)
from table1 t1
join (Select id, value
Union
Select id, value) as bar
on bar.id=t1.id
Group by id) as (alias)
on ??.id=??.id
I wrote my case the way I think it should be written, but normally, when there are more than one column with the same name, SQL states it as ambiguous. I am still unsure if I should use UNION or INTERSECT, but I assume either of them would be done the same way. How should I deal with this?
I'm reading this right, you probably want something like this
SELECT ...
FROM ( ... union #1 ) AS u1
JOIN (... union #2 ) AS u2 ON u1.id = u2.id

SQL Query, Union Order by like a tree

Hello I have a query like
SELECT 4 AS sortf,XX FROM Table GROUP BY Y
UNION
SELECT 1 AS sortf,XX FROM Table GROUP BY Y
UNION
SELECT 2 AS sortf,XX FROM Table GROUP BY Y
UNION
SELECT 3 AS sortf,XX FROM Table GROUP BY Y
ORDER BY 3,2
My problem is that the line 2 and 3 ar not ordered like a tree. I tried some other combinations but it did not work.
if you want to sort your dataset according to numbers put your code and unions into a common table expression and the use ROW_NUMBER() function to generates row number , something like this :
WITH CTE
AS
(
SELECT 4 AS sortf, productid FROM Production.Products
UNION
SELECT 1 AS sortf,productid FROM Production.Products
UNION
SELECT 2 AS sortf,productid FROM Production.Products
UNION
SELECT 3 AS sortf,productid FROM Production.Products
)
SELECT *, ROW_NUMBER() OVER (ORDER BY productid) AS SortOrder
FROM CTE
ORDER BY SortOrder