Home > database >  How can I compare count(distinct) for each value between two different tables on MySQL?
How can I compare count(distinct) for each value between two different tables on MySQL?

Time:09-29

Okay, I haven't found a way to explain it on the title, but here it goes:

I have two tables as such - one called tb_items:

Item Name Rarity
Sword Rare
Shield Rare
Staff Uncommon
Mace Uncommon
Dagger Common
Bow Common
Axe Common

This table represents all the items available, with the name and rarity;

And another one called tb_inventories:

Item Name User Owned
Sword John
Axe John
Dagger John
Staff Adam
Mace Billy
Staff Billy
Bow Billy

This table stores every item each user owns;

What I wanna do is count per user, how many items of each rarity they own compared to the total number of items of that rarity, all in one query. I don't know if that's possible, but for now I make separate queries and then put together all results, however that wouldn't be good using a bigger database. A possible result would look something like this:

User Rarity Owned Count Total Count
John Rare 1 2
John Uncommon 0 2
John Common 2 3
Adam Rare 0 2
Adam Uncommon 1 2
Adam Common 0 3
Billy Rare 0 2
Billy Uncommon 2 2
Billy Common 1 3

I was thinking of making a separate table with the total count of each rarity and just compare with that table, but it looks like it's doable without that, I just can't figure out how...

for taking the total I was doing a simple select statement

SELECT rarity, count(rarity) from tb_items GROUP BY rarity

Then, for the inventory one I was doing this:

SELECT u.user_owned, c.rarity, COUNT(c.rarity)  FROM tb_items c
INNER JOIN tb_inventories u ON u.item_name= c.item_name
GROUP BY u.user_owned, c.rarity

Then I just treated the two results separately

CodePudding user response:

What you can do with your current attempt is make another query that acts as a reference table for your existing queries, like this:

SELECT DISTINCT rarity, user_owned
   FROM tb_items 
CROSS JOIN tb_inventories;

The results:

rarity user_owned
Common John
Uncommon John
Rare John
Common Adam
Uncommon Adam
Rare Adam
Common Billy
Uncommon Billy
Rare Billy

You can see that all user will get assigned with all rarity type regardless if the have it or not. This will be the main reference. Now you can combine all 3 queries by making them as a derived table (subquery) and use LEFT JOIN:

SELECT t1.user_owned AS 'User',
       t1.rarity AS 'Rarity',
       IFNULL(t3.ucr,0) AS 'Owned Count',
       t2.cr AS 'Total Count'
  FROM
(SELECT DISTINCT rarity, user_owned
   FROM tb_items 
CROSS JOIN tb_inventories) t1
LEFT JOIN
(SELECT rarity, count(rarity) AS cr 
  from tb_items 
  GROUP BY rarity) t2 ON t1.rarity=t2.rarity
LEFT JOIN
(SELECT u.user_owned, c.rarity, COUNT(c.rarity) AS ucr
  FROM tb_items c 
  INNER JOIN tb_inventories u ON u.item_name= c.item_name 
  GROUP BY u.user_owned, c.rarity) t3 
  ON t1.user_owned=t3.user_owned
  AND t1.rarity=t3.rarity;

Here's a demo fiddle;

An attempt using some functions only available to MySQL v8 :

WITH cte AS (
  SELECT DISTINCT rarity, user_owned
   FROM tb_items 
CROSS JOIN tb_inventories)
SELECT cte.user_owned AS 'User',
       cte.rarity AS 'Rarity',
       SUM(CASE WHEN t2.user_owned IS NOT NULL THEN 1 ELSE 0 END) AS 'Owned Count',
       t1.tr AS 'Total Count'
  FROM cte
  LEFT JOIN
(SELECT item_name, rarity, COUNT(rarity) OVER (PARTITION BY rarity) AS tr 
  FROM tb_items) t1 
ON cte.rarity=t1.rarity
LEFT JOIN tb_inventories t2
 ON t1.item_name=t2.item_name
 AND cte.user_owned=t2.user_owned
GROUP BY cte.user_owned, cte.rarity, t1.tr;
  • Related