mysql select all rows meeting the first and second lowest values? - mysql

I wonder how I could select every user who met the first and second lowest values of a result table?
Table Test
NAME VALUE
John 8
Marie 8
Luis 10
Carlos 10
Leo 13
Max 14
So the result in this case would be
NAME VALUE
John 8
Marie 8
Luis 10
Carlos 10
Thanks a lot!
BTW, I did my home work searching on google and everything, didn't come up with anything but querying the table and then using PHP to filter that for me, not good for performance.
#Aziz I get the error : #1235 - This version of MySQL doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery'
Thanks
Thank you all for the load of useful answers!
Just in case falls into funny results like I did, I needed more filters and had to add to the query, here follows:
SELECT * FROM results_temp WHERE
semana='semana6' AND
total_pontos IN (
SELECT * FROM (
SELECT DISTINCT total_pontos
FROM results_temp
WHERE semana='semana6'
ORDER BY `total_pontos`
LIMIT 0,2
) AS t
)
Regards

Try this:
SELECT * FROM Test WHERE `value` IN (
SELECT * FROM (
SELECT DISTINCT `value`
FROM Test
ORDER BY `value`
LIMIT 0,2
) AS t
)

Try this:
SELECT t.*
FROM Test t
INNER JOIN (SELECT DISTINCT t.value FROM Test t ORDER BY t.value LIMIT 2
) A ON t.value = A.value;
Check this SQL FIDDLE DEMO
OUTPUT
| NAME | VALUE |
|--------|-------|
| John | 8 |
| Marie | 8 |
| Luis | 10 |
| Carlos | 10 |

SELECT *
FROM tablename
WHERE
value <= (SELECT MIN(Value)
FROM tablename
WHERE value>(SELECT MIN(VALUE)
FROM tablename))
Please see fiddle here.

The uncorrelated version of a given query is nearly always faster...
SELECT x.*
FROM tablename x
JOIN (SELECT DISTINCT value FROM tablename ORDER BY value LIMIT 2) y
ON y.value = x.value;

Related

Select all records where last n characters in column are not unique

