Print a random row from a mysql query - php

I was looking for a way of creating a collaborative translation widget. So I have a mysql database and table (called translations), and a little script to allow users to translate one page at a time.
But I'm not quite convinced with my script. I don't think it's efficient enough. First, the mysql gets all the rows with the empty 'en' column, and then a single one of them is showed by screen through a while. Is there any other way of doing this? This is the code:
//Retrieve all the data from the "translations" table
$result = mysql_query("SELECT * FROM translations WHERE en IS NULL OR en=''") or die(mysql_error());
$Randnum=rand(0,mysql_num_rows($result)-1); //Gets a random number between 0 and the maximum number of rows
$i=0; //Start to 0
while($Col = mysql_fetch_array($result)) //While there are rows to evaluate
{
if ($i==$Randnum)
{
echo "\"".$Col['es']."\"<br><br>Translate it to English: <br>";
}
$i++;
}
I was looking for something like "echo $Col[$Randnum]['es']" or "echo $Col.$Randnum['es']" instead of using the whole while loop to print a single random row. How can I implement this? If it's just a matter of optimization. If you could come with an script or idea to assign to $Col just ONE row with a random number and the empty 'en' col, that'd be even better! (I think it's not possible this last bit). The 'en' row is text so I don't know how to implement other methods I've seen around as they use number with ORDER BY.

You can use ORDER BY RAND() LIMIT 1 in your query to fetch a single random row from the database.

Make it at query side
SELECT * FROM translations WHERE en IS NULL OR en='' ORDER BY rand() LIMIT 0,1

There are a few ways of doing this.
#ThiefMaster's answer will work - but "order by rand()" has pretty major performance problems on large tables. So, I'd populate your table with sample data of roughly the size you want to be able to grow to, and test the performance. If it's not a problem, leave it as it is - premature optimization is the root of all evil!
There are some alternatives; they rely on running two, separate queries; however, assuming you've got indices, that shouldn't be a problem.
Reworked for your scenarion, this becomes:
mysql_query('SELECT #count := COUNT(*) FROM translations WHERE en IS NULL OR en=''');
mysql_query('SET #offset = CONVERT(FLOOR(RAND() * #count), SIGNED)');
mysql_query('PREPARE mystatement FROM "SELECT * FROM translations WHERE en IS NULL OR en='' LIMIT ?, 1"');
$res = mysql_query('EXECUTE mystatement USING #offset');
$row = mysql_fetch_assoc($res);
print_r($row);

Related

What is the difference between while and between, and what is the better choice in my case?

I have a table that have hundreds of rows, and i want to get specific rows, I used the LIMIT and between id and id
In my application i have two text inputs, one is for START NUMBER and one is for END NUMBER, When i use the LIMIT i need to tell the user to make the right calculation to get the right start and end
So for example if i have a table of 3000 rows, and i want to select 100 rows above 2000 the query will be :
Select * from table LIMIT 2000,100
This will select 100 rows above 2000
The between method :
In the between method, i'm running a while function on all the table and i'm using IF statement to get the right id's here is what i'm doing :
Select * from table
$datastart = $_POST["datastart"];
$dataend = $_POST["dataend"];
$firstid = 0;
$lastid = 0;
$varcount6=1;
$sql = "select ID from users_info";
$sqlread = mysqli_query($conn,$sql);
while($row = mysqli_fetch_assoc($sqlread)){
if($datastart==$varcount6){
$firstid = $rowdirstid["ID"];
}
if($varcount6>=$dataend){
$lastid = $rowdirstid["id1"];
break;
}
$varcount6++;
}
So now i have the first id and the last id of the table, next i use another sql query :
Select * from table where id between $firstid and $lastid
Both worked
My question is: what should i use if i'm loading huge amount of data each time ?
Should i go with while ? or the LIMIT will make the job done ?
To begin with, you should never use PHP to get the data required, stick to doing that solely in SQL, as PHP is never needed.
The limit query you're using will not cut it for what you're trying to do, as it will not care what id's the entries has, so, if your id's are not 100% consecutive, you will not get the desired result.
You should use the between query you display at the bottom of your post.
But, since you haven't provided your full code I cannot say wether or not you sanitized that input, but that is always a good thing to keep in mind. It's preferable to use parameterized queries instead aswell.
If your sure the ID are consecutive, use SELECT * FROM t WHERE id BETWEEN a AND b ORDER BY ID ASC
If you use LIMIT, the SQL Engine have to scan and order all the first results.
(and index the id field)

Using count() to count results from PDO in PHP?

I'm in the process of converting an old mysql_X site to work with PDO, and so far, so good (I think). I have a question regarding the reliability of counting the results of queries, however.
Currently, I've got everything going like this (I've removed the try / catch error code to make this easier to read):
$stm = $db->prepare("SELECT COUNT(*) FROM table WHERE somevar = '1'");
$stm->execute();
$count = $stm->fetchColumn();
if ($count > 0){
$stm = $db->prepare("SELECT * FROM table WHERE somevar = '1'");
$stm->execute();
$result = $stm->fetchAll();
}
There might be stupid problems with doing it this way, and I invite you to tell me if there are, but my question is really about cutting down on database queries. I've noticed that if I cut the first statement out, run the second by itself, and then use PHP's count() to count the results, I still seem to get a reliable row count, with only one query, like this:
$stm = $db->prepare("SELECT * FROM table WHERE somevar = '1'");
$stm->execute();
$result = $stm->fetchAll();
$count = count($result);
if ($count > 0){
//do whatever
}
Are there any pitfalls to doing it this way instead? Is it reliable? And are there any glaring, stupid mistakes in my PDO here? Thanks for the help!
Doing the count in MySQL is preferable, especially if the count value is the only result you're interested in. Compare your versions to equivalent question "how many chocolate bars does the grocery store have in stock?"
1) count in the db: SELECT count(*) .... Drive to the store, count the chocolate bars, write down the number, drive home, read the number off your slip of paper
2) count in PHP: SELECT * .... Drive to the store. Buy all the chocolate bars. Truck them home. Count them on your living room floor. Write the results on a piece of paper. Throw away the chocolate bars. Read number off the paper.
which one is more efficient/less costly? Not a big deal if your db/table only has a few records. When you start reaching the thousands/millions of records, version 2) is absolutely ludicrious and likely to burn through your bandwidth, blow up your PHP memory limit, and drive your CPU usage into the stratosphere.
That being said, there's no point in running two queries, one to just count how many records you MAY get. Such a system is vulnerable to race conditions. e.g. you do your count and get (say) 1 record. by the time you go to run the second query and fetch that record, some OTHER parallel process has gone and inserted another record, or deleted the one you'd wanted.
In first case you are counting using MYSQL, and in second case you are counting using PHP. Both are essentialy same results.
Your usage of the queries is correct. The only problem will appear when you use LIMIT, because the COUNT(*) and the count($result) will be different.
COUNT(*) will count all the rows that the query would have returned (given that the counting query is the same and not using LIMIT)
count($result) will count just the returned rows, so if you use LIMIT, you will just get the results up to the given limit.
Yes it's reliable in this use case!

