Oracle Query to Join Two Tables based on Conditions & Group By - sql

I have table in below format,
Table 1
Bank
Category
Month_Year
Loan_Type
Outstanding
SI
R1
JAN-21
Home
10
SI
R1
JAN-21
Land
50
SI
R2
FEB-21
Home
30
SI
R2
MAR-21
Car
40
Table 2
Bank
Loan_Type
SI
Home
SI
Land
SI
Car
SI
Jewel
SI
Education
I would like to convert the table A and B in to below format using join/query. The data(all rows) from the table-2 should get added based of the Category and Month_Year.
BANK
Category
Month_Year
Loan_Type
Outstanding
SI
R1
JAN-21
Home
10
SI
R1
JAN-21
Land
50
SI
R1
JAN-21
Car
0
SI
R1
JAN-21
Jewel
0
SI
R1
JAN-21
Education
0
SI
R2
FEB-21
Home
30
SI
R2
FEB-21
Land
0
SI
R2
FEB-21
Car
0
SI
R2
FEB-21
Jewel
0
SI
R2
FEB-21
Education
0
SI
R2
MAR-21
Home
0
SI
R2
MAR-21
Land
0
SI
R2
MAR-21
Car
40
SI
R2
MAR-21
Jewel
0
SI
R2
MAR-21
Education
0

Principally a CROSS JOIN needed among the tables after Category and Month_Year columns are distinctly selected, and Outstanding column is added in the main query as zero for non-matching values, otherwise returning values of it such as
SELECT t2.Bank, t2.Category, t2.Month_Year, t2.Loan_Type,
NVL(t1.Outstanding,0) AS Outstanding
FROM (SELECT *
FROM (SELECT DISTINCT Category, Month_Year FROM table1)
CROSS JOIN table2) t2
LEFT JOIN table1 t1
ON t2.Category = t1.Category
AND t2.Month_Year = t1.Month_Year
AND t2.Loan_Type = t1.Loan_Type
ORDER BY t2.Category, t2.Month_Year, t1.Outstanding NULLS LAST
Demo

Create a list of all items needed and left join Table1. For example
select items.Bank, items.Category, items.Month_Year, items.Loan_Type, coalesce(t1.Outstanding, 0) Outstanding
from (
select t2.Bank, t2.Loan_Type, my.Month_Year, cat.Category
from (select distinct Month_Year
from Table1) my
cross join (select distinct Category
from Table1) cat
cross join Table2 t2
) items
left join Table1 t1 on items.Bank = t1.Bank and items.Loan_Type = t1.Loan_Type and items.Month_Year = t1.Month_Year and items.Category = t1.Category;
If there exists a table Categories replace it instead of derived categories in the query. You may also wish to generate a set of Month_Year from prameters or use a calendar table.

Related

SQL get table1 names with a count of table2 and table3

