My userid is guid,it is not int.At first,I user ORDER BY RAND() and it works.But someone say that it is not efficient.So I try to change it to other ways.But it can not work.How to fix it?
-- work fine but not efficient
select userid from user order by rand() LIMIT 1
-- do not work,always return the same row
SELECT * FROM user WHERE userid >= ((SELECT MAX(userid) FROM user)
-(SELECT MIN(userid) FROM user)) * RAND()
+ (SELECT MIN(userid) FROM _user) LIMIT 1
-- do not work,always return the same row
SELECT userid FROM user AS t1 JOIN (SELECT RAND()
* (SELECT MAX(userid) FROM user) AS id) AS t2
WHERE t1.userid >= t2.id
ORDER BY t1.userid ASC LIMIT 1;
You are treating a guid as an integer. That just isn't going to work. The problem is not rand(), it is mistreatment of types.
One method that can make the query more efficient is to do something like this:
select userid
from user
where rand() < 0.01
order by rand()
limit 1;
This takes about 1% of your table and uses that for sorting. You can actually formalize this to something like:
select userid
from user cross join (select count(*) as cnt from user) params
where rand() < 100 / cnt
order by rand()
limit 1;
This will select about 100 rows from the table and sort those. Sorting 100 rows is not particularly intensive, so that should be reasonable performance-wise. And, with an expected value of 100 rows, the query should basically never fail to get at least one row.
Related
I am having trouble writing a query for the following problem. I have tried some existing queries but cannot get the results I need.
I have a results table like this:
userid score timestamp
1 50 5000
1 100 5000
1 400 5000
1 500 5000
2 100 5000
3 1000 4000
The expected output of the query is like this:
userid score
3 1000
1 1000
2 100
I want to select a top list where I have n best scores summed for each user and if there is a draw the user with the lowest timestamp is highest. I really tried to look at all old posts but could not find one that helped me.
Here is what I have tried:
SELECT sum(score) FROM (
SELECT score
FROM results
WHERE userid=1 ORDER BY score DESC LIMIT 3
) as subquery
This gives me the results for one user, but I would like to have one query that fetches all in order.
This is a pretty typical greatest-n-per-group problem. When I see those, I usually use a correlated subquery like this:
SELECT *
FROM myTable m
WHERE(
SELECT COUNT(*)
FROM myTable mT
WHERE mT.userId = m.userId AND mT.score >= m.score) <= 3;
This is not the whole solution, as it only gives you the top three scores for each user in its own row. To get the total, you can use SUM() wrapped around that subquery like this:
SELECT userId, SUM(score) AS totalScore
FROM(
SELECT userId, score
FROM myTable m
WHERE(
SELECT COUNT(*)
FROM myTable mT
WHERE mT.userId = m.userId AND mT.score >= m.score) <= 3) tmp
GROUP BY userId;
Here is an SQL Fiddle example.
EDIT
Regarding the ordering (which I forgot the first time through), you can just order by totalScore in descending order, and then by MIN(timestamp) in ascending order so that users with the lowest timestamp appears first in the list. Here is the updated query:
SELECT userId, SUM(score) AS totalScore
FROM(
SELECT userId, score, timeCol
FROM myTable m
WHERE(
SELECT COUNT(*)
FROM myTable mT
WHERE mT.userId = m.userId AND mT.score >= m.score) <= 3) tmp
GROUP BY userId
ORDER BY totalScore DESC, MIN(timeCol) ASC;
and here is an updated Fiddle link.
EDIT 2
As JPW pointed out in the comments, this query will not work if the user has the same score for multiple questions. To settle this, you can add an additional condition inside the subquery to order the users three rows by timestamp as well, like this:
SELECT userId, SUM(score) AS totalScore
FROM(
SELECT userId, score, timeCol
FROM myTable m
WHERE(
SELECT COUNT(*)
FROM myTable mT
WHERE mT.userId = m.userId AND mT.score >= m.score
AND mT.timeCol <= m.timeCol) <= 3) tmp
GROUP BY userId
ORDER BY totalScore DESC, MIN(timeCol) ASC;
I am still working on a solution to find out how to handle the scenario where the userid, score, and timestamp are all the same. In that case, you will have to find another tiebreaker. Perhaps you have a primary key column, and you can choose to take a higher/lower primary key?
Query for selecting top three scores from table.
SELECT score FROM result
GROUP BY id
ORDER BY score DESC
LIMIT 3;
Can you please try this?
SELECT score FROM result GROUP BY id ORDER BY score DESC, timestamp ASC LIMIT 3;
if 2 users have same score then it will set order depends on time.
You can use a subquery
SELECT r.userid,
( SELECT sum(r2.score)
FROM results r2
WHERE r2.userid = r.userid
ORDER BY score DESC
LIMIT 3
) as sub
FROM result r
GROUP BY r.userid
ORDER BY sub desc
You should do it like this
SELECT SUM(score) as total, min(timestamp) as first, userid FROM scores
GROUP BY userid
ORDER BY total DESC, first ASC
This is way more efficient than sub queries. If you want to extract more fields than userid, then you need to add them to the group by.
This will of cause not limit the number of scores pr user, which indeed seems to require a subquery to solve.
I need to sample 100 records for each user (it's a column in the table).
Currently, I have this code:
SELECT * FROM sn.sn_graph_reduced
where user = '643495915'
ORDER BY RAND() LIMIT 100
Obvously, I need to remove the where clause and add something that will sample 100 records for each user in the table.
Any help appreciated
thanks
You can use a rank query mysql does not have window functions for this type of results to get n records per group
SELECT * FROM (
SELECT g.*,
#r:= CASE WHEN #g = g.`user` THEN #r +1 ELSE 1 END rownum,
#g:= g.`user` user_group
FROM sn.sn_graph_reduced g
CROSS JOIN (SELECT #g:=0,#r:=0) t2
ORDER BY `user` , RAND()
) t
WHERE rownum <= 100
Above query will give ranks to the record which belongs to same user group and in parent query you can limit records to 100 for each user
I have this query that does not work and I do not understand why.
Each SELECT statements should return descending results, but they're ordered ascendingly.
Why ?
(SELECT * FROM table WHERE deleted_at = 0 ORDER BY id DESC)
UNION ALL
(SELECT * FROM table WHERE deleted_at <> 0 ORDER BY id DESC)
LIMIT 0,30
I have to say, this query does not generates any error and the results are what I expect.
They are just not well ordered.
There is no guarantee of ordering when using subqueries. If you want the results ordered by id descending, then use:
(SELECT * FROM table WHERE deleted_at = 0)
UNION ALL
(SELECT * FROM table WHERE deleted_at <> 0)
order by id desc
LIMIT 0,30;
However, I think the query you really want is:
select *
from table
order by deleted_at = 0 desc, id desc
limit 0, 30;
This puts the deleted_at = 0 rows first and then fills out the data with the rest of the data.
Note: if deleted_at can be NULL and you want to filter them out too, then add a where clause for this filtering.
from the manual:
To apply ORDER BY or LIMIT to an individual SELECT, place the clause inside the parentheses that enclose the SELECT:
(SELECT a FROM t1 WHERE a=10 AND B=1 ORDER BY a LIMIT 10)
UNION
(SELECT a FROM t2 WHERE a=11 AND B=2 ORDER BY a LIMIT 10);
However, use of ORDER BY for individual SELECT statements implies nothing about the order in which the rows appear in the final result because UNION by default produces an unordered set of rows. Therefore, the use of ORDER BY in this context is typically in conjunction with LIMIT, so that it is used to determine the subset of the selected rows to retrieve for the SELECT, even though it does not necessarily affect the order of those rows in the final UNION result. If ORDER BY appears without LIMIT in a SELECT, it is optimized away because it will have no effect anyway.
If you need it sorted in total:
SELECT * FROM table WHERE deleted_at = 0
UNION ALL
SELECT * FROM table WHERE deleted_at <> 0
ORDER BY deleted_at = 0 DESC, id DESC
LIMIT 0,30
But this is of source the same as:
SELECT * FROM table
ORDER BY deleted_at = 0 DESC, id DESC
LIMIT 0,30
Because you apply the ORDER BY statement before the UNION ALL happens so on just one part of your data, you want to apply it on the whole result and it should be something like this :
SELECT * FROM table WHERE deleted_at = 0
UNION ALL
SELECT * FROM table WHERE deleted_at <> 0
ORDER BY id DESC
LIMIT 0,30
I want to select last 50 rows from MySQL database within column named id which is primary key. Goal is that the rows should be sorted by id in ASC order, that’s why this query isn’t working
SELECT
*
FROM
`table`
ORDER BY id DESC
LIMIT 50;
Also it’s remarkable that rows could be manipulated (deleted) and that’s why following query isn’t working either
SELECT
*
FROM
`table`
WHERE
id > ((SELECT
MAX(id)
FROM
chat) - 50)
ORDER BY id ASC;
Question: How is it possible to retrieve last N rows from MySQL database that can be manipulated and be in ASC order ?
You can do it with a sub-query:
SELECT * FROM
(
SELECT * FROM table ORDER BY id DESC LIMIT 50
) AS sub
ORDER BY id ASC;
This will select the last 50 rows from table, and then order them in ascending order.
SELECT * FROM table ORDER BY id DESC LIMIT 50
save resources make one query, there is no need to make nested queries
SELECT * FROM table ORDER BY id DESC, datechat DESC LIMIT 50
If you have a date field that is storing the date (and time) on which the chat was sent or any field that is filled with incrementally (order by DESC) or de-incrementally (order by ASC) data per row put it as second column on which the data should be ordered.
That's what worked for me!!!! Hope it will help!!!!
Use it to retrieve last n rows from mysql
Select * from tbl order by id desc limit 10;
use limit according to N value.
if anyone need this
you can change this into
SELECT
*
FROM
`table`
WHERE
id > ((SELECT
MAX(id)
FROM
chat) - 50)
ORDER BY id ASC;
into
SELECT
*
FROM
`table`
WHERE
id > (SELECT MAX(id)- 50 FROM chat)
ORDER BY id ASC;
select * from Table ORDER BY id LIMIT 30
Notes:
* id should be unique.
* You can control the numbers of rows returned by replacing the 30 in the query
I have a mySQl db (name "stocks") with 50 tables, each tables with
id, symbol, date, time, open, high, low, close, volume as columns (9 columns).
I would like to know what is the last record for each table, ordered for date then time.
Should I have to ORDER BY all data for each table or there is a better way to just know last record?
I am asking help for a query that just return only last record for each table in db.
Thanks
PS For last record I mean most recent as Date then Time
There are two options how to do that:
-- I would use this only if you need more than one records
SELECT * FROM table ORDER BY date DESC LIMIT 1;
-- Way to go:
SELECT * FROM table WHERE date = (SELECT MAX(date) FROM table) LIMIT 1;
Don't forget to add index on date. If it's possible you add lot's of records at the same time you will have to add:
ORDER BY id DESC -- In case that date is highest for records for last records
ORDER BY time DESC -- Every other case
To the end of query
I am going to make the assumption that the record with the largest ID is the "last" (assuming strictly increasing sequential IDs that are unique within a table). If you have a better definition of "last" that could make a difference.
To get one "last" record, you could do:
Select * from table_1 where id = (select max(id) from table_1);
To get the results of all 50 tables into a single result set, you could do:
Select * from table_1 where id = (select max(id) from table_1)
union
Select * from table_2 where id = (select max(id) from table_2)
union
Select * from table_3 where id = (select max(id) from table_3)
union...
A MySQL-specific solution could be
Select * from table_1 order by id desc limit 1
union
Select * from table_2 order by id desc limit 1
union
Select * from table_3 order by id desc limit 1
union...
Based on your edit (where you actually define what you mean by "last"):
Select * from table_1 order by date desc, time desc, id desc limit 1
union
Select * from table_2 order by date desc, time desc, id desc limit 1
union
Select * from table_3 order by date desc, time desc, id desc limit 1
union...
Here is one way to do it without sorting the table:
select * from tab1
where time = (select max(time)
from tab1
where date = (select max(date) from tab1))
and date = (select max(date) from tab1)
It should be very fast, like, O(c), provided that both columns are indexed, otherwise the time will simply be O(n)