Php MySQL query where array value is not found

I have an array of data that generates unique data on the fly in a manor of speaking. It's actually an array with 5 hashes.
What I want to do is a basic select query with a where clause that checks each via OR basically a one line query rather than a query for each array item.
I'm attempting to ensure that no one hash that enters the db is the same as another which I know the probability is virtually null to that actually happening but it's a possibility none the less, safer than sorry is my perspective on the matter
Anyway the query I'm thinking of makes no sense as if a match is found the query will result in such what I wanna do is from the original array find the one that's not found and use it where if all 5 aren't found I'll just randomly pick one I guess in the end I want to form a result that is 1 to 5 in a new array so I can randomly pick from that result
Is this possible or would it just be easie to cycle over each one with a songle query?
"SELECT
CASE hashes.hash
WHEN $hashes[0] THEN 0
WHEN $hashes[1] THEN 1
WHEN $hashes[2] THEN 2
WHEN $hashes[3] THEN 3
...
END
FROM hashes WHERE hashes.hash IN(".implode($hashes).")"
This should tell you exactly which of the hashes you sent to the server have been found on the server.
The result set would be the index keys (0, 1, 2, 3) of the array that generated the query.
If you sent a query based on an array of 100 hashes and you get a result set of 99 hashes, that means at least one hash was not found in the db.
You could cycle through the result set like this:
while($row = $pdo->fetch()) {
$index = $row[0] // first column of the result set
unset($hashes[$index]);
}
When while finishes the only hashes left in the array should be the ones that weren't found in the database.
My opinion is that it would be easier to to cycle over each one with a single query. From what you say there appears to be no major benefit in doing it all at once.
In that case I would suggest:
alter table myTable create id_bkp int;
update myTable set id_bkp=account_id;
update myTable set account_id=56 where id_bkp=100;
update myTable set account_id=54 where id_bkp=56;
alter table myTable drop id_bkp;
Of course that will depend on what DB system you are using.
Do you mean something like this?
$sql = "SELECT * FROM `table` WHERE `field` = ";
$where_string = "'" . implode("' OR `field` = '",$my_array) . "'";
$sql .= $where_string;
You could use:
$my_array = array_unique($my_array);
To remove duplicate values.

