select a random rows from table - mysql

I have two tables one called topic as shown below!
topic_id topic_name
1 topic 1
2 topic 2
3 topic 3
and another table called questions as shown
q_id question_name topic_id
1 question 1 1
2 question 2 1
3 question 3 1
4 question 4 2
5 question 5 2
6 question 6 2
7 question 7 3
8 question 8 3
9 question 9 3
i want to choose random 2 question from given three topic. Someone please help me to fix this issue

Get list of topics with their question IDs GROUP_CONCAT([column] order by RAND()).
And then link table to itself.
SELECT t.q_id, t.question_name, t.topic_id
FROM table t
JOIN (
SELECT topic_id, SUBSTRING_INDEX(GROUP_CONCAT(q_id ORDER BY RAND()), ',', 2) as qList
FROM table GROUP BY topic_id
) tGrouped ON FIND_IN_SET(t.q_id, tGrouped.qList)>0

One can sort the rows randomly and then fetch the top row from this random order
For two random questions which could have same topic:
SELECT * FROM questions
ORDER BY RAND()
LIMIT 2
For two random questions which should have different topic:
Use 2 different queries which take as parameters two different topic_ids (t1, t2):
First select 2 random topic ids (similarly to above code):
SELECT topic_id FROM topics
ORDER BY RAND()
LIMIT 2
Then select 2 random questions with these topics ids (2 select statements)
SELECT * FROM questions
WHERE topic_id = t1
ORDER BY RAND()
LIMIT 1
SELECT * FROM questions
WHERE topic_id = t2
ORDER BY RAND()
LIMIT 1
UPDATE (after OP's comment and explanation)
To obtain two random questions from every topic use a variation of the above solutions:
3 select statements (one for each topic):
SELECT * FROM questions
WHERE topic_id = needed_topic_id_here
ORDER BY RAND()
LIMIT 2
repeat the select for every topic_id.
Presumably these select statements could be combined into one big select statement, but i'm not sure at this point.
Note as pointed out in another answer, this could be less efficient (to randomly select in pure sql) and a better solution would be to pre-compute random indices in PHP (or whatever your platform is) and then actually select the random questions. Since no language is mentioned in the question, i'll leave it here (and point to the other answer(s) for this approach)

You can use ORDER BY RAND() and LIMIT 2 in the query but it runs painfully slow for tables that have thousand of records or more.
A better approach for big tables is to get the bounding values of the PK field using the WHERE condition you need, generate 2 random numbers smaller between these bounding values in PHP then issue 2 MySQL queries to get 2 questions.
Something along these lines:
$query = '
SELECT MIN(q_id) AS min_id, MAX(q_id) AS max_id
FROM questions
WHERE topic_id = 1 # put the filtering you need here
';
// Run the query
// ... use your regular PHP code for database access here ...
// get and store the returned values in PHP variables $minId and $maxId
// Keep the generated random values here to avoid duplicates
$list = array();
// Get N random questions from the database
for ($cnt = 0; $cnt < N; $cnt ++) {
// Generate a new ID that is not in the list
do {
$id = rand($minId, $maxId);
} while (in_array($id, $list);
// Put it into the list to avoid generating it again
$list[] = $id;
// Get the random question
$query = "
SELECT *
FROM questions
WHERE topic_id = 1
AND q_id <= $id
ORDER BY q_id DESC
LIMIT 1
";
// Run the query, get the question
// ... use your regular PHP code for database access here ...
}
No matter what queries you run (these or others provided by other answer), you need indexes on q_id and the columns used in the WHERE clause.
I hope q_id is the PK of the table which means it already is an UNIQUE INDEX.

Provide 3 topic ids to get 2 questions at random:
select
q.question_name
from
topics t,
questions q
where
t.topic_id = q.topic_id and
t.topic_id in (1, 2, 3) /*define your 3 given topics*/
order by rand() limit 0,2;

Related

Complicated joining on multiple id's

I have a table like this
id | user_id | code | type | time
-----------------------------------
2 2 fdsa r 1358300000
3 2 barf r 1358311000
4 2 yack r 1358311220
5 3 surf r 1358311000
6 3 yooo r 1358300000
7 4 poot r 1358311220
I want to get the concatenated 'code' column for user 2 and user 3 for each matching time.
I want to receive a result set like this:
code | time
-------------------------------
fdsayooo 1358300000
barfsurf 1358311000
Please note that there is no yackpoot code because the query was not looking for user 4.
You can use GROUP_CONCAT function. Try this:
SELECT GROUP_CONCAT(code SEPARATOR '') code, time
FROM tbl
WHERE user_id in (2, 3)
GROUP BY time
HAVING COUNT(time) = 2;
SQL FIDDLE DEMO
What you are looking for is GROUP_CONCAT, but you are missing a lot of details in your question to provide a good example. This should get you started:
SELECT GROUP_CONCAT(code), time
FROM myTable
WHERE user_id in (2, 3)
GROUP BY time;
Missing details are:
Is there an order required? Not sure how ordering would be done useing grouping, would need to test if critical
Need other fields? If so you will likely end up needing to do a sub-select or secondary query.
Do you only want results with multiple times?
Do you really want no separator between values in the results column (specify the delimiter with SEPARATOR '' in the GROUP_CONCAT
Notes:
You can add more fields to the GROUP BY if you want to do it by something else (like user_id and time).

Sum Of mysql table record

I have a table named tbl_Question and a column named INT_MARK which has different marks for different questions. Like this:
VH_QUESTION INT_MARK
----------- --------
Q1 2
Q2 4
My question is: How to get a random set of 20 questions whose total sum of marks is 50?
select VH_QUESTION, sum(INT_MARK) from tbl_Question
group by VH_QUESTION
having sum(INT_MARK) > 50
order by rand() limit 1
I think this question may help you - seems a very similar problem.
If that don't work, I'd try to divide the problem in two: first, you make a combinatory of your questions. Then, you filter them by it's sum of points.
I couldn't find, however, how to produce all combinations of the table. I don't know how difficult that would be.
select VH_QUESTION, sum(INT_MARK) from tbl_Question
group by VH_QUESTION
having sum(INT_MARK) >= 50
order by rand() limit 20
Quick answer
SELECT * ,SUM(INT_MARK) as total_mark FROM tbl_Question
GROUP BY VH_QUESTION
HAVING total_mark="50"
ORDER BY RAND()
LIMIT 5
it returns 0 line when no answers are possible but each time it finds one the questionsare random.
You could check the benchmark to see if you can have a faster query for large tables.

mysql: get top answer

I have a table called answers with 2 rows:
questionid - answer
as you can probably guess questionid stores the question id number and answer stores the answer, the answers are just numbers 1 to 8
I want to display the top answer.
eg:
questionid - answer
4 - 7
4 - 3
4 - 3
2 - 3
6 - 7
7 - 1
9 - 8
1 - 5
top answer = 3
i've tried:
SELECT answer FROM answers WHERE questionid='$qid' ORDER BY answer DESC LIMIT 1
and
SELECT DISTINCT answer FROM answers WHERE questionid='$qid' ORDER BY answer DESC LIMIT 1
$qid = page id ie: /question.php?qid=4
but both return incorrect.
Update:
Is there away of showing the result without using:
while($row = mysql_fetch_array($result)) {
// stuff here
}
as I just want to show 1 result (ie the top answer) based on $qid
There are multiple ways to do this (HAVING(), WHERE, MAX(), etc) The commonality is GROUP BY.
SELECT answer, COUNT(answer) FROM questions GROUP BY answer ORDER BY COUNT(answer) DESC LIMIT 1
This will return:
3 - 3
Read more about GROUP BY.
UPDATE
Seems you modified your question after I posted this answer.
If you want to limit the query to a specific question, add a WHERE clause as the other questions.
... FROM questions WHERE questionid = X GROUP BY ...
Note: Be mindful of SQL Injection.
When you only have 1 record, you can forego the while loop and just access the top result.
$result = mysql_query($sql);
$row = mysql_fetch_array($result);
Note: You'll like want to do more error checking (e.g. ensure a result was returned). Also, don't use mysql_* functions as they are now deprecated. Use MySQLi or PDO instead.
SELECT count(*) AS answer_count, answer FROM answers WHERE questionid='$qid' GROUP BY answer ORDER BY answer_count DESC LIMIT 1

Mysql Covert rows to columns

I have a table with order numbers, first name, last name, question and answers. There are 5 questions asked to the user, each answer to a question generates 1 row of data, which produces 5 rows per user. I need a query that returns order number, first name, last name and the questions and answers converted to columns, returning 1 row per user.
Any help would be appreciated
Thanks,
Larry
Seems like you want to join the table to itself 5 times.
Something like
select q1.first_name, q1.last_name, max(q1.question), max(q1.answer), max(q2.question), max(q2.answer),max(q3.question), max(q3.answer),...
from questions q1
join questions q2 on q1.first_name=q2.first_name and q1.last_name=q2.last_name
join questions q3 on q1.first_name=q3.first_name and q1.last_name=q3.last_name
where q1.order_number = 1 and q2.order_number = 2 and q3.order_number = 3 ...
group by q1.first_name, q1.last_name
Using max will collapse down the rows into unique first name/last name pairs.

Returning query results in predefined order

Is it possible to do a SELECT statement with a predetermined order, ie. selecting IDs 7,2,5,9 and 8 and returning them in that order, based on nothing more than the ID field?
Both these statements return them in the same order:
SELECT id FROM table WHERE id in (7,2,5,9,8)
SELECT id FROM table WHERE id in (8,2,5,9,7)
I didn't think this was possible, but found a blog entry here that seems to do the type of thing you're after:
SELECT id FROM table WHERE id in (7,2,5,9,8)
ORDER BY FIND_IN_SET(id,"7,2,5,9,8");
will give different results to
SELECT id FROM table WHERE id in (7,2,5,9,8)
ORDER BY FIND_IN_SET(id,"8,2,5,9,7");
FIND_IN_SET returns the position of id in the second argument given to it, so for the first case above, id of 7 is at position 1 in the set, 2 at 2 and so on - mysql internally works out something like
id | FIND_IN_SET
---|-----------
7 | 1
2 | 2
5 | 3
then orders by the results of FIND_IN_SET.
Your best bet is:
ORDER BY FIELD(ID,7,2,4,5,8)
...but it's still ugly.
Could you include a case expression that maps your IDs 7,2,5,... to the ordinals 1,2,3,... and then order by that expression?
All ordering is done by the ORDER BY keywords, you can only however sort ascending and descending. If you are using a language such as PHP you can then sort them accordingly using some code but I do not believe it is possible with MySQL alone.
This works in Oracle. Can you do something similar in MySql?
SELECT ID_FIELD
FROM SOME_TABLE
WHERE ID_FIELD IN(11,10,14,12,13)
ORDER BY
CASE WHEN ID_FIELD = 11 THEN 0
WHEN ID_FIELD = 10 THEN 1
WHEN ID_FIELD = 14 THEN 2
WHEN ID_FIELD = 12 THEN 3
WHEN ID_FIELD = 13 THEN 4
END
You may need to create a temp table with an autonumber field and insert into it in the desired order. Then sort on the new autonumber field.
Erm, not really. Closest you can get is probably:
SELECT * FROM table WHERE id IN (3, 2, 1, 4) ORDER BY id=4, id=1, id=2, id=3
But you probably don't want that :)
It's hard to give you any more specific advice without more information about what's in the tables.
It's hacky (and probably slow), but you can get the effect with UNION ALL:
SELECT id FROM table WHERE id = 7
UNION ALL SELECT id FROM table WHERE id = 2
UNION ALL SELECT id FROM table WHERE id = 5
UNION ALL SELECT id FROM table WHERE id = 9
UNION ALL SELECT id FROM table WHERE id = 8;
Edit: Other people mentioned the find_in_set function which is documented here.
You get answers fast around here, don't you…
The reason I'm asking this is that it's the only way I can think of to avoid sorting a complex multidimensional array. I'm not saying it would be difficult to sort, but if there were a simpler way to do it with straight sql, then why not.
One Oracle solution is:
SELECT id FROM table WHERE id in (7,2,5,9,8)
ORDER BY DECODE(id,7,1,2,2,5,3,9,4,8,5,6);
This assigns an order number to each ID. Works OK for a small set of values.
Best I can think of is adding a second Column orderColumn:
7 1
2 2
5 3
9 4
8 5
And then just do a ORDER BY orderColumn