mysql-select a random row from each id - mysql

I have a table in my db which has 2 columns: id and color. each id may have multiple rows with different values for color. so for example:
id color
--------------
1 black
1 white
1 green
2 yellow
3 red
3 black
I want to select only one row for each id, but randomly. I have already tried to use two select queries, but it always returns the first row of each id. what is the problem?!
SELECT * FROM (SELECT * FROM collections ORDER BY RAND()) AS a
GROUP BY id

You can try:
select t.*
from t
where t.color = (select t2.color
from t t2
where t2.id = t.id
order by rand()
limit 1
);
For performance, you can try an index on (id, color).
Your code should simply not work. It uses select * with group by -- meaning that you have unaggregated columns. That should be a compile-time error.
EDIT:
LOL. Of course, the above has an issue. The subquery gets called for each row, giving each row an opportunity to be in the result set. Sigh. Sometimes code doesn't do what I want it to do. One solution is to seed the random number generator. This is "arbitrary" but not "random" -- you'll get the same values on each run:
select t.*
from t
where t.color = (select t2.color
from t t2
where t2.id = t.id
order by rand(concat(t2.id, t2.color))
limit 1
);
If you don't have too many colors, you can use a group_concat() trick:
select t.id,
substring_index(group_concat(color order by rand()), ',', 1)
from tA quick and dirty solution is to seed the random number generator:
group by id;

Related

Query using RAND but following a defined structure

I have the following problem and I would like to know how to solve it creating a query on MySQL.
Check out those two tables:
I need to follow a structure to show the results I want. It is like a randon inside a randon. For example, I want to randomize each section, but ids 1 to 3 must be together and then randomize too.
How would be a query to get such results?
With this query:
select id, id_start, id_end, rand() rnd
from table2
group by id, id_start, id_end
you can return a random number for each of the rows of table2.
Join this query to table1 and sort the result first by that random number and then by random:
select t1.id, t1.description
from table1 t1
inner join (
select id, id_start, id_end, rand() rnd
from table2
group by id, id_start, id_end
) t2
on t1.id between t2.id_start and t2.id_end
order by t2.rnd, rand()
See the demo.

SQL Group by attribute and show results if there is more than one in that group

I have a table, testing, with the attributes: id, fruits.
We have the following contents in said table:
id, fruits
1, Apple
2, Banana
3, Apple
I would like a query that groups these by the fruits (Apples in one group, Bananas in another) and returns if there is more than 1 in that group.
So, for the example above, the query should return:
1, Apple
3, Apple
Here's what I have so far:
SELECT *
FROM testing
GROUP BY 'fruits'
HAVING COUNT(*) > 1
ORDER BY 'id'
This query only returns one of the apples.
Thanks for any help!
Toby.
Actually, the most efficient way to do this is probably to use exists:
select t.*
from testing t
where exists (select 1
from testing t2
where t2.fruits = t.fruits and t2.id <> t.id
);
For optimal performance, you want an index on testing(fruits, id).
You can use a subquery to find the duplicates, and an outer query that gets your rows;
SELECT * FROM testing
WHERE fruits IN (
SELECT fruits FROM testing
GROUP BY fruits HAVING COUNT(*)>1
)
ORDER BY id
An SQLfiddle to test with.
You have to join back to the table in order to get the desired result:
SELECT t1.*
FROM testing AS t1
JOIN (
SELECT fruits
FROM testing
GROUP BY fruits
HAVING COUNT(*) > 1
) AS t2 ON t1.fruits = t2.fruits
ORDER BY t1.id

Using MYSQL GROUP_CONCAT with sub query

I am trying to get my head around using GROUP_CONCAT within MYSQL.
Basically I have the following table, table1:
id, field1, field2, active
I want to bring back 5 rows within the table but in random order. So I'm using this:
SELECT GROUP_CONCAT(id ORDER BY rand()) FROM table1 WHERE active=1
This behaves as I would expect. I then want to use the output to select the other columns (field1, field2) from the table and display the results.
So I've tried using:
SELECT *
FROM table1
WHERE id IN
(
SELECT GROUP_CONCAT(id ORDER BY rand()) as id FROM table1 WHERE active=1
);
I expected something like the above to work but I cant figure out why it doesn't. It DOES bring back results but not all of them, (i.e.) my table contains 10 rows. 6 rows are set to active=1. Therefore I would expect 6 rows to be returned ... this isn't happening I may get 1,2 or 0.
Additionally if it helps I'd like to limit the number of results returned by the sub-query to 3 but adding LIMIT doesn't seem to have any affect on the results returned.
Thank you in advance for your help
I think this is what you are looking for. This will bring back 5 random active rows.
SELECT *
FROM table1
WHERE active=1
ORDER BY RAND()
LIMIT 5;
why not use this :
SELECT *, GROUP_CONCAT(id ORDER BY rand()) as randoms FROM table1 WHERE active=1
If I understand correctly, you are trying to build a query like this:
select *
from table1
where id in (1,2,3,4,5) -- Just an example
and you are trying to "fill" the in condition with a group_concat() result.
That's not the way to do it.
You only need to specify the subquery in the parenthesis:
select *
from table1
where id in (select id from table1 where active=1)
Notice some additional things:
The order by rand() is irrelevant, because the in () will be evaluated regardless of the order of the values.
In this particular scenario, I would recommend to use a join instead of in.
Using join:
select t1.*
from
table1 as t1
inner join table1 as t2 on t1.id = t2.id
where t2.active=1

