SQL - Select where A is equal and B is variant - mysql

I have table like this
mapname id time
---------------------
cvjourney 1 65.24
cvjourney 3 69.45
cvjourney 2 64.25
cvjourney 8 75.45
dark_hop 2 65.87
dark_hop 1 61.51
coldrunss 1 12.50
I want to select data from that table to get something like this (for id 1, 2)
cvjourney 1 65.24
cvjourney 2 64.25
dark_hop 2 65.87
dark_hop 1 61.51
so I want select data where two players are both having records on same map, I dont want select maps where player[1] has record and player[2] doesnt, or the other way.
Here is my select, but I cant get rid of maps where one player has record and another doesnt.
SELECT *
FROM table
WHERE mapname IN (
SELECT mapname
FROM table
WHERE id IN ('1','2')
)
AND id IN ('1', '2')
ORDER BY mapname
Can you help me, please?

You can use aggregation to get only those mapnames which have both the ids present (which means after applying the id filter, distinct count of ids will be two) and then use that to get the relevant rows.
select t1.*
from table t1
join (
select mapname
from table
where id in (1, 2)
group by mapname
having count(distinct id) = 2
) t2 on t1.mapname = t2.mapname
where t1.id in (1, 2)
order by t1.mapname
Use can use IN too:
select t1.*
from table t1
where mapname in (
select mapname
from table
where id in (1, 2)
group by mapname
having count(distinct id) = 2
)
and id in (1, 2)
order by mapname
Demo

Related

How join statements execute in sql

I'm trying to fetch the data from user table such that every row contains date value(not null). If value is null then it should be view that column with a date of id of above date which have same id.
Without updating the table rows, only with select statement?
Here is the table
NAME, DATE, ID
A, 2021-01-21, 1
B, null, 1
C, null, 1
D, 2021-01-18, 2
D, null, 2
It should be viewed like
A, 2021-01-21, 1
B, 2021-01-21, 1
C, 2021-01-21, 1
D, 2021-01-18, 2
D, 2021-01-18, 2
Now the query I think is =>
select t1.name, t2.date ,t1.id from user t1
left join (select id ,date from user where id=1) t2
on t1.id=t2.id;
But this query doesn't work like I thought.
Can anyone please tell me how above join query works ? And how can I improve it ? So that I got the required result.
For testing of above query use this queries =>
create table user(
name varchar(20),
date date,
id integer
);
insert into user values("A",'2021-01-21',1);
insert into user values("",null,1);
insert into user values("",null,1);
insert into user values("",null,1);
insert into user values("",null,1);
insert into user values("",null,1);
insert into user values("B",'2021-01-20',2);
select t1.name, t2.date ,t1.id from user t1
left join (select id ,date from user where id=1) t2
on t1.id=t2.id;
The first problem is that you are joining a table with itself on the condition t1.id = t2.id. So if you have 4 rows with id=1 and 3 rows with id=2 just as an example, you will end up with a result that had 4 * 4 + 3 * 3 = 25 rows. In your specific case you will end up with 6 * 6 + 1 * 1 = 37 rows.
The second problem is that you have hard-code selecting id=1 in your subquery:
(select id ,date from user where id=1) t2
This can't be the appropriate value for all possible rows.
You could try the obvious:
select
t1.name,
ifnull(t1.date, (select t2.date from user t2 where t2.date is not null and t2.id = t1.id limit 1)) as date,
t1.id
from user t1
;
see db-fiddle
name
id
date
A
1
2021-01-21
1
2021-01-21
1
2021-01-21
1
2021-01-21
1
2021-01-21
1
2021-01-21
B
2
2021-01-20
But better would be to use a join:
select u.name, ifnull(u.date, sq.date) as date, u.id
from user u join (
select id, min(date) as date from user group by id
) sq on u.id = sq.id
;
see db-fiddle
I would expect the second version using a join to be more efficient because the first version has a dependent subquery that has to get executed for every row that has a null date.
You don't need a join. Just use a window function:
select name,
max(date) over (partition by id) as date,
id
from users;
Note that your sample data doesn't match the data in the question. That data suggests:
select max(name) over (partition by id) as name,
max(date) over (partition by id) as date,
id
from user;
Here is a db<>fiddle.

MySql: How to select rows where all values are the same?

