Query with multiple IN clause on multiple rows - mysql

Say I have this table:
| ID | idService |
| 1 | 5 |
| 1 | 10 |
| 2 | 5 |
| 2 | 15 |
| 3 | 5 |
| 3 | 20 |
| 4 | 5 |
| 4 | 25 |
I'd like to be able to select all the clients(ID) where the product 5 is present with at least one of the other products (10, 15, 20, 25).
I'm able to do it for one of those combinations with the query:
SELECT ID FROM table WHERE idService IN (5,10) GROUP BY ID HAVING COUNT(DISTINCT idService) = 2
However, if I add the other conditions in this query, I will get all the clients that have at least two of the products, doesn't matter if it's 5 with 10 or 10 with 20.
SELECT ID FROM table WHERE idService IN (5,10) OR idService IN (5,15) OR idService IN (5,20) OR idService IN (5,25) GROUP BY ID HAVING COUNT(DISTINCT idService) = 2
I'm wondering if it's possible to change the IN clauses or replace them to get only the clients with one of the four valid combinations.
Edit:
I've been able to make the query work using a subquery.
SELECT ID FROM table WHERE ID IN ( SELECT ID FROM table WHERE idService =5) AND ( idService =10 OR idService =15 OR idService =20 OR idService =25 ) GROUP BY idSPOrder

select distinct ID from table t1
where (select count(*) from table where ID = t1.ID group by ID) >=2
and (select count(*) from table where Idservice = 5 and ID = t1.ID group by ID) > 0

select Id
from Table T
where exists (select 1 from Table where Id = T.Id AND idService = 5)
group by Id
having count(*) >= 2

select table.id from table,
(select id from table where idservice != 5 group by id)t
where idservice = 5 and table.id = t.id

Related

How to get n rows of records of given m ids in Mysql by id desc

