Use PHP mt_rand() instead of MySQL rand() to avoid slow query - php

I have a PHP file that looks for a random id from a MySQL DB, but when the table is big enough it gets slow.
ID row has gaps.
Original
$sql = "SELECT * FROM definiciones ORDER BY rand() LIMIT 1";
Idea
$random = mt_rand(0, 10000);
$sql = "SELECT * FROM definiciones WHERE id = (SELECT max(id) FROM definitiones WHERE id < $random)";
I know the exact amount of rows in the DB beforehand. Is it a good idea to replace the original query?

Is it a good idea to replace the original query?
Yes, but there's a simpler way of expressing this:
SELECT * FROM definiciones WHERE id >= ? ORDER BY id LIMIT 1
With ? set to a random number between 0 and the maximum ID in the table.
Now, an improvement: If there are any gaps in the values of id, the results from the previous method will be skewed somewhat. (For instance, if there are no rows with id < 100, then a row with id = 100 will be selected much more often than one with id = 101.) You can avoid this by using a separate column for randomization. First, you will need to add the column:
ALTER TABLE definiciones ADD COLUMN randomval FLOAT NOT NULL,
ADD KEY randomval (randomval);
UPDATE TABLE definiciones SET randomval = RAND();
Then, to select a fairly chosen random item:
SELECT * FROM definiciones WHERE randomval > ? LIMIT 1;
using a random value between 0 and 1 for the parameter.
There is a small chance that this will return no rows (if RAND() selects a value greater than the highest value in the table). If this happens, repeat the query.
You will need to set randomval = RAND() when inserting new rows into the table.

Related

SQL: in calculation, I want the result to be 0, not null