I have three tables, table1 is connected to table2 and table3, but table2 and table3 are not connected. I need an output count of table2 and table3 for each table1 row. I have to use joins and a group by table1.name
SELECT Tb_Product.Name, count(TB_Offers.Prod_ID) 'Number of Offers', count(Tb_Requests.Prod_ID) 'Number of Requests'
FROM Tb_Product LEFT OUTER JOIN
Tb_Requests ON Tb_Product.Prod_ID = Tb_Requests.Prod_ID LEFT OUTER JOIN
TB_Offers ON Tb_Product.Prod_ID = TB_Offers.Prod_ID
GROUP BY Tb_Product.Name
I need to combine these queries:
SELECT Tb_Product.[Name], count(TB_Offers.Prod_ID) 'Number of Offers'
FROM Tb_Product LEFT OUTER JOIN
TB_Offers ON Tb_Product.Prod_ID = TB_Offers.Prod_ID
GROUP BY Tb_Product.[Name]
SELECT Tb_Product.[Name], count(Tb_Requests.Prod_ID) 'Number of Requests'
FROM Tb_Product LEFT OUTER JOIN
Tb_Requests ON Tb_Product.Prod_ID = Tb_Requests.Prod_ID
GROUP BY Tb_Product.[Name]
Results:
Name Number of Offers
Airplane 6
Auto 5
Bike 3
Camera 0
Computer 12
Milk 4
Oil 4
Orange 6
Telephone 0
Truck 6
TV 4
Name Number of Requests
Airplane 1
Auto 5
Bike 0
Camera 2
Computer 6
Milk 4
Oil 5
Orange 6
Telephone 0
Truck 1
TV 5
My results for offers and requests are the same value. I am not sure what I am doing wrong with the joins. Do I need to somehow join product to request and separately join product to offers? This needs to be done in one query.
This is for a class. Explanation would also be appreciated.
The simplest way to do this is to count the distinct values of each column:
SELECT
Tb_Product.Name,
count(distinct TB_Offers.Prod_ID) 'Number of Offers',
count(distinct Tb_Requests.Prod_ID) 'Number of Requests'
FROM
Tb_Product
LEFT OUTER JOIN
Tb_Requests ON Tb_Product.Prod_ID = Tb_Requests.Prod_ID
LEFT OUTER JOIN
TB_Offers ON Tb_Product.Prod_ID = TB_Offers.Prod_ID
GROUP BY
Tb_Product.Name
This is necessary because of the way joins work consecutively to produce a rowset that is a combination of all the input relations. COUNT() normally performs a count of non-null values in a column.
You can also do something like this, which aggregates the counts from the child tables independently and then joins them to the base table:
SELECT
p.Name,
o.cnt as Offer_Count,
r.cnt as Request_Count
FROM
TB_Product p
LEFT OUTER JOIN
(SELECT Prod_ID, COUNT(1) cnt FROM TB_Offers GROUP BY Prod_ID) o
LEFT OUTER JOIN
(SELECT Prod_ID, COUNT(1) cnt FROM TB_Requests GROUP BY Prod_ID) r
More explanation...
Let's say you have two products:
Prod_ID
Name
1
Widget
2
Gizmo
And two offers, one for each product:
Offer_ID
Prod_ID
100
1
200
2
And two requests for each product:
Request_ID
Prod_ID
1001
1
1002
1
2001
2
2002
2
Now you join Product relation to Offer relation on Prod_ID, you get a result like this:
Prod_ID
Name
Offer_ID
Prod_ID
1
Widget
100
1
2
Gizmo
200
2
Now when you join that relation to Requests on Prod_ID, you get something like this:
Prod_ID
Name
Offer_ID
Prod_ID
Request_ID
Prod_ID
1
Widget
100
1
1001
1
1
Widget
100
1
1002
1
2
Gizmo
200
2
2001
2
2
Gizmo
200
2
2002
2
Now when you count any of these columns you get 4 because each column has 4 values.

SQL query to full columns without defining it in Group By and optimize the Inner Join