I have a table like this:
name |id | state
name1 12 4
name1 12 4
name2 33 3
name2 33 4
...
I want to select every name and id from table where state is only 4, that means name1 is correct, because it only has two records with state 4 and nothing more. Meanwhile name2 is wrong, because it has record with state 4 and record with state 3.
You can use aggregation as shown below:
SELECT name, id
FROM your_table
GROUP BY name, id
HAVING SUM(state<>4)=0;
See a Demo on SQL Fiddle.
select name, id from mytable where id not in
(select distinct id from mytable where state <> 4)
you might need 2 sub queries .
select with group by name were state 4
select with group by name
compare the count if the count is same then select it
example : select name , count (name) from table where state = 4 as T1
select name , count (name) from table as T2
select T1.name from T1 and T2 where T2.count = T1.count
You can use not exists like this:
select distinct name, id
from table1 a
where not exists (select *
from table1 b
where a.id=b.id and state<>4)
In a more general case you can use count distinct (with not exists or with a join):
select distinct name, id
from table1 a
where not exists (
select *
from table1 b
where a.id=b.id
group by id
HAVING count(distinct state)>1)

MYSQL - Group By / Order By not working

I have the following data inside a table:
id person_id item_id price
1 1 1 10
2 1 1 20
3 1 3 50
Now what I want to do is group by the item ID, select the id that has the highest value and take the price.
E.g. the sum would be: (20 + 50) and ignore the 10.
I am using the following:
SELECT SUM(`price`)
FROM
(SELECT id, person_id, item_id, price
FROM `table` tbl
INNER JOIN person p USING (person_id)
WHERE p.person_id = 1
ORDER BY id DESC) x
GROUP BY item_id
However, this query is still adding (10 + 20 + 50), which is obviously not what I need to have.
Any ideas to where I am going wrong?
Here is what you are trying to achieve. First you need grouping in a subquery and not in outer query. In outer query you need only sum:
SELECT SUM(`price`)
FROM
(SELECT MAX(price) as price
FROM `table` tbl
INNER JOIN person p USING (person_id)
WHERE p.person_id = 1
GROUP BY item_id) x
http://sqlfiddle.com/#!9/40803/5
SELECT SUM(t1.price)
FROM tbl t1
LEFT JOIN tbl t2
ON t1.person_id= t2.person_id
AND t1.item_id = t2.item_id
AND t1.id<t2.id
WHERE t1.person_id = 1
AND t2.id IS NULL;
I'm not sure if this is the only requirement you have. If so, try this.
SELECT SUM(price)
FROM
(SELECT MAX(price)
FROM table
WHERE person_id = 1
GROUP BY item_id)
First of all - you don't need the person table, because the other table already contains the person_id. So i removed it from the examples.
Your query returns a sum of prices for each item.
If you replace SELECT SUM(price) with SELECT item_id, SUM(price) you wil get
item_id SUM(`price`)
1 30
3 50
But that is not what you want. Neither is it what you wrote in the question " (10 + 20 + 50)".
Now replacing the first line with SELECT id, item_id, SUM(price) you will get one row for each item with the highest id.
id item_id price
2 1 20
3 3 50
This works because of the "undocumented feature" of MySQL, wich allows you to select columns that are not listed in the GROUP BY clause and get the first row from the subselect each group (each item in this case).
Now you only need to sum the price column in an additional outer select
SELECT SUM(price)
FROM (
SELECT id, item_id ,price
FROM (
SELECT id, person_id, item_id, price
FROM `table` tbl
WHERE tbl.person_id = 1
ORDER BY id DESC ) x
GROUP BY item_id
) y
However i do not recomend to use that "feature". While it still works on MySQL 5.6, you never know if that will work with newer versions. It already doesn't work on MariaDB.
Instead you can determite the MAX(id) for each item in an subselect, select only the rows with the determined ids and get the summed price of them.
SELECT SUM(`price`)
FROM `table` tbl
WHERE tbl.id IN (
SELECT MAX(tbl2.id)
FROM `table` tbl2
WHERE tbl2.person_id = 1
GROUP BY tbl2.item_id
)
Another solution (wich internaly does the same) is
SELECT SUM(`price`)
FROM `table` tbl
JOIN (
SELECT MAX(tbl2.id) as id
FROM `table` tbl2
WHERE tbl2.person_id = 1
GROUP BY tbl2.item_id
) x ON x.id = tbl.id
Alex's solution also works fine, if the groups (number of rows per person and item) are rather small.
You have used group by in main query, but it is on subquery like
SELECT id, person_id, item_id, SUM(`price`) FROM ( SELECT MAX(price) FROM `table` tbl WHERE p.person_id = 1 GROUP BY item_id ) AS x