I have bit strange requirement in mysql.
I should select all records from table where last 6 characters are not unique.
for example if I have table:
I should select row 1 and 3 since last 6 letters of this values are not unique.
Do you have any idea how to implement this?
Thank you for help.
I uses a JOIN against a subquery where I count the occurences of each unique combo of n (2 in my example) last chars
SELECT t.*
FROM t
JOIN (SELECT RIGHT(value, 2) r, COUNT(RIGHT(value, 2)) rc
FROM t
GROUP BY r) c ON c.r = RIGHT(value, 2) AND c.rc > 1
Something like that should work:
SELECT `mytable`.*
FROM (SELECT RIGHT(`value`, 6) AS `ending` FROM `mytable` GROUP BY `ending` HAVING COUNT(*) > 1) `grouped`
INNER JOIN `mytable` ON `grouped`.`ending` = RIGHT(`value`, 6)
but it is not fast. This requires a full table scan. Maybe you should rethink your problem.
EDITED: I had a wrong understanding of the question previously and I don't really want to change anything from my initial answer. But if my previous answer is not acceptable in some environment and it might mislead people, I have to correct it anyhow.
SELECT GROUP_CONCAT(id),RIGHT(VALUE,6)
FROM table1
GROUP BY RIGHT(VALUE,6) HAVING COUNT(RIGHT(VALUE,6)) > 1;
Since this question already have good answers, I made my query in a slightly different way. And I've tested with sql_mode=ONLY_FULL_GROUP_BY. ;)
This is what you need: a subquery to get the duplicated right(value,6) and the main query yo get the rows according that condition.
SELECT t.* FROM t WHERE RIGHT(`value`,6) IN (
SELECT RIGHT(`value`,6)
FROM t
GROUP BY RIGHT(`value`,6) HAVING COUNT(*) > 1);
UPDATE
This is the solution to avoid the mysql error in the case you have sql_mode=only_full_group_by
SELECT t.* FROM t WHERE RIGHT(`value`,6) IN (
SELECT DISTINCT right_value FROM (
SELECT RIGHT(`value`,6) AS right_value,
COUNT(*) AS TOT
FROM t
GROUP BY RIGHT(`value`,6) HAVING COUNT(*) > 1) t2
)
Fiddle here
Might be a fast code, as there is no counting involved.
Live test: https://www.db-fiddle.com/f/dBdH9tZd4W6Eac1TCRXZ8U/0
select *
from tbl outr
where not exists
(
select 1 / 0 -- just a proof that this is not evaluated. won't cause division by zero
from tbl inr
where
inr.id <> outr.id
and right(inr.value, 6) = right(outr.value, 6)
)
Output:
| id | value |
| --- | --------------- |
| 2 | aaaaaaaaaaaaaa |
| 4 | aaaaaaaaaaaaaaB |
| 5 | Hello |
The logic is to test other rows that is not equal to the same id of the outer row. If those other rows has same right 6 characters as the outer row, then don't show that outer row.
UPDATE
I misunderstood the OP's intent. It's the reversed. Anyway, just reverse the logic. Use EXISTS instead of NOT EXISTS
Live test: https://www.db-fiddle.com/f/dBdH9tZd4W6Eac1TCRXZ8U/3
select *
from tbl outr
where exists
(
select 1 / 0 -- just a proof that this is not evaluated. won't cause division by zero
from tbl inr
where
inr.id <> outr.id
and right(inr.value, 6) = right(outr.value, 6)
)
Output:
| id | value |
| --- | ----------- |
| 1 | abcdePuzzle |
| 3 | abcPuzzle |
UPDATE
Tested the query. The performance of my answer (correlated EXISTS approach) is not optimal. Just keeping my answer, so others will know what approach to avoid :)
GhostGambler's answer is faster than correlated EXISTS approach. For 5 million rows, his answer takes 2.762 seconds only:
explain analyze
SELECT
tbl.*
FROM
(
SELECT
RIGHT(value, 6) AS ending
FROM
tbl
GROUP BY
ending
HAVING
COUNT(*) > 1
) grouped
JOIN tbl ON grouped.ending = RIGHT(value, 6)
My answer (correlated EXISTS) takes 4.08 seconds:
explain analyze
select *
from tbl outr
where exists
(
select 1 / 0 -- just a proof that this is not evaluated. won't cause division by zero
from tbl inr
where
inr.id <> outr.id
and right(inr.value, 6) = right(outr.value, 6)
)
Straightforward query is the fastest, no join, just plain IN query. 2.722 seconds. It has practically the same performance as JOIN approach since they have the same execution plan. This is kiks73's answer. I just don't know why he made his second answer unnecessarily complicated.
So it's just a matter of taste, or choosing which code is more readable select from in vs select from join
explain analyze
SELECT *
FROM tbl
where right(value, 6) in
(
SELECT
RIGHT(value, 6) AS ending
FROM
tbl
GROUP BY
ending
HAVING
COUNT(*) > 1
)
Result:
Test data used:
CREATE TABLE tbl (
id INTEGER primary key,
value VARCHAR(20)
);
INSERT INTO tbl
(id, value)
VALUES
('1', 'abcdePuzzle'),
('2', 'aaaaaaaaaaaaaa'),
('3', 'abcPuzzle'),
('4', 'aaaaaaaaaaaaaaB'),
('5', 'Hello');
insert into tbl(id, value)
select x.y, 'Puzzle'
from generate_series(6, 5000000) as x(y);
create index ix_tbl__right on tbl(right(value, 6));
Performances without the index, and with index on tbl(right(value, 6)):
JOIN approach:
Without index: 3.805 seconds
With index: 2.762 seconds
IN approach:
Without index: 3.719 seconds
With index: 2.722 seconds
Just a bit neater code (if using MySQL 8.0). Can't guarantee the performance though
Live test: https://www.db-fiddle.com/f/dBdH9tZd4W6Eac1TCRXZ8U/1
select x.*
from
(
select
*,
count(*) over(partition by right(value, 6)) as unique_count
from tbl
) as x
where x.unique_count = 1
Output:
| id | value | unique_count |
| --- | --------------- | ------------ |
| 2 | aaaaaaaaaaaaaa | 1 |
| 4 | aaaaaaaaaaaaaaB | 1 |
| 5 | Hello | 1 |
UPDATE
I misunderstood OP's intent. It's the reversed. Just change the count:
select x.*
from
(
select
*,
count(*) over(partition by right(value, 6)) as unique_count
from tbl
) as x
where x.unique_count > 1
Output:
| id | value | unique_count |
| --- | ----------- | ------------ |
| 1 | abcdePuzzle | 2 |
| 3 | abcPuzzle | 2 |