one contain multiple columns and test two tables have one columns.
Table: Client
Name age Benefit Id code value
Tom 33 AA 0A 1 12
Tom 33 AB 0C 1 13
Tom 33 AA 0C 5 11
Sam 31 CC 0B 3 10
Rik 28 EE 0D 5 9
Table2: Sell
Code1
1
4
Table3: tip
Code2
5
6
I want output as Name,Age, Benefit, Id and Code, which code present in both table "sell" and "tip".
Name Age Benefit Id code Approved
Tom 33 AA 0A 1 7
Tom 33 AA 0C 5 11
Code I have written as
------Break code-----------
Create table #temp1 as
select c.* from Client c inner join Sell s on c.code1 = s.code
where Benifit= AA
Create table #temp2 as
select c.* from Client c inner join Sell s on c.code2 = s.code
where Benifit= AA
Create table #combine as
select ss.* ,
,case when ss.value > 5 then ss.value- 5 Else 0 approved
from #temp1 ss inner join #temp2 ff
On ss.Name= ff.Name
AND ss.Age= ff.Age
where Benifit= 'AA'
Group by ss.Name Age
------------------------------------------------
----Since 3 table are created with repeated logic, I have put Above code is put into one code, even thought it is not optimized ----------------
select ss.Name, ss.age
,case when ss.value > 5 then ss.value- 5 Else 0 approved
from
(select * from Client c inner join Sell s on c.code1 = s.code
where Benifit= AA) ss
inner join
(select * from Client cc inner join Tip t on cc.code2 = t.code
where Benifit= AA) ff
On ss.Name= ff.Name
AND ss.Age= ff.Age
Group by Name Age
So, I have two problem:
I want Benefit, Id, code and value beside Name and Age, since current query not allow to do that as 'Group By' is used
I am using two select multiple inner join as below
a. (select * from Client c inner join Sell s on c.code = s.code1
where Benifit= AA) ss
b. (select * from Client cc inner join Tip t on cc.code2 = t.code
where Benifit= AA) ff
but I don't want my code to repeatedly use the same code as above is repeated.
A new column Approved is created by subtracting 5 from Value column
Note: The output will Inner join based on "Name" and "Age" between
Inner join of Client & Sell
Inner join between Client & Tip
Please suggest.
Note I want Name, Age having code in both 'Sell' and 'Tip' where Benifit= AA Although output will be from "Sell".
I think you want a query like this -
SELECT C.Name, C.Age, C.Benefit, C.Id, C.Code, (C.Value - 5) Approved
FROM Client C
INNER JOIN Sell S ON C.Code = S.Code1
INNER JOIN Tip T ON S.Code1 = T.Code2
WHERE C.Benefit = 'AA'

Using two Count() functions without changing the results

SELECT CARRIER.CARRIERID, PLAN.PLANID, PLNDESCRIPTION,
COUNT(MEMBER.PLANID) AS MBRCNT
FROM CARRIER
LEFT OUTER JOIN PLAN
ON CARRIER.CARRIERID = PLAN.CARRIERID
LEFT OUTER JOIN MEMBER
ON PLAN.PLANID = MEMBER.PLANID
GROUP BY CARRIER.CARRIERID, PLAN.PLANID, PLNDESCRIPTION
ORDER BY CARRIER.CARRIERID;
results in
CARR PLANID PLNDESCRIPTION MBRCNT
---- ---------- ----------------------------------- ----------
ANTH 4 Single SuperMed 6
ANTH 5 2-Party SuperMed 4
ANTH 6 Family SuperMed 7
BCBS 1 Single Basic Medical 9
BCBS 2 2-Party Basic Medical 15
BCBS 3 Family Basic Medical 11
DLT 7 Single Dental Only 6
DLT 8 Family Dental Only 0
MM 10 Single SuperMed with Dental 5
MM 11 2-Party SuperMed with Dental 0
MM 12 Family SuperMed with Dental 2
NWD 9 Life Only 2
PHC 0
and
SELECT CARRIER.CARRIERID, COUNT(PLAN.CARRIERID)
FROM CARRIER
LEFT OUTER JOIN PLAN
ON CARRIER.CARRIERID = PLAN.CARRIERID
LEFT OUTER JOIN MEMBER
ON PLAN.PLANID = MEMBER.PLANID
GROUP BY CARRIER.CARRIERID
ORDER BY CARRIER.CARRIERID;
result in
CARR COUNT(PLAN.CARRIERID)
---- ---------------------------------------
ANTH 17
BCBS 35
DLT 7
MM 8
NWD 2
PHC 0
How can I combine these to get all of the rows next to each other?
I think you just want analytic functions:
SELECT c.CARRIERID, p.PLANID, p.PLNDESCRIPTION,
COUNT(m.PLANID) AS MBRCNT,
SUM(COUNT(m.PLANID)) OVER (PARTITION BY c.CARRIERID) as CNT_2
FROM CARRIER c LEFT JOIN
PLAN p
ON c.CARRIERID = p.CARRIERID LEFT JOIN
MEMBER m
ON p.PLANID = m.PLANID
GROUP BY c.CARRIERID, p.PLANID, p.PLNDESCRIPTION
ORDER BY c.CARRIERID;
One option is to full outer join them together
with firstCount as (
SELECT CARRIER.CARRIERID, PLAN.PLANID, PLNDESCRIPTION,
COUNT(MEMBER.PLANID) AS MBRCNT
FROM CARRIER
LEFT OUTER JOIN PLAN
ON CARRIER.CARRIERID = PLAN.CARRIERID
LEFT OUTER JOIN MEMBER
ON PLAN.PLANID = MEMBER.PLANID
GROUP BY CARRIER.CARRIERID, PLAN.PLANID, PLNDESCRIPTION
ORDER BY CARRIER.CARRIERID
)
secondCount as (
SELECT CARRIER.CARRIERID, COUNT(PLAN.CARRIERID) as count
FROM CARRIER
LEFT OUTER JOIN PLAN
ON CARRIER.CARRIERID = PLAN.CARRIERID
LEFT OUTER JOIN MEMBER
ON PLAN.PLANID = MEMBER.PLANID
GROUP BY CARRIER.CARRIERID
ORDER BY CARRIER.CARRIERID
)
select coalesce(firstCount.carrierId, secondCount.carrierId) as carrierId,
firstCount.PLANID,
firstCount.PLNDESCRIPTION,
firstCount.MBRCNT,
secondCount.Count
from firstCount
full outer join secondCount on firstCount.CarrierId = secondCount.CarrierId

