How to find exact row match using IN in Where clause? - mysql

I have two tables tbl_group AND tbl_members.
Here is snapshot of both the tables.
+----+------------+
| id | groupTitle |
+----+------------+
| 1 | Group 1 |
| 2 | Group 2 |
+----+------------+
+----+---------+--------+
| id | groupId | userId |
+----+---------+--------+
| 1 | 1 | 1 |
| 2 | 1 | 2 |
| 3 | 1 | 3 |
| 4 | 2 | 1 |
| 5 | 2 | 2 |
+----+---------+--------+
Now i want to create one more group with userId 3,2 but before that i want to check that is there any group which has same member 3 and 2 not any other.
i have used IN clause but it does not work.
SELECT DISTINCT groupId
FROM tbl_members
WHERE userId IN (3,2)
It is returning both the group though result would be empty.

We can try using INSERT INTO ... SELECT with an EXISTS clause which asserts that userId of 2/3 does not already occur for any group.
INSERT INTO tbl_members (groupId, userId)
SELECT groupId, userId
FROM (
SELECT 3 AS groupId, 2 AS userId UNION ALL SELECT 3, 3
) t
WHERE NOT EXISTS (SELECT 1 FROM tbl_members
WHERE userId IN (2, 3)
GROUP BY groupId
HAVING MIN(userId) <> MAX(userId));
You would also have to insert a new record into the tbl_group table for the new group.

SELECT DISTINCT groupId FROM tbl_members WHERE userId = '2'
INTERSECT
SELECT DISTINCT groupId FROM tbl_members WHERE userId = '3'

One way is to use conditional aggregation based filtering using GROUP BY with HAVING clause. We will use a query to get the groupId value which has userId values of 3 and 2 only. If the query does not return any such value; this implies that no such Group exists; hence you can create the group.
SELECT
groupId
FROM tbl_members
GROUP BY groupId
HAVING SUM(userId = 2) AND /* userID 2 exists */
SUM(userId = 3) AND /* userID 3 exists */
NOT SUM(userId NOT IN (2,3)) /* No other userId exists (other than 2,3) */

This returns groupId's where only 2 AND 3 are in the group.
Count of all userId's in the group - sum of 1 for records where userId = 2 or 3 has to be 0 if there are no other userid's in the group.
Sure this will work only if 2 and 3 don't repeat itself in the same group.
SELECT groupId
FROM tbl_members
GROUP BY groupId
HAVING COUNT(userId)-SUM(userId IN (2,3)) = 0

First inner query will decide all fetched groups have only two members that will reduce set of unnecessary records then second inner query take that group id and check whether there is no other member then 3,2 in that group, and finally whole query will give group id which has only 3,2 as member.
SELECT groupId
FROM tbl_members m
WHERE m.userId IN (3,2) AND
(SELECT COUNT(memberId) FROM tbl_members mmm WHERE mmm.groupId=m.groupId)=2 AND
(SELECT COUNT(memberId) FROM tbl_members mm WHERE mm.groupId=m.groupId AND
m.userId NOT IN (3,2))<=0
GROUP BY groupId

I think this query will give the group of values in userid (2,3), groupid (2,3)
from the table
SELECT count(1), userid ,groupid FROM tbl_members
WHERE groupid IN (3,2) and userid in (3,2)
group by userid, groupid;

Related

how to get count items that not exists for user with SQL

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

UNION records from two tables and favor fields that are not NULL (otherwise favor values from first table)

I have fought my way through various answers and did some progress, however, the final solution has not been discovered.
The DB situation:
Table "clients_a":
userid | name
1 | Steve
2 | John
3 | Paul
Table "clients_b":
userid | name
1 | NULL
3 | Jokename
4 | Jessy
Desired result/output:
userid | name
1 | Steve
2 | John
3 | Paul
4 | Jessy
Description of what is going on:
userid is unique in the result (merged)
a result for name that is not NULL is favored
if two entries, then result from table clients_a is favored
all entries have a groupid (see below), that has to be taken into account
MySql queries I tried (and came close):
Attempt 1: This query works, but it does not regard the name. It takes all names from client_a:
SELECT * FROM
(
SELECT userid, name
FROM `client_a`
WHERE groupid = 123
UNION DISTINCT
SELECT userid, name
FROM `client_b`
WHERE groupid = 123
) AS res
GROUP BY res.userid
Attempt 2: This query creates duplicate entries (one userid can occur twice), but regards the name, as it seems:
SELECT o.*, i.* FROM
(
SELECT userid, name
FROM `client_a`
WHERE groupid = 123
UNION DISTINCT
SELECT userid, realname
FROM `client_b`
WHERE groupid = 123
GROUP BY userid
) AS o
LEFT JOIN `client_a` as i on i.userid = o.userid
I also tried to use MIN(name) without success.
Any help is appreciated.
You can do it with NOT EXISTS:
SELECT a.userid, a.name
FROM clients_a a
WHERE a.groupid = 123
AND (name IS NOT NULL OR NOT EXISTS (SELECT 1 FROM clients_b b WHERE b.userid = a.userid))
UNION
SELECT b.userid, b.name
FROM clients_b b
WHERE b.groupid = 123
AND NOT EXISTS (SELECT 1 FROM clients_a a WHERE a.userid = b.userid AND a.name IS NOT NULL)
See the demo.
Results:
userid
name
1
Steve
2
John
3
Paul
4
Jessy

