Mysql порядок по рейтингу - но захватить все - PullRequest
0 голосов
/ 28 марта 2009

У меня есть этот код PHP / MYSQL, который возвращает записи из таблицы, упорядоченные по их рейтингам, от самых высоких до самых низких:

<table width="95%">
    <tr>
    <?php
        if (isset($_GET['p'])) {
            $current_page = $_GET['p'];
        } else {
            $current_page = 1;
        }
        $cur_category = $_GET['category'];
        $jokes_per_page = 40;
        $offset = ($current_page - 1) * $jokes_per_page;

        $result = mysql_query("
        select jokedata.id as joke_id, jokedata.datesubmitted as datesubmitted,
        jokedata.joketitle as joke_title, sum(ratings.rating)/count(ratings.rating) as average
        from jokedata inner join ratings
        on ratings.content_type = 'joke' and ratings.relative_id = jokedata.id
        WHERE jokecategory = '$cur_category'
        group by jokedata.id
        order by average desc
        limit $offset, $jokes_per_page
        ");

        $cell = 1;
        while ($row = mysql_fetch_array($result)) {
            if ($cell == 5) {
                echo "</tr><tr class=\"rowpadding\"><td></td></tr><tr>";
                $cell = 1;
            }
            $joke_id = $row['joke_id'];
            $joke_title = $row['joke_title'];
            $joke_average = round($row['average'], 2);

            echo "<td><strong><a class=\"joke_a\" href=\"viewjoke.php?id=$joke_id\">$joke_title</a></strong> -average rating $joke_average.</td>";
            $cell++;
        }
    ?>
    </tr>
    <tr class="rowpadding"><td></td></tr>
</table>

Работает отлично, но есть одна проблема - если предмет не имеет хотя бы одного рейтинга, он вообще не будет выбран запросом!

Как я могу исправить это? Спасибо.

Ответы [ 4 ]

1 голос
/ 09 апреля 2009

Попробуйте это:

SELECT  jokedata.id as joke_id,
        jokedata.datesubmitted as datesubmitted,
        jokedata.joketitle as joke_title,
        COALESCE(
        (
        SELECT  AVG(rating)
        FROM    ratings
        WHERE   ratings.relative_id = jokedata.id
                AND ratings.content_type = 'joke'
        ), 0) AS average
FROM    jokedata
ORDER BY
        average DESC
LIMIE   $offset, $jokes_per_page
1 голос
/ 30 марта 2009

Я бы порекомендовал следующее:

  • используйте left outer join, чтобы получить анекдоты без рейтинга
  • используйте avg() вместо вычисления среднего вручную
  • возможно использовать coalesce(), чтобы избежать null значений в результате

Вот упрощенная версия ваших таблиц:

create table joke(jokeid int primary key, jokedata varchar(50));
create table ratings(rating int, relative_id int);
insert into joke values(1, "killing");
insert into joke values(2, "no rating");
insert into ratings values(5, 1);
insert into ratings values(10, 1);

И некоторые примеры запросов:

select joke.jokeid, avg(ratings.rating) as average 
   from joke 
   left outer join ratings 
     on ratings.relative_id = joke.jokeid 
   group by joke.jokeid;
+--------+---------+
| jokeid | average |
+--------+---------+
|      1 |  7.5000 | 
|      2 |    NULL | 
+--------+---------+

Или, используя coalesce():

select joke.jokeid, avg(coalesce(ratings.rating, 0)) as average 
  from joke 
  left outer join ratings 
    on ratings.relative_id = joke.jokeid 
  group by joke.jokeid;
+--------+---------+
| jokeid | average |
+--------+---------+
|      1 |  7.5000 | 
|      2 |  0.0000 | 
+--------+---------+
1 голос
/ 28 марта 2009

Вам нужно использовать левое соединение, а не внутреннее, а затем обработать случай, когда ratings.ratings равен нулю:

$result = mysql_query("
            SELECT jokedata.id AS joke_id, 
            jokedata.datesubmitted AS datesubmitted,
            jokedata.joketitle AS joke_title, 
            -- average is 0 if count or sum is null
            IFNULL(SUM(ratings.rating)/COUNT(ratings.rating), 0) AS average
            FROM jokedata 
            -- return all rows from left table (jokedata), and all nulls for ratings
            -- data when there is no matching row in the right table (ratings)
            LEFT JOIN ratings ON ratings.content_type = 'joke' AND jokedata.id = ratings.relative_id 
            WHERE jokecategory = '$cur_category'
            GROUP BY jokedata.id
            ORDER BY average desc
            LIMIT $offset, $jokes_per_page
            ");

Левое объединение вернет все результаты из jokedata и просто вернет все пустые значения для оценок для каждой строки, где условие соединения не выполнено.

0 голосов
/ 09 апреля 2009

Мне кажется, что использование среднего значения немного несправедливо. Если шутка A имеет 1 рейтинг 5, а шутка B имеет 25 оценок 4, то шутка A будет иметь преимущество над шуткой B. Это дает непопулярным шуткам больше преимуществ для более высокого рейтинга.

Я бы предложил связать вес с рейтингами, а затем ранжировать по весу. Например, по шкале от 1 до 5, 1 получит -2, 2 получит -5, 3 - 0, 4 - +.5, а 5 - +2. Таким образом, это позволит шутке с рейтингом 5 «4» быть выше, чем шутка с рейтингом 1 «5».

Шкале от -2 до +2 может потребоваться доработка, но, надеюсь, вы понимаете мою точку зрения.

...