Suppose i have a table like this
id | user_id | rating
1 | 500 | 5
2 | 501 | 3
3 | 500 | 5
4 | 502 | 4
5 | 502 | 1
How can i write a mysql query to find the last 10 records for each id of given three ids (500, 501,502) by id desc
assuming your id is an auto increment column and the three ids (500, 501,502) are for user_id
then you could use
select *
from my_table
where user_id in (500, 501,502)
order by id desc
limit 10
Being a bit lazy here's a way to get the last 2 per user_id by using a variable to allocate a row number joined to the max occurrences per user
DROP TABLE IF EXISTS T;
CREATE TABLE T
(ID INT AUTO_INCREMENT PRIMARY KEY, USER_ID INT, RATING INT);
INSERT INTO T (USER_ID,RATING) VALUES
(500,1),
(502,1),
(500,2),(500,3),
(502,2),
(600,1);
SELECT U.*
FROM
(
SELECT S.ID,S.USER_ID,S.RATING,
T.MAXROWS
FROM
(
SELECT ID,USER_ID,RATING,
IF(USER_ID <> #P,#R:=1,#R:=#R+1) RN,
#P:=USER_ID
FROM T
ORDER BY USER_ID ASC,ID ASC
) S
JOIN
(SELECT USER_ID,COUNT(*) MAXROWS FROM T GROUP BY USER_ID) T ON T.USER_ID = S.USER_ID
) U
WHERE U.RATING > U.MAXROWS - 2 AND U.USER_ID IN(500,502);
+----+---------+--------+---------+
| ID | USER_ID | RATING | MAXROWS |
+----+---------+--------+---------+
| 3 | 500 | 2 | 3 |
| 4 | 500 | 3 | 3 |
| 2 | 502 | 1 | 2 |
| 5 | 502 | 2 | 2 |
+----+---------+--------+---------+
4 rows in set (0.00 sec)
You can use UNION ALL operator to get 10 result of each id
(select * from test
where user_id =500
order by id desc
limit 10)
UNION ALL
(select * from test
where user_id =501
order by id desc
limit 10)
UNION ALL
(select * from test
where user_id =502
order by id desc
limit 10)
order by id desc;
Check here for DEMO
NOTE: Reviewer are welcome to enhance or improve the query.

How to delete old duplicate rows based on 2 columns but keep the latest row?

So I have this table (called test_table)
id | hotel_id | user_id
1 | 1 | 1
2 | 1 | 1
3 | 1 | 2
4 | 2 | 3
5 | 1 | 2
6 | 3 | 3
So if the hotel_id and the user_id is the same, then I want to delete the duplicate rows but keep the latest row (the latest row is the row with the higher id).
So after deleting my table would look like the table below.
I deleted id 1 because there is a newer row id 2.
I deleted id 3 because there is a newer row id 5.
id | hotel_id | user_id
2 | 1 | 1
4 | 2 | 3
5 | 1 | 2
6 | 3 | 3
I tried with the code below but it only checks if one column is a duplicate. What is the most efficient way to do this?
delete test_table
from test_table
inner join (
select max(id) as lastId, hotel_id
from test_table
group by hotel_id
having count(*) > 1) duplic on duplic.hotel_id = test_table.hotel_id
where test_table.id < duplic.lastId;
The traditional way in MySQL uses a JOIN:
delete tt
from test_table tt join
(select tt.hotel_id, tt.user_id, max(tt.id) as max_id
from test_table tt
group by tt.hotel_id, tt.user_id
) tokeep
on tokeep.hotel_id = tt.hotel_id and
tokeep.user_id = tt.user_id and
tokeep.max_id > tt.id;
If id is unique in the table, this can be simplified to:
delete tt
from test_table tt left join
(select tt.hotel_id, tt.user_id, max(tt.id) as max_id
from test_table tt
group by tt.hotel_id, tt.user_id
) tokeep
on tt.id = tokeep.max_id
where to_keep.max_id is null;
In MySQL 8.x (available since April 2018) you can use windows functions to identify the obsolete rows. For example:
delete from test_table where id in (
select id
from (
select
id, row_number() over(partition by hotel_id, user_id order by id desc) as rn
from test_table
) x
where rn <> 1
)

Mysql - Select at least one or select none

I have a table as so...
----------------------------------------
| id | name | group | number |
----------------------------------------
| 1 | joey | 1 | 2 |
| 2 | keidy | 1 | 3 |
| 3 | james | 2 | 2 |
| 4 | steven | 2 | 5 |
| 5 | jason | 3 | 2 |
| 6 | shane | 3 | 3 |
----------------------------------------
I'm running a select like so:
SELECT * FROM table WHERE number IN (2,3);
The problem im trying to solve is that I want to only grab get results from groups that have 1 or more rows of each number. For instance the above query is returning id's 1-2-3-5-6, when I'd like the results to exclude id 3 since the group of '2' can only return 1 result for the number of '2' and not for BOTH 2 and 3, since there's no row with the number 3 for the group 2 i'd like it to not even select id 3 at all.
Any help would be great.
Try it this way
SELECT *
FROM table1 t
WHERE number IN(2, 3)
AND EXISTS
(
SELECT *
FROM table1
WHERE number IN(2, 3)
AND `group` = t.`group`
GROUP BY `group`
HAVING MAX(number = 2) > 0
AND MAX(number = 3) > 0
)
or
SELECT *
FROM table1 t JOIN
(
SELECT `group`
FROM table1
WHERE number IN(2, 3)
GROUP BY `group`
HAVING MAX(number = 2) > 0
AND MAX(number = 3) > 0
) q
ON t.`group` = q.`group`;
or
SELECT *
FROM table1
WHERE `group` IN
(
SELECT `group`
FROM table1
WHERE number IN(2, 3)
GROUP BY `group`
HAVING MAX(number = 2) > 0
AND MAX(number = 3) > 0
);
Sample output (for both queries):
| ID | NAME | GROUP | NUMBER |
|----|-------|-------|--------|
| 1 | joey | 1 | 2 |
| 2 | keidy | 1 | 3 |
| 5 | jason | 3 | 2 |
| 6 | shane | 3 | 3 |
Here is SQLFiddle demo
On this, you can approach from a fun way with multiple joins for what you WANT qualified, OR, apply a prequery to get all qualified groups as others have suggested, but readability is a bit off for me..
Anyhow, here's an approach going through the table once, but with joins
select DISTINCT
T.id,
T.Name,
T.Group,
T.Number
from
YourTable T
Join YourTable T2
on T.Group = T2.Group AND T2.Group = 2
Join YourTable T3
on T.Group = T3.Group AND T3.Group = 3
where
T.Number IN ( 2, 3 )
So on the first record, it is pointing to by it's own group to the T2 group AND the T2 group is specifically a 2... Then again, but testing the group for the T3 instance and T3's group is a 3.
If it cant complete the join to either of the T2 or T3 instances, the record is done for consideration, and since indexes work great for joins like this, make sure you have one index for your NUMBER criteria, and another index on the (GROUP, NUMBER) for those comparisons and the next query sample...
If doing by more than this simple 2, but larger group, prequery qualified groups, then join to that
select
YT2.*
from
( select YT1.group
from YourTable YT1
where YT1.Number in (2, 3)
group by YT1.group
having count( DISTINCT YT1.group ) = 2 ) PreQualified
JOIN YourTable YT2
on PreQualified.group = YT2.group
AND YT2.Number in (2,3)
Maybe this,if I understand you
SELECT id FROM table WHERE `group` IN
(SELECT `group` FROM table WHERE number IN (2,3)
GROUP BY `group`
HAVING COUNT(DISTINCT number)=2)
SQL Fiddle
This will return all ids where BOTH numbers exist in a group.Remove DISTINCT if you want ids for groups where just one numbers is in.

Group by in MySQL

I have a table of the following structure:
ID | COMPANY_ID | VERSION | TEXT
---------------------------------
1 | 1 | 1 | hello
2 | 1 | 2 | world
3 | 2 | 1 | foo
is there a way to get the most recent version of records only, i.e. I would want to have as a result set the IDs 2 and 3?
I'm sure there are better ways, but I tend to use this kind of query:
SELECT *
FROM
(SELECT * FROM test ORDER BY VERSION DESC) AS my_table
GROUP BY COMPANY_ID
Produces this result set:
ID | COMPANY_ID | VERSION | TEXT
---------------------------------
2 | 1 | 2 | world
3 | 2 | 1 | foo
Try this:
SELECT *
FROM (
SELECT company_id, MAX(version) maxVersion
FROM table
GROUP BY company_id ) as val
JOIN table t ON (val.company_id = t.company_id AND t.version = val.maxversion)
If your IDs are ordered (newer version iff higher id):
SELECT t.*, a.maxversion
FROM (
SELECT MAX(id) maxid, MAX(version) maxversion
FROM table
GROUP BY company_id
) a
INNER JOIN table t
ON a.maxid = t.id
However, if your IDs are not properly ordered, you need to use the following query:
SELECT t.*
FROM (
SELECT company_id, MAX(version) maxversion
FROM table
GROUP BY company_id
) v
INNER JOIN table t
ON v.company_id = t.company_id
AND v.maxversion = t.version
(assuming there's an UNIQUE constraint/index on (company_id, version))

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.