Select Random Max in MySQL - mysql

I have this table:
id name bid
1 Test1 5.50
2 Test2 5.50
3 Test3 5.49
I want to select the row with the highest bid. If the highest bid is equal on another row, then it should randomly select one of the highest bid rows.
I tried:
SELECT name,max(bid) FROM table ORDER BY rand()
The output:
id name bid
1 Test1 5.50
My problem is that id "2" is never displayed because for some reason my query is only selecting id "1"

SELECTing name and MAX(bid) in the same query makes no sense: you are asking for the highest bid aggregated across all the rows, plus a name that's not aggregated, so it's not at all clear which row's name you'll be picking. MySQL typically picks the “right” answer you meant (one of the rows that owned the maximum bid) but it's not guaranteed, fails in all other databases, and is invalid in ANSI SQL.
To get a highest-bid row, order by bid and pick only the first result. If you want to ensure you get a random highest-bid row rather than just an arbitrary one, add a random factor to the order clause:
SELECT name, bid
FROM table
ORDER BY bid DESC, RAND()
LIMIT 1

SELECT name,bid
FROM table
WHERE bid=(SELECT max(bid) FROM table)
ORDER BY RAND()
LIMIT 1
should do the trick. Waiting for more optimized request ^^

That's because you're using an aggregate function, which collapses everything into a single row. You need a sub-select:
SELECT *
FROM table
WHERE bid = (SELECT MAX(bid) FROM table)
ORDER BY rand()
LIMIT 1;
But also be aware of why not to use ORDER BY RAND(). Although if you have only a few results, the performance implications may not be significant enough to bother changing.

Related

Why does SQL LIMIT clause returns random rows for every query?

It is a very simple query. For every query, I get a different result. Similar things happen when I used TOP 1. I would like a random sub-sample and it works. But am I missing something? Why does it return a different value every time?
SELECT DISTINCT user_id FROM table1
where day_id>="2009-01-09" and day_id<"2011-02-16"
LIMIT 1;
There's no guarantee that you will get a random result with your query. It's quite likely you'll get the same result each time (although the actual result returned will be indeterminate). To guarantee that you get a random, unique user_id, you should SELECT a random value from the list of DISTINCT values:
SELECT user_id
FROM (SELECT DISTINCT user_id
FROM table1
WHERE day_id >= "2009-01-09" AND day_id < "2011-02-16"
) u
ORDER BY RAND()
LIMIT 1
SQL statements represent unordered sets, add order by clause such as
...
ORDER BY user_id
LIMIT 1

Take into account additional fields if count results are equal

I am currently using the following query to find a "category_id" that is used the most based on the "name" of an item.
SELECT Name,category_id,COUNT(*) as count
FROM ex.item
where name LIKE '%living%'
GROUP BY category_id ORDER by count DESC;
However I hit situations in which the count results are equal
So I have modified my query to return the result randomly :
SELECT Name,category_id,COUNT(*) as count
FROM ex.item
where name LIKE '%living%'
GROUP BY category_id ORDER by count,rand() DESC LIMIT 1;
This works but I wanted to improve the query and remove the rand() altogether doing the following:
1) Take subcategory_id into account (on the same table) into, so on the above example if category_id 550 is the most prevalent category_id being used and it has three subategory_id columns two with 800 and one with 900 then return the category_id 500 and subcategory_id 800 as the most common result.
2)Assuming that we still have the scenario per the picture above in which the count returns the same number ( even though that we included the subategory_id) , to try to use the description of the item field (in the same table) to see if the query string appears in the description field as well and if it appears in the description and name, to return the row that it appears in both as the prevalent result.
Thanks
1) You can group by more than one value:
SELECT Name,category_id, subcategory_id, COUNT(*) as count
FROM ex.item
where name LIKE '%living%'
GROUP BY category_id, subcategory_id ORDER by count DESC;
2) Use a CASE statement to compute a score/weight/boost that you can combine with other factors:
SELECT Name,case when description like '%living%' then 1 else 0 end as boost, category_id,COUNT(*) as count
FROM ex.item
where name LIKE '%living%'
GROUP BY category_id ORDER by count DESC;
Stuff you didn't ask about:
If these categories are "tied", why not take them to the one that
makes you the most money (highest margins, highest sale percentage,
etc)?
If you use wildcards at the beginning of your values, the
query can't use an index on that column. Look into a text search
function (MySQL has one, or go use something like
solr/elasticsearch/cloudsearch). This will also help you with the scoring.

MySQL select distinct doesn't work

