2 Unique RAND() items from database - php

At present im using:
SELECT * from foo ORDER BY RAND() LIMIT 2
to select 2 random items from database but there is the possiblily of the result both being the same item so how can i make it so they are not and both always different?
As always thanks in advance :)

Using ORDER BY RAND() does not result in duplicate rows.
Judging from scattered forum posts and bug reports, this might have been the case in a previous MySQL version (although I strongly doubt this), but it definitely is not anymore. I have just run a couple of tests myself and can confirm that ORDER BY RAND() should never return duplicate rows.

Related

MySQL Select efficient first and last row

I want to get two rows from my table in a MySQL databse. these two rows must be the first one and last one after I ordered them. To achieve this i made two querys, these two:
SELECT dateBegin, dateTimeBegin FROM worktime ORDER BY dateTimeBegin ASC LIMIT 1;";
SELECT dateBegin, dateTimeBegin FROM worktime ORDER BY dateTimeBegin DESC LIMIT 1;";
I decided to not get the entire set and pick the first and last in PHP to avoid possibly very large arrays. My problem is, that I have two querys and I do not really know how efficient this is. I wanted to combine them for example with UNION, but then I would still have to order an unsorted list twice which I also want to avoid, because the second sorting does exactly the same as the first
I would like to order once and then select the first and last value of this ordered list, but I do not know a more efficient way then the one with two querys. I know the perfomance benefit will not be gigantic, but nevertheless I know that the lists are growing and as they get bigger and bigger and I execute this part for some tables I need the most efficient way to do this.
I found a couple of similar topics, but none of them adressed this particular perfomance question.
Any help is highly appreciated.
(This is both an "answer" and a rebuttal to errors in some of the comments.)
INDEX(dateTimeBegin)
will facilitate SELECT ... ORDER BY dateTimeBegin ASC LIMIT 1 and the corresponding row from the other end, using DESC.
MAX(dateTimeBegin) will find only the max value for that column; it will not directly find the rest of the columns in that row. That would require a subquery or JOIN.
INDEX(... DESC) -- The DESC is ignored by MySQL. This is almost never a drawback, since the optimizer is willing to go either direction through an index. The case where it does matter is ORDER BY x ASC, y DESC cannot use INDEX(x, y), nor INDEX(x ASC, y DESC). This is a MySQL deficiency. (Other than that, I agree with Gordon's 'answer'.)
( SELECT ... ASC )
UNION ALL
( SELECT ... DESC )
won't provide much, if any, performance advantage over two separate selects. Pick the technique that keeps your code simpler.
You are almost always better off having a single DATETIME (or TIMESTAMP) field than splitting out the DATE and/or TIME. SELECT DATE(dateTimeBegin), dateTimeBegin ... works simply, and "fast enough". See also the function DATE_FORMAT(). I recommend dropping the dateBegin column and adjusting the code accordingly. Note that shrinking the table may actually speed up the processing more than the cost of DATE(). (The diff will be infinitesimal.)
Without an index starting with dateTimeBegin, any of the techniques would be slow, and get slower as the table grows in size. (I'm pretty sure it can find both the MIN() and MAX() in only one full pass, and do it without sorting. The pair of ORDER BYs would take two full passes, plus two sorts; 5.6 may have an optimization that almost eliminates the sorts.)
If there are two rows with exactly the same min dateTimeBegin, which one you get will be unpredictable.
Your queries are fine. What you want is an index on worktime(dateTimeBegin). MySQL should be smart enough to use this index for both the ASC and DESC sorts. If you test it out, and it is not, then you'll want two indexes: worktime(dateTimeBegin asc) and worktime(dateTimeBegin desc).
Whether you run one query or two is up to you. One query (connected by UNION ALL) is slightly more efficient, because you have only one round-trip to the database. However, two might fit more easily into your code, and the difference in performance is unimportant for most purposes.

1250 - Table 'sub' from one of the SELECTs cannot be used in global ORDER clause

i'm trying to make a chat site for learning purposes, so in the course of that, I want the last 30 messages appear in ascending order w.r.t. time. Like, latest messages at the bottom, oldest one at the top. After a huge amount of googling and yet having found no solution that could help, I had to ask this.
This is the MySQL statement.
It returns the data I want but in descending order. That is, latest ones at the top. Even if I change the ASC to DESC, nothing happens.
SELECT * FROM (SELECT msg,sender FROM chatlogs WHERE user1='userone' AND user2='usertwo' ORDER BY 'timeofmsg' DESC LIMIT 30) sub ORDER BY 'sub.timeofmsg' ASC
After a lot of testing and trying solutions out, I found out by myself that when I try to sort the resultant table using PHPMyAdmin's UI, it throws the following error. Turns out it's a MySQL bug of sorts. So how to I work my way around it?
1250 - Table 'sub' from one of the SELECTs cannot be used in global ORDER clause
If you can tell me how to print the query in reverse, even that would help. But no matter how you help, please do explain how your solution would work... I'm a beginner at this.
Your first issue is that you're trying to do an "order by" on a column that is not present in the table "sub". You'll need to return it in the alias:
SELECT * FROM (SELECT msg,sender, timeofmsg FROM chatlogs WHERE user1='userone' AND user2='usertwo' ORDER BY timeofmsg DESC LIMIT 30) sub ORDER BY sub.timeofmsg ASC
Query Error, you cannot use the query as you mentioned it is not possible.

Pick random entry in MySQL database based on certain criteria

Alright, let's say my MySQL table is set up with entries similar to:
id code sold
1 JKDA983J1KZMN49 0
2 JZMA093KANZB481 1
3 KZLMMA98309Z874 0
I'd like it to pick a random ID within the ranges already in the database (or just go from 1-X) and then just assign it to a variable for my own action to be taken. So let's say we want to pick a code that isn't sold (marked as 0 and not 1), then we'd pick it.
Now, it doesn't have to be 100% random, it could check if the first one is sold, if not, keep going. But I'm not 100% sure on how to go by this. Snippets would be appreciated because I can work out things easily on my own, I just need an example to see where I am going.
How about using a WHERE and ORDER BY RAND()
SELECT id, code
FROM tablename
WHERE sold = 0
ORDER BY RAND()
LIMIT 1
If you don't need the random, then don't use it. It can affect performance very negatively. Since you mentioned in your post that it wasn't necessary, I would recomment using Ezequiel's answer above and dropping the rand. See Most Efficient Way To Retrieve MYSQL data in random order PHP for more info.
It seems that your codes are already random, so why not just take the first item; if you have many unsold records in your database, doing the typical ORDER BY RAND() will hurt the database performance.
SELECT *
FROM codes
WHERE sold = 0
LIMIT 1
FOR UPDATE;
I've also added FOR UPDATE to avoid race conditions, assuming that you're using transactions, as you update the record later (to actually sell it).
Have you tried
SELECT * FROM myTable WHERE sold = 0 ORDER BY RAND() LIMIT 1
Adding ORDER BY RAND() to the rest of your SELECT query is the most straightforward way to accomplish this.

mysql_fetch_array() and output in chunks

So I have a table with 45 records (but can be dynamic) and I use mysql_fetch_array() to get the data from the database. What is the best way to output 5 records at a time? So I need to do record 1-5, then have a link for records 6-10, 11-15, and so on. I thought about doing something with array_chunk but not sure how to keep track of the record number. Thanks for hints.
To get the first 5 results form a table:
SELECT * FROM table ORDER BY table.column_name ASC LIMIT 0, 5
Selects from `table`
Ordered by the column name Ascending
Limit 0,5 selects the first 5 results, starting at 0.
Change LIMIT 0,5 to 5,5 to list results 6-10 (start at record 5, and continue for 5 records.)
Ordering is just good practice to ensure consistency. Under most circumstances set this to 'id' if you have an auto-increment 'id' column. If you want results sorted by date, order by a timestamp column. If you want data reversed, order by DESC.
You can keep track of where your queries are though PHP Sessions, Passing GET parameters, temporary database tables, and probably a few more I missed.
Other solution:
Use the array returned from the mysql_fetch_array() and utilite http://php.net/manual/en/language.types.array.php
The obvious disadvantage to this approach is the fact that it fetches all rows in the table. This is okay if you'll NEVER have more than a manageable number of rows. In your case, 45 should be fine, assuming they're not gigantic rows. This approach may also may useful if you want data pre-loaded.
I'd suggest using limits and incremental offsets in your query. Your first query would then be:
select * from TABLE limit 0,5;
Your link has a parameter referencing the next offset so the next query would be:
select * from TABLE limit 5,5;
And so on.
You need in your query LIMIT 0,5. Search web for php paginator.

LIMIT results in MySQL?

ok; this has been frying my brain for hours. I think I might need a sub query, but I'm not that advanced at this kind of thing so any help or pointers in the right direction would be greatly appreciated.
Here's the Query I have....
$query = "SELECT * FROM events WHERE event_type = 'Christmas Listings' AND event_active='Yes' ORDER BY event_date ASC LIMIT 5";
$result= mysql_query($query);
OK... now for the plain english bit on what I want to achieve (to understand what I'm trying to achieve):
I want to check the event type ('event_type') is what I'm getting (ie. Christmas Listings) as there are multiple types in this column.
I want to check the event is active ('event_active') is Yes(the data in this field is Yes/No).
I want to order them by the ('event_date') ASC (the data in this field is yyyy-mm-dd) so they show the latest entry by its date from the DB.
I want to LIMIT (or in some way control the output) the results to only have 5 results displayed when running this kind of query through a WHILE statement.
OK, this all works BUT; when I get to the actual output display, i'm having a shaky output in how many results are actually display... What happens is if I have multiple events which are switched off, as in event_active is 'Off' then its almost like the argument is counting from the all the results that are (including event_active='Off') and consequently not showing how I expect them to display?
Hope this makes sense.... Any help would be gratefully received.
SELECT *
FROM events
WHERE event_type = 'Christmas Listings' AND event_active='Yes'
ORDER BY event_date
LIMIT 0, 5
so your statement is easyer to read..
You shoul use 1 / 0 instead of Yes / no
The Limit does not count all lines!
First step - doing the query including WHERE
Second step - ORDER BY
Third step - LIMIT
If you have set an index on the colum you sort. The sort will stop after 5 lines,
also means - it get faster
The ASC in the ORDER BY command is not necessary, because ASC is default
I am really not sure what you are asking, but LIMIT works as follows:
The LIMIT means that after your query is done, and ALL WHERE statements are processed, only the first 5 are returned.
ALl results where event_active is not 'Yes' will not be shown, and disregarded in everything.
This result is the same as a result where you would do the query without the limit, and just look at the first 5 lines.
That query should be fine. I'd check your data set (or even better, post it!). You might want to look into normalizing the database too. It'll help you out in the future.
The problem, I think, is with your 'event_active'.
MySQL uses 0 and 1 to indicate whether the field is true/false, yes/no, on/off. Try using 0 and 1 in your SELECT statement unless the field type on that field is VARCHAR and you actually are using those words.

Categories