I have a SQL query in PHP that recalculates the average rating when the user cancels his vote:
$delete_vote = $conn->prepare("UPDATE table SET
votes = votes - 1,
total_value = total_value - :recall_vote,
average = (total_value * 10) / (total_votes * 10)
WHERE id=id");
The problem is with the recalculation of the average vote. Even if I set the default value for the column 'total_value' and for 'average' as zero, if the query in question has only one vote, and that vote is being recalled, the average value will be set to null instead of 0, as I wish it to be. One possible way of solving this would be to retrieve these two values from the database, check if they're null, and then have them changed to 0--but that's a bit of a hassle. So instead, I want to know if there's a simpler solution.
Another thing I've tried is to add a zero to the calculation, hoping that the null would convert to 0:
$vote_count = $conn->prepare("UPDATE table SET
total_votes = (0 + total_votes) + 1
That doesn't work either. Is there a simple solution to this?
Since you're using PHP, I'm assuming you're using MySQL with it. But I think every RDBMS has a similar function... In MySQL you can define a fallback value for when a value is NULL. If the value isn't NULL, it will return the original value:
IFNULL(something, 0)
Besides that, slightly offtopic maybe; I usually try to avoid denormalisation like you did by saving the average - which in most cases can be calculated when querying the database. But this depends on the situation.
I totally do not understand your where clause -- which would almost always evaluate to true.
The correct syntax to use is the ANSI standard COALESCE() or CASE which are available in almost all databases. You could write this as:
UPDATE table
SET votes = votes - 1,
total_value = total_value - :recall_vote,
average = COALESCE((total_value * 10) / NULLIF(total_votes * 10, 0), 0)
WHERE id = $id -- something other than `id`
I think I would be inclined to be explicit:
UPDATE table
SET votes = votes - 1,
total_value = total_value - :recall_vote,
average = (CASE WHEN total_votes > 0
THEN total_value / total_votes
ELSE 0
END)
WHERE id = $id -- something other than `id`
By the way, why are you multiplying by 10 in both the numerator and denominator? Seems unnecessary.

Random data mysql

I have table in mysqli database now i want to get 1 result random
This is code mysql
$cid = $_GET['id'];
$ans = $db->query("select * from result where cat_id='$cid' limit 1");
$rowans = $ans->fetch_object();
echo"
<h4>".$rowans->title."</h4><br>
<img src='".$rowans->img."' style='width:400px;height:300;' />
<p>".$rowans->desc."</p>";
in this code i get 1 result but not random always gives me the same result
SQL:
SELECT *
FROM result
ORDER BY rand()
LIMIT 1
LIMIT orders the rows by the primary key of the table, so you always get the same one (the "first" row according to that key).
Try:
SELECT * FROM result WHERE cat_id='$cid' ORDER BY RAND() LIMIT 0,1;
You can think of it this way: it first orders the results by a random order, then picks the first one.

PHP, MySQL: how to select random records without RAND (attempt to avoid RAND()) [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
How to request a random row in SQL?
Because of a huge table I cannot use MySQL function RAND() and trying to avoid using RAND() this way:
$a = mysql_fetch_array(mysql_query("SELECT MIN(id), MAX(id) FROM table WHERE category_id='".$var."'"));
$id = rand($a[0],$a[1]);
$id2 = rand($a[0],$a[1]);
Which doesn't work, because: in $a I have the biggest and smallest ID with the respective category_id - that's ok.
The problem brings the rows with $id and $id2 - because there are used the minimal and maximal ID values, but PHP function rand() will take some random value between minimum and maximum - and there is a big probability, that the picked out record will not belongs into category_id.
So I would like to ask you about a way, how could I fix this example.
THank you
You could try this:
$row = mysql_fetch_assoc(
mysql_query(
"SELECT count(*) as count FROM table WHERE category_id='$var'"));
$randomrow = rand(0, $row['count'] -1);
$q = mysql_query("
SELECT * FROM table
WHERE category_id='$var'
LIMIT 1 OFFSET $randomrow");

PHP MySQL - Find Row ID

I have a table called "participants" that has 3 fields:
prt_id
prt_event_id
prt_participant_id
What I have is a select query with a where condition on event_id. The query returns let's say 20 rows (20 different participants). What I would like to do is to be able to figure out the row number for a given participant (prt_id).
SELECT *
FROM participants
WHERE prt_id = someinteger
While you can't specifically find a row ID using MySQL, you could do something like the following:
$conn = new mysqli(/*dbinfo*/);
$res = $conn->query("SELECT prt_id FROM participants");
$rowids = array(); $currid = 1;
while ($row = $res->fetch_object()) { // this is using the mysqli library
$rowids[$row->prt_id] = $currid;
$currid++;
}
This would give you an array of ids associated with prt_id.
You could do something like:
<?php
$counter = 1; // Start at one for first entry
$res = mysql_query("SELECT * FROM participants WHERE prt_id = 12");
while( $array = mysql_fetch_assoc($res) )
{
// Do something with the counter, store it into array with details
$counter++;
}
?>
This should do what you want inside MySQL (ie assign a rownum in the order of prt_id), but the performance will be dependent on the number of rows in the table so it's not optimal.
SELECT * FROM (
SELECT #tmp:=#tmp+1 rownum, p.*
FROM (SELECT #tmp:=0) z, participants p
ORDER BY prt_id
) participants
WHERE prt_id = 36;
Demo here.
Edit: This "doh level" rewrite uses an simple index range instead of a table scan, so should be much faster (provided prt_id is a PRIMARY KEY)
SELECT *, COUNT(p2.prt_id) ROWNUM
FROM participants p1
JOIN participants p2
ON p1.prt_id >= p2.prt_id
WHERE p1.prt_id=36;
Demo here.
you could just add an index column in your database, set it as int, primary key and auto increment. then when retrieving the row you retrieve the index number.
RowID is a feature of Oracle: http://docs.oracle.com/cd/B19306_01/server.102/b14200/pseudocolumns008.htm.
MySQL does not have something like that, you can basically emulate that by assign number to an array inside php as you retrieve each row, but that doesn't guarantee you the same number next time you retrieve that results. You probably have to settle for using one of the primary IDs

Easiest way to choose a percentage of an amount of rows from a MySQL table?

I have a script that has a GET variable: $_GET['percentage']
I have a MySQL table of data.
Now lets say that there are 100 rows of data in this table.
In pseudo-code:
SELECT data FROM table
Now would it be possible to select $_GET['percentage'] of random data from table?
For example (again in pseudo-code):
$_GET['percentage'] = 10;
SELECT 10% of data from table order by rand()
If this IS possible, how could I do it?
In MySQL, it's probably easiest to do this in two queries. First, get the count of rows in the table:
SELECT COUNT(*) FROM MyTable;
Then prepare the query to get random rows:
SELECT ... FROM MyTable ORDER BY RAND() LIMIT ?;
Then execute the prepared query and send the value of the count divided by 10.
Not every problem needs to be solved by a single query.
Here's an example PHP script, edited to use the old mysql extension.
<?php
// Get the total number of rows in the table.
$sql = "SELECT COUNT(*) FROM Kingdoms";
$result = mysql_query($sql);
$row = mysql_fetch_array($result);
$rows_in_table = $row[0];
// We only want a portion of the rows, specified by the user
// choice of percentage. The count we want is therefore equal
// to the total number of rows in the table multiplied by the
// desired percentage.
$percentage = intval($_GET["percentage"]) / 100.0;
$count = intval(round($rows_in_table * $percentage));
// LIMIT makes the query return at most the number of rows specified.
// Sort randomly first (if the table has too many rows this will be slow),
// then return the first $count rows.
$sql = "SELECT * FROM Kingdoms ORDER BY RAND() LIMIT {$count}";
$result = mysql_query($sql);
while ($row = mysql_fetch_array($result)) {
print_r($row);
}
PS: Always be careful when interpolating a variable into an SQL expression. You should force the variable to a known format -- an integer value in this case. Otherwise you risk creating an SQL Injection vulnerability.
If you have auto incremented ID field you may use
HAVING ID_FIELD<=ceil(count(*)*10/100);
Otherwise a stored procedure can help in this.
select columnvalue from mytable WHERE RAND() <= 0.5 .....will directly result in very near to 50% of the records
May be this event rise the solution
drop event OEAuditEvent;
DELIMITER $$
CREATE EVENT OEAuditEvent
ON SCHEDULE EVERY 1 SECOND
STARTS '2012-09-05 09:00:00'
DO
BEGIN
DECLARE a CHAR(20);
DECLARE b,c,d INT;
DECLARE done INT DEFAULT FALSE;
IF CURRENT_TIME() = '23:40:00' THEN
begin
DECLARE cur CURSOR FOR select OE_User,count(OE_User) from RNCM_Status where date(OE_Date)=CURDATE() group by OE_User;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
OPEN cur;
read_loop: LOOP
FETCH cur INTO a, b;
SET c=ceil((b*5)/100);
IF done THEN
LEAVE read_loop;
ELSE
insert into OE_Audit(MDN,CAF,UploadedDate,OEUser,OEDate,UserCount,QCCount,intime) select MDN,CAF,UploadedDate,OE_User,OE_Date,b,c,now() from RNCM_Status where OE_User=a and date(OE_Date)=CURDATE() order by rand() limit c;
END IF;
END LOOP;
CLOSE cur;
end ;
END IF;
END $$
DELIMITER ;

Categories