I have a database with 1 table with the following rows:
id name date
-----------------------
1 Mike 2012-04-21
2 Mike 2012-04-25
3 Jack 2012-03-21
4 Jack 2012-02-12
I want to extract only distinct values, so that I will only get Mike and Jack once.
I have this code for a search script:
SELECT DISTINCT name FROM table WHERE name LIKE '%$string%' ORDER BY id DESC
But it doesn't work. It outputs Mike, Mike, Jack, Jack.
Why?
Because of the ORDER BY id DESC clause, the query is treated rather as if it was written:
SELECT DISTINCT name, id
FROM table
ORDER BY id DESC;
except that the id columns are not returned to the user (you). The result set has to include the id to be able to order by it. Obviously, this result set has four rows, so that's what is returned. (Moral: don't order by hidden columns — unless you know what it is going to do to your query.)
Try:
SELECT DISTINCT name
FROM table
ORDER BY name;
(with or without DESC according to whim). That will return just the two rows.
If you need to know an id for each name, consider:
SELECT name, MIN(id)
FROM table
GROUP BY name
ORDER BY MIN(id) DESC;
You could use MAX to equally good effect.
All of this applies to all SQL databases, including MySQL. MySQL has some rules which allow you to omit GROUP BY clauses with somewhat non-deterministic results. I recommend against exploiting the feature.
For a long time (maybe even now) the SQL standard did not allow you to order by columns that were not in the select-list, precisely to avoid confusions such as this. When the result set does not include the ordering data, the ordering of the result set is called 'essential ordering'; if the ordering columns all appear in the result set, it is 'inessential ordering' because you have enough data to order the data yourself.

Select most common value from a field in MySQL

I have a table with a million rows, how do i select the most common(the value which appears most in the table) value from a field?
You need to group by the interesting column and for each value, select the value itself and the number of rows in which it appears.
Then it's a matter of sorting (to put the most common value first) and limiting the results to only one row.
In query form:
SELECT column, COUNT(*) AS magnitude
FROM table
GROUP BY column
ORDER BY magnitude DESC
LIMIT 1
This thread should shed some light on your issue.
Basically, use COUNT() with a GROUP BY clause:
SELECT foo, COUNT(foo) AS fooCount
FROM table
GROUP BY foo
ORDER BY fooCount DESC
And to get only the first result (most common), add
LIMIT 1
To the end of your query.
In case you don't need to return the frequency of the most common value, you could use:
SELECT foo
FROM table
GROUP BY foo
ORDER BY COUNT(foo) DESC
LIMIT 1
This has the additional benefit of only returning one column and therefore working in subqueries.

What's the most efficient way to select the last n rows in a table without changing the table's structure?

What's the most efficient way to select the last n number of rows in a table using mySQL? The table contains millions of rows, and at any given time I don't know how large the table is (it is constantly growing). The table does have a column that is automatically incremented and used as a unique identifier for each row.
SELECT * FROM table_name ORDER BY auto_incremented_id DESC LIMIT n
Actually the right way to get last n rows in order is to use a subquery:
(SELECT id, title, description FROM my_table ORDER BY id DESC LIMIT 5)
ORDER BY tbl.id ASC
As this way is the only I know that will return them in right order. The accepted answer is actually a solution for "Select first 5 rows from a set ordered by descending ID", but that is most probably what you need.
(Similar to "marco"s answer,)
my fav is the max()-function of MySQL too, in a simple one-liner, but there are other ways of sure:
SELECT whatever FROM mytable WHERE id > (SELECT max(id)-10 FROM mytable);
... and you get "last id minus 10", normally the last 10 entries of that table.
It's a short way, to avoid the a error 1111 ("Invalid use of group function") not only if there is a auto_increment-row (here id).
The max()-function can be used many ways.
Maybe order it by the unique id descending:
SELECT * FROM table ORDER BY id DESC LIMIT n
The only problem with this is that you might want to select in a different order, and this problem has made me have to select the last rows by counting the number of rows and then selecting them using LIMIT, but obviously that's probably not a good solution in your case.
Use ORDER BY to sort by the identifier column in DESC order, and use LIMIT to specify how many results you want.
You would probably also want to add a descending index (or whatever they're called in mysql) as well to make the select fast if it's something you're going to do often.
This is a lot faster when you have big tables because you don't have to order an entire table.
You just use id as a unique row identifier.
This is also more eficient when you have big amounts of data in some colum(s) as images for example (blobs). The order by in this case can be very time and data consuming.
select *
from TableName
where id > ((select max(id) from TableName)-(NumberOfRowsYouWant+1))
order by id desc|asc
The only problem is if you delete rows in the interval you want. In this case you would't get the real "NumberOfRowsYouWant".
You can also easily use this to select n rows for each page just by multiplying (NumberOfRowsYouWant+1) by page number when you need to show the table backwards in multiple web pages.
Here you can change table name and column name according your requirement . if you want to show last 10 row then put n=10,or n=20 ,or n=30 ...etc according your requirement.
select * from
(select * from employee
Order by emp_id desc limit n)
a Order by emp_id asc;