Get lowest shared value in a one to many relationship table

I have a one to many relationship table.
I want to get the lowest other_id that is shared between multiple id's.
id other_id
5 5
5 6
5 7
6 6
6 7
7 7
I can do this by building an SQL statement dynamically with parts added for each additional id I want to query on. For example:
select * from (
select other_id from SomeTable where id = 5
) as a
inner join (
select other_id from SomeTable where id = 6
) as b
inner join (
select other_id from SomeTable where id = 7
) as c
on a.other_id = b.other_id
and a.other_id = c.other_id
Is there a better way to do this? More specifically, is there a way to do this that doesn't require a variable number of joins? I feel like this problem probably already has a name and better solutions.
My query gives me the number 7, which is what I want.
i dont have a mysql server to test it out at the moment, but try a "group by" statement:
select
other_id
from
SomeTable
group by
other_id having count(*) > 1
order by
other_id asc
limit 1
the lowest other_id shared between all ids
select other_id
from SomeTable
group by other_id
having count(distinct id) = 3
order by other_id limit 1
or dynamically
select other_id
from SomeTable
group by other_id
having count(distinct id) = (select count(distinct id) from SomeTable)
order by other_id limit 1
or if you want to look for the lowest other_id shared between specific ids
select other_id
from SomeTable
where id in (5,6,7)
group by other_id
having count(distinct id) = 3
order by other_id limit 1
Use MIN for simple query
SELECT MIN(other_id) FROM table
Though, there seems to be working solutions I want to add my version, just to show, how smart I am ;-)
SELECT other_id FROM SomeTable a INNER JOIN SomeTable b on a.other_id=b.other_id ORDER by other_id ASC LIMIT 1

MySQL select rows until fixed number of condition is reached

I have this table
id fruit
---------
1 apple
2 banana <--
3 apple
4 apple
5 apple
6 apple
7 banana <----
8 apple
9 banana
10 apple
And I want to select rows until 2 bananas are found, like
SELECT id FROM table_fruit UNTIL number_of_bananas = 2
So the result would be 1,2,3,4,5,6,7
How could I achieve this?
thanks
I wish I could give credits to all of you who answered my question. I'v tested all of them, and they all work perfectly (got the expected result).
Though answers of Devart and ypercube seem a little bit complex and difficult for me to understand.
And since AnandPhadke was the first one provided a working solution, I'll choose his answer as accepted.
You guys are awesome, thanks!
Try this query -
SELECT id, fruit FROM (
SELECT
b.*, #b:=IF(b.fruit = 'banana', 1, 0) + #b AS banana_number
FROM
bananas b,
(SELECT #b := 0) t
ORDER BY id) t2
WHERE
banana_number < 2 OR banana_number = 2 AND fruit = 'banana'
SQLFiddle demo
select * from tables where id <=
(
select id from (
select id from tables where fruit='banana'
order by id limit 2) a order by id desc limit 1
)
SQLFIDDLE DEMO
#Devart's answer is perfect but it's an alternative option to we can use:
SELECT * FROM table_fruit WHERE id <=
(
SELECT id FROM
(SELECT id FROM table_fruit WHERE fruit='banana' ORDER BY id LIMIT 2) a
ORDER BY ID DESC LIMIT 1
);
Or using MAX
SELECT * FROM table_fruit WHERE id <=
(
SELECT MAX(id) FROM
(SELECT id FROM table_fruit WHERE fruit='banana' ORDER BY id LIMIT 2) a
);
See this SQLFiddle
select * from table_fruit where id <=
(
select max(id) from
(select id from table_fruit where fruit='banana' order by id limit 2) t
)
If there are less than 2 rows with 'banana', this will return all rows of the table:
SELECT t.*
FROM table_fruit AS t
JOIN
( SELECT MAX(id) AS id
FROM
( SELECT id
FROM table_fruit
WHERE fruit = 'banana'
ORDER BY id
LIMIT 1 OFFSET 1
) AS lim2
) AS lim
ON t.id <= lim.id
OR lim.id IS NULL ;