Using the OR operator in a nested SQL Statement

I have a database with vendors, what they sell and where they are located.
I need to search for vendors that are based off of a specific locality like a state or for vendors who sell a certain number of products.
An example of this question is:
What are the full names of all vendors who can supply more than one item or are based in Illinois?
This would be easy if I could use two sql queries (but for this problem I cannot).
Assuming there is no joins between tables used, my solution is incorrect but this is what I tried
select
cs.vendor_id, name, count(cs.PRODUCT_ID)
from
grocery.vendor
where
va.state_territory_province = 'Illinois'
group by
(cs.vendor_id)
or /# error found here #/
having
(count(cs.product_id)>1);
ERROR at line 1:
ORA-00933: SQL command not properly ended
If I try each one separately, I get these results
Using Illonois
select
cs.vendor_id, v.name, count(cs.PRODUCT_ID)
from
grocery.vendor v
inner join grocery.vendor_address va
on (v.vendor_id = va.vendor_id)
inner join grocery.can_supply cs
on (v.vendor_id = cs.vendor_id)
where
va.state_territory_province = 'Illinois'
group by
(cs.vendor_id, v.name);
VENDOR_ID NAME COUNT(CS.PRODUCT_ID)
33 Drinks R Us 1
35 Jungle Man 1
34 Poland Spring 1
Using the number of products
select
cs.vendor_id, v.name, count(cs.PRODUCT_ID)
from grocery.vendor v
inner join grocery.vendor_address va
on (v.vendor_id = va.vendor_id)
inner join grocery.can_supply cs
on (v.vendor_id = cs.vendor_id)
group by (cs.vendor_id, v.name)
having(
count(cs.product_ID)>1);
VENDOR_ID NAME COUNT(CS.PRODUCT_ID)
8 Orgo Home Farm 3
17 Wellness 2
21 Wily Wonka 4
27 Camel 3
29 Supplies R Us 5
13 Clean Me Please 5
15 Oral Care Inc 2
31 Cheese Cake Factory 2
37 Crunchy 2
1 Moo Moo Milk Farm 4
4 Haagen Daz 3
26 Beer Inc 4
6 Sailor Bob 3
10 Dawn 2
16 SPAM 2
18 Wonder inc 3
5 Butcher Mat 3
9 Soda Forever 4
14 Wash Shampoo Inc 4
24 Huntz 4
20 Hershey 3
22 Bake Me Inc 5
30 We Make Pizza 2
36 Taste Treat 3
7 Monkey Paradise 6
19 Puff 5
26 rows selected.
Basically I want to merge these two queries into one. Is there a way to nest these together?
I would be inclined to do:
select v.*
from gorcery.vendor v join
grocery.vendor_address va
on v.vendor_id = va.vendor_id
where va.state_territory_province = 'Illinois' or
(select count(*)
from grocery.can_supply cs
where v.vendor_id = cs.vendor_id
) > 1;
Note: this isn't perfect, because a vendor can have multiple addresses. So, I think the better solution is:
select v.*
from gorcery.vendor v
where exists (select 1
from grocery.vendor_address va
where v.vendor_id = va.vendor_id and
va.state_territory_province = 'Illinois'
) and
(select count(*)
from grocery.can_supply cs
where v.vendor_id = cs.vendor_id
) > 1;
You should be able to put both conditions in the HAVING clause:
select v.vendor_id,
v.name,
count(cs.PRODUCT_ID)
from grocery.vendor v
join grocery.vendor_address va
on v.vendor_id = va.vendor_id
join grocery.can_supply cs
on v.vendor_id = cs.vendor_id
group by v.vendor_id, v.name, va.state_territory_province
having va.state_territory_province = 'Illinois'
or count(cs.product_ID) > 1
As pointed out in the comments, I couldn't just use va.state_territory_province in the HAVING clause (silly me), so I added it to the GROUP BY clause. I am assuming that every vendor only has one address.
how about using UNION clause for the two queries?

