Group by with two columns - sql

I am trying to write a query using group by in sub query ,I referred lot of blogs but could not get all the values.
I have three tables and below is the structure of those tables.
Pet_Seller_Master
ps_id ps_name city_id
2 abc 1
3 xyz 2
4 fer 4
5 bbb 1
City_Master
city_id city_name
1 Bangalore
2 COIMBATORE
4 MYSORE
Api_Entry
api_id ps_id otp
1 2 yes
2 3
3 2 yes
4 3 yes
5 4
6 5 yes
7 5 yes
8 5 yes
Query is to get number of sellers, no of pet sellers with zero otp, no of pet sellers with 1 otp, no of pet sellers with 2 otp,no of pet sellers with otp>2 for the particular city and within date range.
Through Below query I am able to get city , psp , and zero otp
select cm.city_name,
count(ps.ps_id) as PSP,
((select count(ps1.ps_id)
FROM ps_master ps1
WHERE ps1.city = cm.city_id)-
(SELECT count(distinct ps1.ps_id)
from ps_master ps1
INNER JOIN api_entry ae ON ps1.ps_id = ae.ps_id and otp!=''
WHERE ps1.city = cm.city_id and date(timestamp) >= curdate() - INTERVAL DAYOFWEEK(curdate())+6 DAY AND date(timestamp) < curdate())) as zero_psp
from ps_master ps INNER JOIN city_master cm ON ps.city = cm.city_id and cm.city_type = 'IN HOUSE PNS'
group by city_id
Please tell me the solution to solve this query.
Thanks in advance

It's not hard to do and you were on a right track. Here is what I would use:
select c.city_name, a.otp, p.ps_name, COUNT(*) nbr
from Api_Entry a
inner join Pet_Seller_Master p on p.ps_id=a.ps_id
inner join City_Master c on p.city_id=c.city_id
group by c.city_name, a.otp, p.ps_name
Now, if you want to get the number of sellers with zero otp, you just apply where clause:
where otp <> 'yes'
If you want to get the number of pet sellers with otp>2, then you just use subquery:
select *
from (
select c.city_name, a.otp, p.ps_name, COUNT(*) nbr
from #tempA a
inner join #tempP p on p.ps_id=a.ps_id
inner join #tempC c on p.city_id=c.city_id
group by c.city_name, a.otp, p.ps_name
) g
where nbr > 2

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 help i need to find the inventory remaining in my office

In sql help i have 3 tables, table one is asset table which is as follow
id
asset_code
asset_name
asset_group
asset_quantity
1
A001
demo asset
4
5
2
A002
demo asset 2
6
3
and another table is asset_allocation
id
asset_id
allocated_quantity
allocated_location
1
1
2
IT office
2
1
1
main hall
the last table is asset_liquidated which will present assets that are no longer going to be used
id
asset_id
liquidated_quantity
1
1
2
2
1
1
lets say i have 5 computers and i have allocated 3 computers and 1 is no longer going to be used so i should be remaining with 1 computer so now how do i make sql auto generate this math for me
You need to use aggregation and the join your tables -
SELECT id, asset_code, asset_name, asset_group, asset_quantity,
asset_quantity - COALESCE(AA.allocated_quantity, 0) - COALESCE(AL.liquidated_quantity, 0) available_quantity
FROM asset A
LEFT JOIN (SELECT asset_id, SUM(allocated_quantity) allocated_quantity
FROM asset_allocation
GROUP BY asset_id) AA ON A.id = AA.asset_id
LEFT JOIN (SELECT asset_id, SUM(liquidated_quantity) liquidated_quantity
FROM asset_liquidated
GROUP BY asset_id) AL ON A.id = AL.asset_id
This query will give you -1 as available_quantity for asset_id 1 as you have only 5 available, 3 of them are allotted and 3 are liquidated as per your sample data.
Please see if this helps
SELECT
asset_quantity AS Total_Assets
,ISNULL(allocated_quantity, 0) allocated_quantity
,ISNULL(liquidated_quantity, 0) liquidated_quantity
FROM asset
LEFT OUTER JOIN (
SELECT
asset_id, SUM(allocated_quantity) AS allocated_quantity
FROM asset_allocation
GROUP BY asset_id
) asset_allocation2
ON asset_allocation2.asset_id = asset.id
LEFT OUTER JOIN (
SELECT
asset_id, SUM(liquidated_quantity) AS liquidated_quantity
FROM asset_liquidated
GROUP BY asset_id
) asset_liquidated 2
ON asset_liquidated 2.asset_id = asset.id

