MySQL: applying a random sort on multiple columns - php

In order to have a well scrambled table (for a psychological experiment), I'd like to sort each column of my array by RAND().
Althrough this code works:
SELECT Sort.Variable1, Sort.Variable2 FROM Sort ORDER BY Variable1, Variable2 ASC LIMIT 0 , 30
replacing "ASC" by "RAND()" make the query fail. Can someone give me an advice (even a solution with PHP) ?
Thanks
Edit:
Thanks to all your responses, I finally did it. Here's my PHP code for this (and sorry for the old-fashioned-not-brand-new-PDO-queries). Even if it's maybe useless, I post it:
$i=0;
//Describe to retrieve variables' names
$sqlCol= 'DESCRIBE Sort';
$sqlCol= mysql_query($sqlCol);
while ($res=mysql_fetch_array($sqlCol)) {
$var[$i]=$res['Field'];
$i++;
}
$NbCol=mysql_num_rows($sqlCol); //Number of column to shuffle
// Number of items for each column
$sqlCount= 'SELECT COUNT(*) FROM Sort';
$req2= mysql_query($sqlCount) or die ('Err');
$NbLignes= mysql_result($req2,0,0) or die ();//Number of rows
//Data array
$sql= "SELECT * FROM Sort";
$req= mysql_query($sql) or die ('Err');
$sort=mysql_fetch_array($req);
for($i=0;$i<$NbCol;$i++) {
${'sql'.$i}='SELECT * FROM Sort ORDER BY RAND()';
${'input'.$i} = mysql_query(${'sql'.$i});
while(${'array'.$i}=mysql_fetch_array(${'input'.$i})) {
$bigArray[$i][]=${'array'.$i}[$i];
}
}
for($i=0;$i<$NbLignes;$i++) {
echo '<div id="q'.$i.'"style="margin-bottom: 50px; float:left">Question '.($i+1);
echo '<ul id="sortable'.$i.'" class="sortable">';
for($j=0;$j<$NbCol;$j++) {
echo '<li class="ui-state-default" id="'.$var[$j].$i.'" name="'.$var[$j].$i.'">'. $bigArray[$j][$i].'</li>';
}
echo '</ul></div>';
}