Group by names desc - get last entered values for a grouped name

I have an table like that:
id | name | v (lvl)
11 | Jane | 6
12 | John | 5
13 | Jane | 6
14 | John | 5
15 | Jane | 7
16 | Jane | 5
In my autocomplete form now id like to group the names but get the last value (value with biggest id). In the example above would be
Jane | 5
I tried with combinations like distinct, group by, order by. But im always get
Jane | 6
or grouped like this and reversed:
Jane | 6
Jane | 7
Jane | 5
I would need something like this:
SELECT name,lvl FROM
(
SELECT DISTINCT name, lvl FROM pora WHERE name LIKE 'Jane' ORDER BY lvl DESC
)
GROUP BY name
EDIT: I won't get the highest lvl, i want get the lvl of the highest id, grouped by name. Thats all. My example above would be the best explanation what i like to get.
In the inner query i change the order to DESC for all and in the outer i group it by names. But i get an error for this.
EDIT 2 I finally did at my own. The correct solution (i was already close):
SELECT a.name, a.lvl FROM
(
SELECT DISTINCT name, lvl FROM pora WHERE name LIKE 'Jane' ORDER BY id DESC
)as a
GROUP BY name
LIKE without % is just =
SELECT *
FROM yourTable
WHERE name = 'Jane'
ORDER BY id DESC
LIMIT 1
But because you mention autocomplete functionality you should use:
WHERE name LIKE 'Jane%'
To have the latest, you need to have a field dateAdded which stores the date you ran the insert command.
Following which, you use MAX(dateAdded) to get the latest ID (since, as you mentioned, it may decrease as well)
UPDATE:
if ID doesn't decrease, you can always use MAX(ID)
SELECT MAX(id), v from tablename where name = 'Jane'
UPDATE:
This has been tested:
SELECT ID, v from tableName where ID = (SELECT MAX(ID) as ID from tableName where name like '%Jane%')
Try the following query (h/t #lamak)
WITH CTE AS
(
SELECT *,
RN = ROW_NUMBER() OVER(PARTITION BY name
ORDER BY [id] DESC)
FROM poro
)
SELECT *
FROM CTE
WHERE RN = 1

SELECT random but every name should be selected at least once

I have a problem in my MySQL query: I have more that 100000 records,
All I want is to select randomly 10 entries where I can see all devices at least once,
Devices names are differents from user to other.
The table has many devices name, the name is in another table where I joined to that table with the device ID
Every user has less than 10 devices so basically I can see all of them in LIMIT 10 but the random function doesn't pick some of them sometimes
I use :
SELECT DISTINCT name,signal
FROM my_table
Where name='any_name'
ORDER BY RAND()LIMIt 10
This selects 10 entries but sometimes some devices are not selected
Example :
+------------+------------+
| Device | Signal |
+------------+------------+
| Router1 | -60 |
| Router2 | -56 |
| Router3 | -102 |
| Router4 | -125 |
| ....... | .... |
+------------+------------+
Try using this code:
SELECT column_name FROM table_name ORDER BY RAND() LIMIT 10
But make sure you have put your column_name and table_name as your intended.
In additional, try to use this link beside:
http://jan.kneschke.de/projects/mysql/order-by-rand/
Good luck.
You can try this (untested). The logic is to select those device records that their signal is the maximum one for that device.
SELECT aa.name, aa.signal
FROM (
SELECT name, MAX(signal) AS max_signal
FROM my_table
GROUP BY name
) AS _aa
INNER JOIN my_table AS aa
ON aa.name = _aa.name
WHERE aa.signal = _aa.max_signal
ORDER BY RAND()
LIMIT 10;
Nasty way of doing it with a sub query on the SELECT:-
SELECT b.Device, (SELECT Signal FROM my_table a WHERE a.Device = b.Device ORDER BY RAND() LIMIT 1)
FROM
(
SELECT DISTINCT Device
FROM my_table
ORDER BY RAND()
LIMIT 10
) b

selecting multiple max values

i have a table like this on a mysql database:
id | item
-----------
1 | 2
2 | 2
3 | 4
4 | 5
5 | 8
6 | 8
7 | 8
i want the result to be 3 record with the highest Item value
select max(item) returns only 1 value
how can i select multiple max values?
thank you
You can use a derived table to get the maximum value and join it back to the original table to see all rows corresponding to it.
select t.id, t.item
from tablename t
join (select max(item) as mxitem from tablename) x
on x.mxitem = t.item
Edit:
select t.co_travelers_id, t.booking_id, t.accounts_id
from a_co_travelers t
join (select accounts_id, max(booking_id) as mxitem
from a_co_travelers
group by accounts_id) x
on x.mxitem = t.booking_id and t.accounts_id = x.accounts_id
If you use an 'aggregate function' without GROUP BY only one row will be returned.
You may use GROUP BY , with aggregate functions.
Here is SQLFiddle Demo
SELECT id,max(item) AS item
FROM table_name
GROUP BY id
ORDER BY item DESC
LIMIT 3
Hope this helps.
There is the graphical explanation.
There is script mysql (low abstraction level, no inner join or sth)
select * from ocena, uczen where ocena.ocena = (SELECT MAX(ocena.ocena) FROM ocena WHERE ocena.przedmiot_id="4" and ocena.uczen_id="1") and ocena.uczen_id=uczen.id and ocena.przedmiot_id="4" and uczen_id="1"

specific mysql select query

today i need your help to get an specific sql select query.
i have following table:
and after a specific query regarding a specific id (in this case id 1) i wanna have a result like this:
user_id (an alias for the id_sender/id_recipient), date (maybe a max function, cause i wanna have the latest date to group), messages (a count function to the messages):
10 | 2012-01-14 09:10:05 | 4
11 | 2012-01-13 13:52:49 | 1
13 | 2012-01-13 20:01:17 | 1
14 | 2012-01-14 09:20:17 | 1
i tryed a lot but dont get the exact results - so my approach was something like this:
SELECT `id_recipient`, `id_sender`, MAX(`date`) AS `date`, COUNT(*) AS `messages` FROM `table` WHERE `id_recipient` = 1 OR `id_sender` = 1 GROUP BY `id_recipient`, `id_sender`
but then i get this result:
its not so bad but as u can see the 4th line should be included in the results of the first one.
i hope u got me. feel free to ask if smth is not clear.
thanks in advance,
greetings
Ok, so since we know the value for id_recipient, we can use some math to trick SQL into getting this nasty query done.
Let n be the id value of the person of interest.
We know that the pairing of id_recipient and id_sender will ALWAYS include the user with id value n. Based on the where clause.
Therefore, id_recipient + id_sender == n + id_otherPerson is true.
The resulting query will be very similar to this.
(It's been a while, but I don't think I have any syntax problems)
SELECT (`id_recipient` + `id_sender` - n) AS `id_otherPerson`,
MAX(`date`) AS `date`, COUNT(*) AS `messages`
FROM `table`
WHERE `id_recipient` = n XOR `id_sender` = n
GROUP BY `id_otherPerson`;
Edit: I've changed it to an XOR, so if person n messages person n, it won't cause all values to be incremented by the number of times n has messaged themself.
What about this?
SELECT user_id, MAX(date), COUNT(*)
FROM (
SELECT id_recipient AS 'user_id', date
FROM table
WHERE id_recipient <> 1 AND id_sender = 1
UNION
SELECT id_sender AS 'user_id', date
FROM table
WHERE id_recipient = 1 AND id_sender <> 1
) AS tbl
GROUP BY user_id
It assumes you want to use id_recipient if the id_sender is 1 and id_sender if id_recipient is 1.
I believe the output you want should be as below
10 | 2012-01-13 20:01:17 | 3
11 | 2012-01-13 13:52:49 | 1
13 | 2012-01-13 20:01:17 | 1
I'm saying as you are mixing id_recipient and id_sender