MySql: generate random rows as a subquery

I've got a problem with a correlation inside a sub-query.
I have 2 tables :
- table1 : contains "groups" with a groupid, a groupename and a categoryid
- table2 : tells which people is member of which group (with fields: userid, groupid)
I would like to ask my database to give me :
all groups from a specific "category" with for each of them :
- the groupid, the groupname
- and a random selection of 4 members for each group
I followed the question MySQL select 10 random rows from 600K rows fast to generate 4 random members of a specific group.
It works well if I run the query separately.
but if I try to incorporate my sub-query inside my "general query" :
SELECT
g.groupid, g.groupname,
(
SELECT GROUP_CONCAT(table2.userid SEPARATOR ",")
FROM table2, (
SELECT userid AS uid
FROM table2
WHERE table2.groupid = g.groupid
ORDER BY RAND( )
LIMIT 4
) tmp
WHERE table2.userid = tmp.uid
) AS randomusers
FROM table1 AS g WHERE g.categoryid = ?
... I get a "Unknown column 'g.groupid' in 'where clause'" ERROR.
I tried to pass the subquery into a LEFT JOIN but I can't figure out how to do it properly as each of my attempts are unsuccessful.
Any help on this? Thanks :)
If I understood right, your query looks overly complicated
SELECT t1.groupid,
t1.groupname,
(SELECT GROUP_CONCAT(table2.userid) FROM table2 WHERE table2.userid = t1.userid ORDER BY RAND() LIMIT 4) AS `users`
FROM table1 AS t1
Have not tested it, but this query should return first four random elements for each group

mysql optimization: select a non previously selected random pair of different values from a column of unique values

What would be the most efficient way to select a non previously selected pair of different random values from a column of unique (non repeated) values?
My current approach is (keeping every pair of values already associated in a separate "mytable_associations" table):
SELECT * FROM
(
SELECT id,count(*) AS associations_count FROM mytable
INNER JOIN mytable_associations
WHERE (myvalue=myvalue1 OR myvalue=myvalue2)
GROUP BY myvalue
HAVING associations_count<(SELECT count(*) FROM mytable)-1
ORDER BY rand() limit 1
) mytable1
LEFT JOIN
(SELECT myvalue AS myvalue2 FROM mytable) mytable2
ON mytable1.myvalue1<>mytable2.myvalue2
WHERE
(
SELECT myvalue1 FROM mytable_associations
WHERE
myvalue1=mytable1.myvalue1 AND myvalue2=mytable2.myvalue2
OR
myvalue1=mytable2.myvalue2 AND myvalue2=mytable1.myvalue1
) IS NULL;
(And then of course update mytable_associations with this new association)
Which, as you can see, could hugely benefit from some optimization.
(Sorry about the poor indentation in the code, I really don't know how to indent mysql commands).
Can you help me?
(P.S. This is my first question ever posted here: Sure I'm doing lots of things wrong and I'd understand the consequent flaming, but please don't be too hard on me ;) )
Any solution involving order by rand() is going to be inefficient. For alternatives, see:
My answer to Selecting random rows with MySQL
Jan Kneschke on Order by RAND()
To exclude numbers you've already picked, here's how I'd do it (this is pseudocode):
$c1 = SELECT COUNT(DISTINCT myvalue) FROM mytable
$c2 = SELECT COUNT(*) FROM mytable_associations
$offset = ROUND( RAND() * ($c1 * ($c1-1) - $c2) )
SELECT v.* FROM (
SELECT LEAST(m1.myvalue,my2.myvalue) AS myvalue1,
GREATEST(m1.myvalue,my2.myvalue) AS myvalue2
FROM (SELECT DISTINCT myvalue FROM mytable) AS m1
INNER JOIN (SELECT DISTINCT myvalue FROM mytable) AS m2
ON m1.myvalue <> m2.myvalue
) AS v
LEFT OUTER JOIN mytable_associations AS a USING (myvalue1,myvalue2)
WHERE a.myvalue1 IS NULL
LIMIT 1 OFFSET $offset
By ensuring that myvalue1 < myvalue2, and storing them in that order in mytable_associations, you can simplify the join.