Using ORDER BY RAND() won't randomize columns - it will randomize rows.
To randomize each column separately in SQL you can:
create a result set for each column separately
randomize the order of each of them
join the columns by row number
Unfortunately the MySQL development team haven't yet implemented ROW_NUMBER which would have made this task easy, but you can workaround it by simulating ROW_NUMBER using variables:
SELECT
Column1,
Column2
FROM
(
SELECT Column1, #rn1 := #rn1 + 1 AS rank
FROM Table1, (SELECT #rn1 := 0) vars
) T1
JOIN
(
SELECT Column2, #rn2 := #rn2 + 1 AS rank
FROM Table1, (SELECT #rn2 := 0) vars
ORDER BY RAND()
) T2
ON T1.rank = T2.rank

Here is one possible method to do what you want using PHP. In the example code, I generate a list of numbers and place them in an array. You will need to grab the list from your database.
<?php
// Create 20 rows.
$rows = 20;
// Create an empty array to hold sorted values.
$sort = array();
// Fill the array with ascending/descending numbers.
for ($i = 0; $i < $rows; $i++) {
$sort[$i]['var1'] = $i + 1;
$sort[$i]['var2'] = $rows - $i;
}
// Display the sorted array.
print "Sorted:\n";
print_rows($sort);
// Here's where the important bit happens:
// Create two arrays, each containing a list of the
// array keys from the sorted array (one for each column).
$var1 = array_keys($sort);
$var2 = array_keys($sort);
// Shuffle each list or array keys (one for each column).
shuffle($var1);
shuffle($var2);
// Create an empty array to hold shuffled values.
$shuffle = array();
// For every row in the list of shuffled keys, get
// the matching value from the sorted array, and
// place it in the shuffled array.
for ($i = 0; $i < $rows; $i++) {
$shuffle[$i]['var1'] = $sort[$var1[$i]]['var1'];
$shuffle[$i]['var2'] = $sort[$var2[$i]]['var2'];
}
// Display the shuffled array.
print "\nShuffled:\n";
print_rows($shuffle);
function print_rows($array) {
print "Row | Var 1 | Var2\n";
print "------------------\n";
foreach ($array as $key=>$row) {
printf("%3d | %5d | %4d\n", $key, $row['var1'], $row['var2']);
}
}
?>
Here's the output:
Sorted:
Row | Var 1 | Var2
------------------
0 | 1 | 20
1 | 2 | 19
2 | 3 | 18
3 | 4 | 17
4 | 5 | 16
5 | 6 | 15
6 | 7 | 14
7 | 8 | 13
8 | 9 | 12
9 | 10 | 11
10 | 11 | 10
11 | 12 | 9
12 | 13 | 8
13 | 14 | 7
14 | 15 | 6
15 | 16 | 5
16 | 17 | 4
17 | 18 | 3
18 | 19 | 2
19 | 20 | 1
Shuffled:
Row | Var 1 | Var2
------------------
0 | 8 | 2
1 | 19 | 12
2 | 14 | 5
3 | 16 | 17
4 | 2 | 8
5 | 11 | 4
6 | 7 | 11
7 | 9 | 10
8 | 12 | 1
9 | 5 | 9
10 | 13 | 20
11 | 10 | 6
12 | 17 | 19
13 | 18 | 18
14 | 4 | 14
15 | 20 | 7
16 | 3 | 16
17 | 15 | 15
18 | 6 | 3
19 | 1 | 13
The code is a bit rough and ready. I'm sure there are a number of ways in which it could be improved. For example, you could change it so the it is able to handle a variable number of columns. However, the basic idea is there.
Edit:
Here's a tidied up version of the code, which will handle a variable number of columns:
// Here's where the important bit happens:
// Create an empty array to hold shuffled values.
$shuffle = array();
// Get the name of each column (key) in the array.
foreach (array_keys($sort[0]) as $col) {
// Create an array of keys containing a list of the
// array keys from the sorted array.
$keys = array_keys($sort);
// Shuffle each list or array keys.
shuffle($keys);
// For every row in the list of shuffled keys, get
// the matching value from the sorted array, and
// place it in the shuffled array.
for ($i = 0; $i < $rows; $i++) {
$shuffle[$i][$col] = $sort[$keys[$i]][$col];
}
}
// Display the shuffled array.
print "\nShuffled:\n";
print_rows($shuffle);

You can do by 2 SQL statments:
SELECT Sort.Variable1 FROM Sort ORDER BY RAND(), Variable1 LIMIT 0 , 30
SELECT Sort.Variable2 FROM Sort ORDER BY RAND(), Variable2 LIMIT 0 , 30
if you need random in PHP array use: [array_rand][1]
<?php
// load all table values in array i just set them
$input = array("Neo", "Morpheus", "Trinity", "Cypher", "Tank");
$rand_keys = array_rand($input, 2);
echo $input[$rand_keys[0]] . "\n";
echo $input[$rand_keys[1]] . "\n";
?>

Related

Querying MySQL results into multi-dimensional associative PHP array

I'm probably overlooking a fairly simple way of doing this; perhaps someone has an idea of how to make this easy with limited looping and without an excessively long query. Let's say I have a MySQL table with data like this: (There's 12 months, and could be maybe 10 different possible grades). I'll query out just the results for a given user_id and year.
+----+---------+------+-------+-------+-------+
| id | user_id | year | month | grade | value |
+----+---------+------+-------+-------+-------+
| 1 | 1 | 2021 | Jan | A | 95 |
+----+---------+------+-------+-------+-------+
| 2 | 2 | 2021 | Jan | D | 75 |
+----+---------+------+-------+-------+-------+
| 3 | 2 | 2021 | Feb | F | 45 |
+----+---------+------+-------+-------+-------+
I want to be able to query the data and put it into a multi-dimensional associative PHP array.
Essentially, so I can access the data like this:
echo $month_value['Jan']['D']; // Should give me 75
echo $month_value['Feb']['F']; // Should give me 45
Figured out a simple method that works for me:
$sql_retrieve = $con->prepare("SELECT month, grade, value
FROM table
WHERE user_id = ? AND year = ?;");
$bind_process = $sql_retrieve->bind_param('ii',$user_id,$year);
$sql_retrieve->execute();
$result = $sql_retrieve->get_result();
$month_values = []; // initialize array
if($result->num_rows > 0 ){ // If there are results
while($row=$result->fetch_assoc()){
$month_values[$row["month"]][$row["grade"]] = $row["value"]; // add to array
} // end while
} // end of if num_rows > 0
print_r($month_values); // Example
echo 'Value: '.$month_values['Jan']['D'];
This then provides the MySQL results into a multi-dimensional associative PHP array, so they can be referenced as such.

Group Array items in PHP

I have the above array:
| Student First Name |Student Last Name | Age |Disability|
| Student_First_Name_1 |Student_Last_Name_1 | 30 | 1 |
| Student_First_Name_2 |Student_Last_Name_2 | 28 | 0 |
| Student_First_Name_3 |Student_Last_Name_3 | 21 | 0 |
| Student_First_Name_4 |Student_Last_Name_4 | 20 | 1 |
| Student_First_Name_5 |Student_Last_Name_5 | 22 | 0 |
and I want to grouped the students by age and Disability.
So if my code runs correctly I'll have the above results:
Student_First_Name_1 : Student_First_Name_4
Student_First_Name_3 : Student_First_Name_5
Student_First_Name_2
But instead I have the above:
Student_First_Name_1 : Student_First_Name_4
Student_First_Name_3 : Student_First_Name_5
Student_First_Name_2 : Student_Last_Name_3
Student_First_Name_2 : Student_Last_Name_5
My code is:
$StudentsForSID = $conn->prepare("SELECT * FROM members WHERE sid = :sid AND level = :level");
$StudentsForSID->execute([ 'sid' => $SelectedSID, 'level' => 'LRN_B1' ]);
while($row = $StudentsForSID->fetch(PDO::FETCH_ASSOC)){
$TempSelected[] = $row;
}
$count=count($TempSelected);
for($i=0; $i<$count-1; $i++){
for ($j = $i+1; $j < $count; $j++) {
if($TempSelected[$i]['disability']==$TempSelected[$j]['disability']){
if( abs($TempSelected[$i]['age']-$TempSelected[$j]['age']) <= 23 ){
$Student1 = $TempSelected[$j]['first_name'];
$Student2 = $TempSelected[$i]['first_name'];
print_r($Student1.'-'.$Student2.'<br/>');
}
}
}
}
I don't think I explained very well. So i edit the question.
What I want:
I want to make groups of 2 students with the same value in disability and the age difference between the 2 students to be equal or under 23.
So I have the above array with 5 students. From this array I'll make 3 groups and the groups will be the above (2 groups with 2 students with fulfilled the criteria, and 1 group with one student).
Can you help me?
Thank you
Why don't you use a double group by in your query?
Group by Age , Disability
This will actually group your results into two groups like you wanted so you will save the php sorting and those multiple if and for.

Keep subtracting value in loop getting mysql result php

I have an mysql table named example.
Id | Amount | Left | Filled
1 | 1 | 1 | 0
2 | 4 | 4 | 0
5 | 7 | 7 | 0
I have an variable named $var = 9
Now I have an array named $array with those ids as array([0] => 1, [1] => 2, [2] => 5) Which itself is a mysql result.
How do I make a loop so that ids in array keep subtracting the left and keep filling as per the amount but within the total value of $var so that my end result in table is
Id | Amount | Left | Filled
1 | 1 | 0 | 1
2 | 4 | 0 | 4
5 | 7 | 3 | 4
You can use while loop in order to loop on the ids and reduce the amount in each iteration.
I am not sure how you access your DB so I leave it pseudo.
Consider the following code:
$ids = array(1,2,5);
$value = 9;
function reduceAmount($id, $value) {
$query = mysqli_query($conn, "SELECT * FROM example WHERE Id='$id'");
$row = mysqli_fetch_array($query);
$take = min($row['Left'], $value); // the amount you can take (not more then what left)
$left = $row['Left'] - $take;
$filled = $row['Filled'] + $take;
$conn->query("UPDATE example SET Left='$left', Filled='$filled' WHERE Id='$id'")
return max(0, $value - $take);
}
while ($value > 0 && !empty($ids)) { // check if value still high and the options ids not finish
$id = array_shift($ids); //get first ID
$value = reduceAmount($id, $value);
}
You can check at the end of the loop if value still bigger then 0 - this can happen when no enough "Amount" in ids

Merge first column that has the same second column

I have an INNER JOIN query that returns this result:
first column | second column
2 | 15
5 | 16
6 | 16
5 | 18
6 | 22
I want to output them in my page like this:
first column | second column
2 | 15
5,6 | 16
| 18
| 22
Total first column: 2
It will combine the first column that has the same second column. And value of the combined first column will be share between them.
And would also tell the total first column (which in the example is 2).
I'm still trying with this code:
if($stmt = $con->prepare("SELECT firsttb.firstcolumn, secondtb.secondcolumn FROM firsttb INNER JOIN secondtb ON firsttb.connectid = secondtb.connectid WHERE firsttb.getid = ? ORDER BY secondtb.secondcolumn")){
$stmt->bind_param("i",$_GET["getid"]);
$stmt->execute();
$stmt->bind_result($firstcolumn,$secondcolumn);
$lastid = 0;
$total = 0;
$combine = "";
while($stmt->fetch()){
if($lastid == $secondcolumn){
$combine = $combine."".$firstcolumn;
$lastid = $secondcolumn;
}
else {
$totalrow = $totalrow + 1;
$lastid = $secondcolumn;
$combine = $secondcolumn;
echo $combine." | ".$secondcolumn."<br>";
}
}
$stmt->close();
echo "Total first column: ".$totalrow;
}
But only get this output:
first column | second column
2 | 15
5 | 16
5 | 18
6 | 22
Total first column: 4
It is okay to scrap my code and create it with your own. Just want to look for the right idea.

How to calculate a running total?

I have looked at the other (many closed) running total questions in PHP however none satisfy
This is my query
<?php
$query = "SELECT SUM(game_total) AS totalgames,
SUM(goals) AS totalgoals,
FROM player
WHERE year between 2006 and 2013
AND month between 1 and 12
GROUP BY
year,
month between 1 and 3,
month between 4 and 6,
month between 7 and 9,
month between 10 and 12
ORDER BY year ASC, month ASC";
$rquery = mysql_query($query) or die(mysql_error());
$show2 = '';
while ($row = mysql_fetch_array($rquery)){
$games = $row['totalgames'];
$goals = $row['totalgoals'];
$gpg = $games == 0 ? 0 : number_format($goals/$games,2);
}
?>
If I echo the results of the query completing the loop I get the following result
+---------+----------+-----------+
| Goals | Games | GPG |
+---------+----------+-----------+
| 8 | 15 | 0.53 |
+---------+----------+-----------+
| 2 | 12 | 0.17 |
+---------+----------+-----------+
| 2 | 12 | 0.17 |
+---------+----------+-----------+
| 13 | 21 | 0.62 |
+---------+----------+-----------+
etc etc
However, I want to calculate a running GPG in the following manner
Goals/Games=GPG
8/15=0.53
10/27=0.37 (8+2/15+12)=GPG
12/39=0.31 (10+2/27+12)=GPG
25/60=0.41 (12+13/39+21)=GPG
and I want to echo it like this
0.53,0.37,0.31,0.41 etc etc
how would I do this?
$count = 0;
while ($row = mysql_fetch_array($rquery)){
$count++;
$games = (int)$row['totalgames'];
$goals = (int)$row['totalgoals'];
$totalGames = 0;
$totalGoals = 0;
$totalGames += $goalData['games'];
$totalGoals += $goalData['goals'];
echo $totalGoals/$totalGames;
echo ($count === mysql_num_rows($rquery)-1) '' : ',';
}
There we go, running total GPG as it iterates, commas separated without making an extra array. That what you're looking for?

Categories