На моей веб-странице есть переменная с именем $submission
. Я хотел бы отобразить ровно 11 строк из запроса ниже: строка, где $submission
равна $row["title"]
, 5 строк над ней и 5 строк ниже нее. Все ранжируются по points
.
Как я могу это сделать?
$sqlStr = "SELECT title, points, submissionid FROM submission ORDER BY points DESC"; $result = mysql_query($sqlStr); $arr = array(); $count=1; echo "<table class=\"samplesrec\">"; while ($row = mysql_fetch_array($result)) { echo '<tr >'; echo '<td>'.$count++.'.</td>'; echo '<td class="sitename1">'.$row["title"].'</td>'; echo '<td class="sitename2"><div class="pointlink2">'.number_format($row["points"]).'</div></td>'; echo '</tr>'; } echo "</table>";
Это немного сложно, если несколько строк имеют одинаковое значение для «точек»:
points | title | submissionid ------ + ----- + ------------ ... 50 | 'foo' | ABCD01234 <-- If (50, 'foo') is the "midpoint" record, 50 | 'bar' | EF7654321 <-- does (50, 'bar') come before or after? ...
В этом случае нам нужно наложить порядок. Для удобства мы собираемся заказать по «точкам» по убыванию, а затем «название» по убыванию.
Предполагая, что ваша «средняя точка» имеет значение точек «@points» и заголовок «@title», мы скажем, что записи, которые доходят до «середины», это те записи, чьи (точки, заголовок)> (@points, @заглавие). Аналогично, эти записи «после» середины имеют свои (точки, заголовок) <(@points, @title).
Объединяя это, мы имеем:
-- First, initialize user variables with the points and -- title of our midpoint (including midpoint) -- SELECT @title := title, @points := points FROM submission WHERE title = ? -- bind your $submission variable bere LIMIT 1; -- Now, select six records greater than or equal to our -- midpoint. -- SELECT title, points, submissionid FROM ( SELECT title, points, submissionid FROM submission WHERE (points, title) >= (@points, @title) ORDER BY points ASC, title ASC LIMIT 6) gte -- and UNION those records with five records less than -- our midpoint -- UNION SELECT title, points, submissionid FROM ( SELECT title, points, submissionid FROM submission WHERE (points, title) < (@points, @title) ORDER BY points DESC, title DESC LIMIT 5) lt -- Finally sort the result set -- ORDER BY points DESC, title DESC
Вам необходимо использовать UNION
( SELECT title, points, submissionid FROM submission WHERE points < (SELECT points FROM submission WHERE title = <row_title> LIMIT 1) ORDER BY points DESC LIMIT 5 )UNION( SELECT title, points, submissionid FROM submission WHERE points > (SELECT points FROM submission WHERE title = <row_title> LIMIT 1) ORDER BY points ASC LIMIT 5 ) ORDER BY points DESC
Я не тестировал его, но это суть. Вы получите 10 записей (или меньше), вам нужно будет найти на PHP, какие записи должны идти выше вашего $submission
и ниже, так как если вы получите 9 записей, вы не знаете, если 4 выше, или 5.
В качестве альтернативы вы могли бы просто сделать это с помощью двух запросов, конечно> _>
SELECT title, points, submissionid FROM submission WHERE ROWID >= (SELECT ROWID FROM submission WHERE ..... ORDER BY points DESC) - 5 AND ROWID <= (SELECT ROWID FROM submission WHERE ..... ORDER BY points DESC) + 5 ORDER BY points DESC
Я думаю, что следующее может сделать то, что вы просите. Он выполнит запрос и начнет считывать записи из набора результатов до тех пор, пока не найдет первое вхождение записи с заголовком, равным переменной $submission
которую вы имеете (если я правильно понимаю, что первичный ключ вашей таблицы является файлом uploadid и заголовок – это простое поле, т. е. у вас нет уникального ключа, поэтому может быть несколько записей с одним и тем же заголовком).
После того, как он обнаружит, что первая запись, он будет читать еще 5 записей и останавливаться. Затем он предоставит вам часть массива записей, которые вы хотите распечатать, и, наконец, распечатает его.
$sqlStr = "SELECT title, points, submissionid FROM submission ORDER BY points DESC"; $result = mysql_query($sqlStr); $count = 1; $found = false; $continue = true; $records = array(); $row = mysql_fetch_array($result); while ($row !== false && $continue === true) { if($found == false && $row['title'] == $submission) { $found = true; } elseif($found == true && $count < 6) { $count++; } elseif($found == true && $count >= 6) { $continue = false; } $records[] = $row; $row = mysql_fetch_array($result); } if($found === true) { if(array_count($records) > 11) $records = array_splice($records, -11); } else { $records = array(); } echo "<table class=\"samplesrec\">"; for($i = 1; $i <= count($records); $i++) { echo '<tr >'; echo '<td>'.$i.'.</td>'; echo '<td class="sitename1">'.$records[$i]["title"].'</td>'; echo '<td class="sitename2"><div class="pointlink2">'.number_format($records[$i]["points"]).'</div></td>'; echo '</tr>'; } echo "</table>";
Если нет повторяющихся значений "title"
и если нет повторяющихся "points"
SELECT title, points, submissionid FROM submission WHERE points <= ( SELECT points FROM submission WHERE points >= ( SELECT points FROM submission WHERE title = @row_title ) ORDER BY points ASC LIMIT 1 OFFSET 5 ) ORDER BY points DESC LIMIT 11 OFFSET 0
Я думаю, что его простейший просто сделать 2 запросов к базе данных, но вы можете сделать это в PHP:
$entryFound = false; $counter = 0; $priorEntries = array(); while ($row = mysql_fetch_array($result)) { $rowHtml = '<tr >'; $rowHtml .= '<td>'.$count++.'.</td>'; $rowHtml .= '<td class="sitename1">'.$row["title"].'</td>'; $rowHtml .= '<td class="sitename2"><div class="pointlink2">'.number_format($row["points"]).'</div></td>'; $rowHtml .= '</tr>'; if ($entryFound) { if ($counter < 5) { $counter++; echo $rowHtml; } } else { array_unshift($priorEntries, $rowHtml); if (strcmp($row["title"], $submission) == 0) { echo implode(array_reverse($priorEntries)); $entryFound = true; } array_splice($priorEntries, 5); } }
Если намерение заключается в использовании одного запроса, а таблица мала, а производительность не имеет значения, используйте
SELECT b.title, b.points FROM ( SELECT @rank1 := @rank1 + 1 as slno, temp1.* FROM ( SELECT s1.title, s1.points, COUNT(s2.title) rank FROM submission s1 JOIN submission s2 ON s1.points <= s2.points GROUP BY s1.title, s1.points ORDER BY rank, s1.title ASC ) as temp1 JOIN( SELECT @rank1 := 0 ) AS init ORDER BY slno ) a LEFT JOIN ( SELECT @rank2 := @rank2 + 1 as slno, temp1.* FROM ( SELECT s1.title, s1.points, COUNT(s2.title) rank FROM submission s1 JOIN submission s2 ON s1.points <= s2.points GROUP BY s1.title, s1.points ORDER BY rank, s1.title ASC ) as temp1 JOIN( SELECT @rank2 := 0 ) AS init ORDER BY slno ) b ON a.slno BETWEEN b.slno - 5 AND b.slno + 5 WHERE a.title = <row_title>;
Это вернет строку, выбранную и выше 5 (если она есть), и до 5 ниже (если она есть).
Тем не менее, настоятельно рекомендуется использовать временную таблицу для хранения рангов и использовать ее для отображения рангов.
Подробнее о запросе выше http://www.artfulsoftware.com/infotree/queries.php?&bw=1280#460
Подробнее об оптимизированном методе @ http://onlamp.com/pub/a/mysql/2007/03/01/optimize-mysql-rank-data.html