SQL retrieving tree structure query

I have 2 tables
Products table:
ID ProductName Category
0 T-Shirt 15
1 Aqua De Gio 12
2 Jacket 15
3 Hot Water 13
Categories table:
categoryID catagoryName highercategoryID
8 Fragnance 0
99 Clothing 0
15 Armani Clothing 99
12 Armani Fragnance 8
102 Davidoff Fragnance 8
Expected result
ID ProductName Category CategoryTree
0 T-Shirt 15 Clothing > Armani Cloting
1 Aqua De Gio 12 Fragnance > Armani Fragnance
2 Jacket 15 Clothing > Armani Cloting
3 Hot Water 13 Fragnance > Davidoff Fragnance
For example take the T-Shirt from the products table
its category is 15.
go for the categories table and see where categoryID=15
look at the highercategoryID if its = 0 stop, if not then take its value 99
look for 99 in the categoryID column, the higher category now is 0 so stop.
based on the above I need to get "Clothing > Armani Clothing".
I am new to SQL queries and here is my first try
select
x.*,
x.p1 + isnull((' > ' + x.c1), '') + isnull((' > ' + x.c2), '') as CategoryTree
from
(select
RP.categoryid as catid,
RP.catagoryName as p1,
R1.catagoryName as c1,
R2.catagoryName as c2
from
categories as RP
left outer join
categories as R1 on R1.highercategoryid = RP.categoryid
left outer join
categories as R2 on R2.highercategoryid = R1.categoryid
left outer join
categories as R3 on R3.highercategoryid = R2.categoryid
where
RP.highercategoryid != 0 ) x
I am not sure how to stop the joining when I find the 0 value in the higher category, and how to join the products on their categories, and is there a dynamic way without using a lot of joins?
Here it goes:
With CTE (CatID, CatName, HigherCatID) AS
(
SELECT categoryID, CAST(catagoryName AS VARCHAR(1000)), highercategoryID
FROM CATEGORIES
UNION ALL
SELECT C.categoryID, CAST(CTE.CatName + ' > ' + C.catagoryName AS VARCHAR(1000)), CTE.HigherCatID
FROM CATEGORIES C
INNER JOIN CTE ON C.HigherCategoryID = CTE.CatID
)
SELECT P.ID, P.ProductName, Cte.CatID, CTE.CatName
FROM CTE INNER JOIN PRODUCTS P
ON (CatID = P.Category)
WHERE CTE.HigherCatID=0
you got a SQLFiddle Here