Storing randomly picked rows in session?

Storing randomly picked rows in session?
Hi!
I’m building a PHP script that outputs a random word from a MySQL table. Each time the script is refreshed I want a new word to display. (It is connected to jquery – so the data output of the php-file is directly displayed on my page)
However, I want to display each word only once. If all the words are picked, I want the script to reset and start picking over again.
Right now I have done this by setting up an additional table called “visited” and putting all the picked rows from table “wordlist” in there, with the users unique session-id to
prevent results from multiple users to interfere with eachother.
So the query goes like this:
session_start();
$id = session_id();
$random_sql = "SELECT *
FROM wordlist AS a
LEFT JOIN visited AS b ON a.word = b.word
AND b.sessionid = '$id'
WHERE b.word IS NULL
ORDER BY a.weight * rand( ) DESC // Weighted random
LIMIT 1";
$random_row = mysql_query($random_sql);
if(mysql_num_rows($random_row) > 0)
{
while($row = mysql_fetch_row($random_row))
{
$insert_query = "INSERT INTO visited (ID, word, sessionid, date) VALUES ('$row[0]', '$row[1]', '$id', CURDATE())";
$insert = mysql_query($insert_query) or die (mysql_error());
echo $row[1];
}
This works perfectly fine, but I reckon it would be hard for the database to handle many visitors at the same time?
So my question is:
How can I store the information of “visited” words in a session and exclude them from the query?
One more thing: I’m estimating that the wordlist-table will have around 8000 rows. Will this be too many for the ORDER BY RAND-function, and render out to be noticeably slow?
Thanks!
This depends on how much the data must be persistent. If you don't need persistency then session is of course much more efficient in this case. You can store there any PHP data structure, i.e. I guess you'd use associative array in this case.
Regarding performance: if the words are indexed sequentially, you can think of generating the random number as a direct id and just retrieve the particular row directly. ORDER BY RAND() must generate all the numbers and sort them, which is much less efficient than just generate one id like RAND() * MAX(ID).
Read more here.

Select nth record from a SQL result set in php

I run a query and get a result set. I need to get the nth record from this result set. Any ideas how I can do this?
I cannot select the nth record from the database directly coz I need to filter it and don't know how many records there are in the result set. So I cannot be certain which number to pass to the mysql_result method i.e.
Based on certain conditions, get a few rows from a table
From these rows, select the nth row (the number n is not fixed. It depends on the number of records returned)
The basic idea is to get all results based on a set condition and get a random result from these.
Any ideas? Thanks.
Your question seems unclear. However here's a guess:
You want to select the record in the middle:
$count = mysql_num_rows($result);
$middle_name = mysql_result($result, intval($count/2), 'name');
Besides that, you can also do that if you have really less records:
$rs = array();
while ($row = mysql_fetch_assoc($result)){
$rs[] = $row;
}
and then you can use $rs[N-1] to reach Nth record.
Read mysql_data_seek from PHP Manual if you will fetch just one record.
The basic idea is to get all results based on a set condition and get a random result from these.
SELECT ... ORDER BY RAND() LIMIT 1
I know this is not best practice, but as the given information is rather sparse, this could be starting point for further reading. And to be honest, in an small enough application, this is often the easiest solution.

Categories