SQL apply MAX to left join table having non null rows

table name: country
id country_name
1 USA
2 GERMANY
3 RUSSIA
table name: user
id user_name points country_id
1 user1 20 1
2 user2 10 2
3 user3 11 2
Result should be country-user with maximum points and only country if no user available(3rd record),like below
country_name user_name points
USA user1 20
GERMANY user3 11
RUSSIA (null) (null)
Currently I am using below query but it is sometime taking too much time, like when i have 100000 records.
SELECT c.country_name,u.user_name,u.points FROM country c
LEFT JOIN user u on u.country_id = c.id
WHERE (u.points = (SELECT MAX(points) AS points FROM user WHERE user.id = u.id) OR u.points IS NULL)
So, is there any other way to do it more efficiently, time-wise.
Thanks already!
You can use ROW_NUMBER():
SELECT c.country_name, u.user_name, u.points
FROM country c LEFT JOIN
(SELECT u.*,
ROW_NUMBER() OVER (PARTITION BY u.country_id ORDER BY u.points DESC) as seqnum
FROM user u
WHERE u.points IS NOT NULL
) u
ON u.country_id = c.id AND u.seqnum = 1;
Note: This returns one user per country, even if there are ties for the top one. If you want all of them, use RANK() instead of ROW_NUMBER().

SQL - Select pairs of users with equal count

I'm trying to write a query that returns pairs of users that have the same number of loans.
I have this table:
LOANS
id_loan book_barcode id_user
1 123 1
2 321 2
3 456 3
4 678 4
5 721 1
6 934 2
That's my code how to get pairs of users:
SELECT l1.id_user user_1, l2.id_user user_2
FROM loans l1
JOIN loans l2 ON l2.id_user > l2.id_user
GROUP BY l2.id_user, l2.id_user;
This is what I want from my query, but I don't know how to compare in the right way two counts, I tried but it didn't work.
id_user1 id_user2 number_loan
1 2 2
3 4 1
The most reasonable way is to aggregate the loans before joining:
with u as (
select id_user, count(*) as num_loans
from loans l
group by id_user
)
select u1.id_user, u2.id_user
from u u1 join
u u2
on u1.num_loans = u2.num_loans and u1.id_user < u2.id_user;
You could do this without pre-aggregating, but the query will be much more expensive:
select u1.id_user, u2.id_user
from loans u1 join
loans u2
on u1.id_user < u2.id_user
group by u1.id_user, u2.id_user
having count(distinct u1.id_loan) = count(distinct u2.id_loan);
I don't recommend this approach.

SQL: CUSTOMER that has bought a WORK from every ARTIST || 2 tables

So I have 2 tables that both contain IDs
work:
workID, artistID
1 6
2 7
3 8
4 6
5 6
trans:
CustomerID, workID
10 1
11 2
12 3
10 4
10 5
So far I've only managed to put all the ID's together but the result I need is to show any customerID that has purchased a workID from every artistID:
SELECT
trans.customerID, trans.workID, work.artistID
FROM
trans
INNER JOIN work
ON trans.workID=work.workID
WHERE
trans.customerID IS NOT null
ORDER BY
trans.customerID;
Basically I have no idea what my next step should be, if anyone could point me in the right direction or explain a solution that would be great.
EDIT: added simplified data to tables
This should get you going:
SELECT t.customerId
FROM
( SELECT t.CustomerId, COUNT(DISTINCT artistId) as artists
FROM trans t INNER JOIN work w ON w.workid = t.workid) t
INNER JOIN
( SELECT COUNT(DISTINCT artistId) artists FROM work) a ON t.artists = a.artists