My case looks simple but i'm messing around with this..
I have 4 tables: User, Macros, Categories, and another one that relate users with categories. One Macro have many Categories.
What i need, is a query that based on the Macro, get the users and the Categories where user is NOT IN.
Example: I have a macro named VEICULES, with categories CAR,TRUCK and Motorcycle. User José is on category CAR and User Julio on category CAR and TRUCK, so my query should return:
José | TRUCK,Motorcycle
Julio | Motorcycle
Tables:
prd_users
id | name | Email
---------------------------
1 | José | jose#email.com
2 | Júlio | julio#email.com
3 | André | andre#email.com
cat_macros
macro_id | macro_name
-----------------------
1 | Veicules |
cat_categories
category_id | category_name | macro_id
---------------------------------------
1 | Cars | 1
2 | Trucks | 1
3 | Motorcycles | 1
prd_tr_rabbit_catg
id | category_id | tasker_user_id
---------------------------------------
1 | 1 | 1
2 | 1 | 2
3 | 2 | 2
I'm stucked on just getting the categories where the user already is ..
SELECT prd_users.id, prd_users.name,
prd_users.email,cat_macros.macro_name as macro,
GROUP_CONCAT(cat_categories.category_name SEPARATOR ', ') as in_categories
FROM prd_users
INNER JOIN prd_tr_rabbit_catg ON prd_tr_rabbit_catg.tasker_user_id = prd_users.id
INNER JOIN cat_categories ON cat_categories.category_id = prd_tr_rabbit_catg.category_id
INNER JOIN cat_macros ON cat_macros.macro_id = cat_categories.macro_id
WHERE cat_macros.macro_id = '45'
GROUP BY prd_users.id;
To solve this problem it's necessary to create a list of all users joined with all categories for the given macro category. This can be done with a CROSS JOIN:
SELECT *
FROM prd_users u
CROSS JOIN (SELECT m.macro_id, m.macro_name, c.category_name, c.category_id
FROM cat_macros m
JOIN cat_categories c ON c.macro_id = m.macro_id) c
This can then be LEFT JOINed to the prd_tr_rabbit_catg table and by selecting those rows where there is no matching entry in the prd_tr_rabbit_catg table, we can find the users who don't have an entry for the given category:
SELECT c.macro_name, u.id AS user_id, u.name, u.Email, GROUP_CONCAT(c.category_name) AS missing_cats
FROM prd_users u
CROSS JOIN (SELECT m.macro_id, m.macro_name, c.category_name, c.category_id
FROM cat_macros m
JOIN cat_categories c ON c.macro_id = m.macro_id) c
LEFT JOIN prd_tr_rabbit_catg x ON x.tasker_user_id = u.id AND x.category_id = c.category_id
WHERE x.id IS NULL
AND c.macro_id = 1
GROUP BY c.macro_name, u.id
For your sample data, this gives:
macro_name user_id name Email missing_cats
Veicules 1 José jose#email.com Motorcycles,Trucks
Veicules 2 Júlio julio#email.com Motorcycles
Veicules 3 André andre#email.com Cars,Motorcycles,Trucks
Update
To exclude users who don't have any of the categories, add a HAVING clause:
HAVING COUNT(*) < (SELECT COUNT(*) FROM cat_categories WHERE macro_id = 1)
Demo on SQLFiddle
Related
I have table user_item
+----+---------+---------+
| id | user_id | item_id |
+----+---------+---------+
| 1 | 1 | 1 |
| 2 | 1 | 3 |
| 3 | 2 | 1 |
| 4 | 2 | 2 |
| 5 | 3 | 2 |
+----+---------+---------+
Is it possible to get for each user (except user_id 1) number of items that user_id 1 has and other users don't. The desired output should be:
+---------+-------+
| user_id | count |
+---------+-------+
| 2 | 1 |
| 3 | 2 |
+---------+-------+
Thanks.
Building on Gordon Linoff's answer, here's my take:
select
u.id,
count(*) - count(ui.item_id) cnt
from
users u
join user_items ui1 on ui.id=1
left join user_items ui on ui.user_id=u.id and ui.item_id=ui1.item_id
where
u.id <>1
group by
u.id
We start by taking each user except the one with id=1. Then we multiply each row by each item for user with id=1. Then to each of the resulting rows we try to join the row for the same item of the other user. Then we group them together and count. The total count(*) will always be the number of items that user with id=1 has. The count(ui.item_id) will be the count of items that both users have overlapping. And the difference is the count of items that user with id=1 has that the other user doesn't have.
Hmmmm . . . This is tricky. Let's start by getting the count that match user 1. Assuming user/item pairs are not duplicated:
select ui.user_id, count(ui1.item_id) as match_user_1
from user_items ui left join
user_items ui1
on ui1.item_id = ui.item_id and
ui1.user_id = 1
group by ui.user_id;
Now, let's subtract from the total number of items that the user has:
select ui.user_id, count(*) - count(ui1.item_id) as not_match_user_1
from user_items ui left join
user_items ui1
on ui1.item_id = ui.item_id and
ui1.user_id = 1
group by ui.user_id;
EDIT:
For the reverse, it is pretty much the same idea, but you need to subtract the matches from the total for user 1:
select ui.user_id, count(ui1.item_id) as match_user_1,
uuix.cnt - count(ui1.item_id) as not_match_user_1
from user_items ui cross join
(select count(*) as cnt
from user_items
where user_id = 1
) ui1x left join
user_items ui1
on ui1.item_id = ui.item_id and
ui1.user_id = 1
group by ui.user_id;
Creating the table and populating the table with sample data:
CREATE TABLE user_item
(
id int PRIMARY KEY,
user_id int,
item_id int
);
INSERT INTO user_item VALUES (1,1,1),(2,1,3),(3,2,1),(4,2,2),(5,3,2);
The below query displays the number of items of user_id 1 that other users don't have.
SELECT ui.user_id, (select count(item_id) - count(ui1.item_id) from user_item where user_id = 1) as count
FROM user_item UI
LEFT JOIN user_item ui1 ON ui1.item_id = ui.item_id AND ui1.user_id = 1
WHERE ui.user_id <> 1
GROUP BY ui.user_id
ORDER BY ui.user_id;
Output:
+---------+-------+
| user_id | count |
+---------+-------+
| 2 | 1 |
+---------+-------+
| 3 | 2 |
+---------+-------+
you can try this too..
select u2.user_id, (T.counter - COALESCE(SUM(u1.item_id), 0))counter
From user_item u2
LEFT OUTER JOIN (
SELECT user_id, item_id
FROM user_item
WHERE user_id=1
)u1 ON u2.item_id=u1.item_id
LEFT OUTER JOIN (
SELECT user_id, COUNT(1)counter FROM user_item where user_id=1 group by user_id
)T ON u2.user_id != T.user_id
where u2.user_id!=1
Group by u2.user_id,T.counter
The user (Ben) has joined group 2 and group 3. How can I write this in a select query... I want to select from groups I haven't joined and groups I didn't create.
users_tbl table
user_id username
| 1 | ben
| 2 | betty
| 3 | tim
| 4 | jimmy
| 5 | sammy
user_groups table
user_id group_id
| 1 | 2
| 1 | 3
group_tbl table
group_id user_id
| 1 | 5
| 2 | 4
| 3 | 5
I am able to get the list of groups I didn't create using this query...
SELECT * FROM group_tbl LEFT JOIN users_tbl ON users_tbl.user_id = group_tbl.user_id WHERE group_tbl.user_id != ? ORDER BY RAND() LIMIT 10
How can I get the list of groups users hasn't joined?
You can do it if you do a LEFT join of group_tbl to users_tbl and return the unmatched rows of group_tbl:
SELECT g.*
FROM group_tbl g LEFT JOIN user_groups u
ON u.group_id = g.group_id AND u.user_id = 1
WHERE u.user_id IS NULL
Or with NOT EXISTS:
SELECT g.*
FROM group_tbl g
WHERE NOT EXISTS (
SELECT 1
FROM user_groups u
WHERE u.group_id = g.group_id AND u.user_id = 1
)
See the demo.
Results:
group_id
user_id
1
5
I am having a problem grouping and counting items in a MYSQL database with JOIN clause
My two tables are as follows
users table
id | surname | othernames
1 | Doe | John
2 | Doe | Jane
3 | Doe | Mary
subscriptions table
id | user_id | parent_subscription_id
1 | 1 | Null
2 | 2 | 1
3 | 3 | 1
4 | 4 | 2
5 | 5 | 3
6 | 6 | 3
I need to be able to produce a list as follows
Name | Referrals
John Doe | 2
Jane Doe | 1
Mary Doe | 2
In other words,it Is the user in users table with the users.id which matches subscriptions.user_id that has the subscription with subscriptions.id which is a parent subscription to another subscription. That means, if your subscription is referenced by another subscription as its own parent_subscription_id, then that new subscription becomes your referral.
I have tried the following query and it is not giving me the expected results
SELECT users.surname, users.othernames,count('s.parent_subscription_id') as referrals
FROM users
LEFT JOIN subscriptions s ON s.user_id=users.id
group BY parent_subscription_id
I have checked some other questions on SO but I have not been able to find any that solves this type of issue
Thank you
I think that the logic you want is:
select u.surname, u.othernames, count(s.parent_subscription_id) referrals
from subscriptions s
left join subscriptions p on p.id = s.parent_subscription_id
inner join users u on u.id = coalesce(p.user_id, s.user_id)
group by u.id, u.surname, u.othernames
Demo on DB Fiddle:
surname | othernames | referrals
:------ | :--------- | --------:
Doe | John | 2
Doe | Jane | 1
Doe | Mary | 2
You ca use join between users table and select calculated count as:
SELECT
-- Get all users
users.surname,
users.othernames,
IFNULL(reff.cnt, 0) as referrals -- Preset 0 for users have not referrals in joined table
FROM users
-- Join calculation result
LEFT JOIN (
-- Calculate count by user
SELECT parent_subscription_id, COUNT(*) AS cnt
FROM subscriptions
GROUP BY subscriptions.parent_subscription_id
) reff on reff.parent_subscription_id = users.id;
Change group by fields with users.surname, users.othernames
SELECT users.surname, users.othernames,count(s.parent_subscription_id) as referrals
FROM users
LEFT JOIN subscriptions s ON s.user_id=users.id
group BY users.surname, users.othernames
you need to group by id of users table because you need get count for each user and here is your main table is users. try like this.
SELECT users.surname, users.othernames, count('s.parent_subscription_id') as referrals
FROM users
LEFT JOIN subscriptions s ON s.user_id = users.id
group BY users.id
This query eventually gave me the result I have been looking for
SELECT u.surname,u.othernames,s1.id,s1.parent_subscription_id,
s1.refcode, IFNULL(count(s2.parent_subscription_id),0) as referrals
FROM `subscriptions` s1 left join subscriptions s2
on s1.id=s2.parent_subscription_id
LEFT JOIN users u ON u.id=s1.user_id
GROUP by s1.id
Thank you all for your guidance and support on this. I deeply appreciate it
I have 4 table and join with all then i want show data like here :
pr_id |nama | jumlah_liker | id_user
1 |Milk | 5 | 1
2 |Choco| 0 | 1
Review Table produk
pr_id | nama
1 | Milk
2 | Choco
3 | Salad
Review Table liker
id_produk | id_user
1 | 1
1 | 1
1 | 1
1 | 3
1 | 2
Review Table featured_ukm
id | id_produk
1 | 1
2 | 2
But i got sql record
id_produk | nama | jumlah_liker | id_user
1 | milk | 1 | 1 //problem row i dont get count all record id_produk
2 | choco | 0 | 1
SELECT produk.*, COUNT(liker.id_produk), liker.id_user
FROM produk
left join liker ON liker.id_produk = produk.pr_id AND liker.id_user = 1
INNER JOIN featured_ukm ON featured_ukm.id_produk = produk.pr_id
GROUP BY featured_ukm.id_produk
Edit: My suggestions are first, I actually put what will produce your exact requested results at the bottom
It appears that you're trying to show how many likes each of the "Featured Product" has.
It isn't entirely clear what you're trying to do with the user id, or what its purpose in the result set is; here is a query to show how many likes each "Featured Product" has:
SELECT
produk.*,
(SELECT IFNULL(COUNT(*), 0)
FROM liker WHERE liker.id_produk = produk.pr_id)
FROM featured_ukm F
INNER JOIN produk ON produk.pr_id = F.id_produk
Here is a query to show how many likes each product by each user
SELECT DISTINCT
P.*,
(SELECT IFNULL(COUNT(*), 0) FROM liker WHERE liker.id_user = L.id_user),
IFNULL(L.id_user, 0)
FROM produk P
LEFT JOIN liker L ON L.id_produk = P.pr_id
and if you want to just see the items that ONE user liked, add:
WHERE L.id_user = 1
to the end of it.
Here is a query to show how many likes each Featured Product has for each user id:
SELECT DISTINCT
P.*,
(SELECT IFNULL(COUNT(*), 0) FROM liker WHERE liker.id_user = L.id_user),
IFNULL(L.id_user, 0)
FROM produk P
LEFT JOIN liker L ON L.id_produk = P.pr_id
INNER JOIN featured_ukm F on F.id_produk = P.pr_id
To get the exact result set that it would appear that you're looking for (in your example), you will need to reference a user table in your query. I assume you have one because you're referencing user ids. For the purpose of this example, I'm creating the below user table.
id | name
----------------
1 | user_one
2 | user_two
3 | user_three
With this table, the following query will give exactly what it appears you're looking for:
SELECT P.*, (SELECT IFNULL(COUNT(*), 0) FROM liker WHERE liker.id_produk =
F.id_produk), user.id
FROM user
CROSS JOIN featured_ukm F
LEFT JOIN produk P ON F.id_produk = P.pr_id
WHERE user.id = 1
But play around with some of my other example queries. They may be more helpful.
I have three tables:
users
user_id username
---------------------
1 | mrzander
2 | foo
3 | bar
---------------------
interests
interest_id interest
------------------------
1 | cars
2 | power tools
3 | shaving
4 | phones
5 | computers
------------------------
user_interests
id uid iid
-----------------
1 | 1 | 2
2 | 1 | 4
3 | 2 | 3
4 | 1 | 5
-----------------
Basically, I have a table of users, a table of interests, and a table that shows what users have what interests. If I know what user id I want the interests from, what query would give me all of a particular users interests?
In this example, what query would return a table called "Interests" that tells me user_id = 1 likes power tools, phones, and computers?
If you want the result on same row you should use join and group concat
select c.username, group_concat( b.interst)
from user_interest as a
left join interest as b on a.iid = b.interest_id
left join users as c. on c.user_id = a.uid
where c.user_id = 1
group by c.username
or if you need result on different rows se join only
select c.username, b.interst
from user_interest as a
left join interest as b on a.iid = b.interest_id
left join users as c. on c.user_id = a.uid
where c.user_id = 1
Simply join the two tables.
select i.*
from interests i
join user_interests u
on u.iid = i.interest_id
where i.uid = 1;