Mysql Query to find the last transaction date with the purchased item from Purchase_history table

Table: purchase_history having all details of users
Fields are : id,uid, purchase_date, item_id, item_size, item_color
where id is a primary key.
There are many rows for an similar uid. e.g.
id | uid | purchase_date | item_id | item_size | item_color
1 | 200 | 2016-10-22 | 1021 | 3 | red
2 | 122 | 2016-08-02 | 21 | 1 | black
3 | 200 | 2016-05-01 | 222 | 1 | blue
4 | 101 | 2016-01-07 | 102 | 1 | red
So now I want a single query to get the last transaction date, item_id and uid group by uid. I used below query:
select uid, max(purchase_date), item_id from purchase_history group by uid;
it gives me correct uid and purchase date but the item id is not picked from the last row. It is coming from the first row. Is there any way that we can find the item id from the last row with uid and purchase_date?
Try this:
select uid, max(purchase_date) as date, item_id from purchase_history group by uid ORDER by date desc,item_id desc
Make sure that you item_id type is an integer.
You can find max of purchase date for each user in a subquery and join it with the main table like so:
select t1.uid, t1.purchase_date, t1.item_id
from purchase_history t1
inner join (
select uid, max(purchase_date) purchase_date
from purchase_history
group by uid
) t2 on t1.uid = t2.uid
and t1.purchase_date = t2.purchase_date;
NOTE: It'll give multiple rows for a uid, if there are rows with multiple max dates.
Use correlated subquery:
SELECT uid, purchase_date, item_id
FROM purchase_history p1
WHERE purchase_date = (
SELECT MAX(purchase_date)
FROM purchase_history p2
WHERE p2.uid = p1.uid
);
try this query
select * from (select * from purchase_history order by purchase_date asc) purchase_history group by uid;

MySQL, understanding SQL query behavior

I have a simple MySQL table:
| id | sid | date |
+--------+---------+------------+
| 1 | 1 | 2013-12-01 |
+--------+---------+------------+
| 3 | 2 | 2013-12-17 |
+--------+---------+------------+
| 4 | 1 | 2013-12-17 |
+--------+---------+------------+
| 5 | 1 | 2013-12-18 |
+--------+---------+------------+
I need group this table by sid field and get records with max id for each sid with correct date. I try below code:
SELECT MAX(id), date FROM my_table GROUP BY sid
But the date field is incorrect, for example I get date 2013-12-01 with id 5 as a result.
What am I doing wrong ?
The way standard SQL is defined (at least up to ansi 1992, others will correct me), any field of your SELECT clause must be included in your group by condition. [Mysql allows you to not do so, but that is why it is confusing: the results are not as you expect]
Your query should then be:
SELECT MAX(id), date FROM my_table GROUP BY sid, date
But in this case, clearly this is not what you want. Your requirement is to get the date corresponding to the MAX(id) for each sId.
You have to isolate each part of the algorithm in different queries.
1 - get the max id for each sid:
SELECT MAX(id) AS id, sid FROM my_table GROUP BY sid
2 - get the date corresponding to the max of ids for each sid:
SELECT date FROM my_table WHERE sid = X AND id = Y
3 - join these 2 queries using an INNER JOIN or more shortly, a JOIN:
SELECT m.sid, m.id, m.date
FROM my_table m
JOIN (SELECT MAX(id) AS id, sid FROM my_table GROUP BY sid) t
ON t.sid = m.sid AND m.id = t.id
You need a join:
SELECT a.id, a.date
FROM foo a
INNER JOIN (SELECT MAX(id) as max_id FROM foo GROUP BY sid) b ON a.id = b.max_id

SELECT all the newest records distinct

i have table structure like this
sn | person_id | image_name |
1 | 1 | abc1.jpb
2 | 1 | aa11.jpg
3 | 11 | dsv.jpg
4 | 11 | dssd.jpg
5 | 11 | sdf.jpg
I need distinct person_id newest row as following
2 | 1 | aa11.jjpb
5 | 11 | sdf.jpg
IT is possible ?
SELECT * FROM yourtable GROUP BY person_id ORDER BY sn DESC
Essentially you want to select all records from your table. Then it is grouped by the person_id (limiting the result to 1 per person id)... Ordering by SN decending means that it will return the most recent (highest) sn
Update: (and verified)
SELECT * FROM (SELECT * FROM stackoverflow ORDER BY sn DESC) a GROUP BY person_id ORDER BY sn
SELECT * FROM table GROUP BY person_id HAVING MAX(sn)
EDIT
SELECT f.*
FROM (
SELECT person_id, MAX(sn) as maxval
FROM table GROUP BY person_id
) AS x INNER JOIN table AS f
ON f.person_id = x.person_id AND f.sn = x.maxval;
where table is your table name.
SELECT * FROM table a WHERE a.`id` = ( SELECT MAX(`id`) FROM table b WHERE b.`person_id` = a.`person_id` );
What you are doing inside the parenthesis is selecting the max id for the rows that have that distinct person_id. So for each unique person_id you are getting the most recent entry.