Проектирование и запрос продукта / Система обзора

Я создал систему продукта / обзора с нуля, и мне трудно выполнить следующий запрос в SQL Server.

В моей схеме есть разные таблицы: товары, обзоры, категории, фотографии товаров и марки. Я должен запросить их все, чтобы найти название бренда и категории, фотографии, средний рейтинг и количество отзывов.

Мне трудно получить количество отзывов и средний рейтинг.

Отзывы могут быть скрытыми (пользователь удалил) или заблокирован (ожидает модерации). В моей таблице товаров нет столбцов "Количество отзывов" или "Средний рейтинг", поэтому мне нужно посчитать их по этому запросу, но не считать заблокированные и скрытые (r.bloqueado=0 и r.hidden=0).

У меня есть запрос ниже, но он считает заблокированные и скрытые. Если я раскомментирую часть "and r.bloqueado=0 and r.hidden=0", я получу правильный подсчет, но тогда он не покажет товары, у которых есть 0 отзывов (что мне нужно!).

select top 20 
    p.id, p.brand, m.nome, c.name, 
    count(r.product) AS NoReviews, Avg(r.nota) AS AvgRating, 
    f.id as cod_foto,f.nome as nome_foto 
from
    tblBrands AS m 
inner join 
    (tblProducts AS p 
left join 
    tblProductsReviews AS r ON p.id = r.product) ON p.brand = m.id 
left join 
    tblProductsCategorias as c on p.categoria = c.id 
left join 
    (select 
         id_product, id, nome 
     from 
         tblProductsFotos O 
     where 
         id = (SELECT min(I.id) 
               FROM tblProductsFotos I 
               WHERE I.id_product = O.id_product)) as f on p.id = f.id_product 
where 
    p.bloqueado = 0
    //Problem - and r.bloqueado=0 and r.hidden=0    
group by 
    p.id, p.brand, p.modalidade, m.nome, c.name, f.id,f.nome"

Нужен ваш совет:

Я видел другие системы, которые имеют средний рейтинг и количество отзывов в таблице продуктов. Это очень помогло бы в сложности этого запроса (возможно, также и в производительности), но тогда мне придется делать дополнительные запросы в каждом новом обзоре, заблокированных и скрытых действиях. Я могу легко к этому. Учитывая, что включает и обновления происходит гораздо меньше, чем показ продуктов, это звучит хорошо. Было бы лучше сделать это?

Или лучше найти способ исправить этот запрос? Можете ли вы помочь мне найти решение?

Спасибо

2 ответа

Потерять записи очень легко, если объединить предложение where с внешним соединением. Строки, которые не существуют во внешней таблице, возвращаются как NULL. Ваш фильтр случайно исключил эти нули.

Вот пример, который демонстрирует, что происходит:

/* Sample data.
 * There are two tables: product and review.
 * There are two products: 1 & 2.
 * Only product 1 has a review.
 */
DECLARE @Product TABLE
    (
        ProductId   INT
    )
;

DECLARE @Review TABLE
    (
        ReviewId    INT,
        ProductId   INT,
        Blocked     BIT
    )
;

INSERT INTO @Product
    (
        ProductId
    )
VALUES
    (1),
    (2)
;

INSERT INTO @Review
    (
        ReviewId,
        ProductId,
        Blocked
    )
VALUES
    (1, 1, 0)
;

Внешнее объединение таблиц без предложения where возвращает:

запрос

-- No where.
SELECT
    p.ProductId,
    r.ReviewId,
    r.Blocked
FROM
    @Product AS p   
        LEFT OUTER JOIN @Review AS r        ON r.ProductId = p.ProductId
;

Результат

ProductId   ReviewId    Blocked
1           1           0
2           NULL        NULL

Фильтрация для Blocked = 0 удалил бы вторую запись, и поэтому ProductId 2. Вместо этого:

-- With where.
SELECT
    p.ProductId,
    r.ReviewId,
    r.Blocked
FROM
    @Product AS p   
        LEFT OUTER JOIN @Review AS r        ON r.ProductId = p.ProductId
WHERE
    r.Blocked = 0
    OR r.Blocked IS NULL
;

Этот запрос сохраняет значение NULL, и ProductId 2. Ваш пример немного сложнее, потому что у вас есть два поля.

SELECT
    ...
WHERE
    (
        Blocked = 0
        AND Hidden = 0
    )
    OR Blocked IS NULL
;

Вам не нужно проверять оба поля на NULL, так как они отображаются в одной таблице.

Для подсчета количества товаров вы можете использовать case, когда и sum присваивая 1 там значение не r.bloqueado=0 или r.hidden=0 и 0 для этих значений (так что вы можете избежать фильтра где)

  "select top 20 p.id, p.brand, m.nome, c.name, sum(
                              case  when  r.bloqueado=0 then 0 
                                    when  r.hidden=0 then 0 
                                    else 1 
                              end )  AS NoReviews, 
  Avg(r.nota) AS AvgRating, f.id as cod_foto,f.nome as nome_foto 
  from tblBrands AS m 
  inner join (tblProducts AS p 
  left join tblProductsReviews AS r ON p.id=r.product ) ON p.brand = m.id 
  left join tblProductsCategorias as c on p.categoria=c.id 
  left join (select id_product,id,nome from tblProductsFotos O 
  where id = (SELECT min(I.id) FROM tblProductsFotos I 
            WHERE I.id_product = O.id_product)) as f on p.id = f.id_product where p.bloqueado=0
  group by p.id, p.brand, p.modalidade, m.nome, c.name, f.id,f.nome"

для AVG может быть, вы можете сделать что-то подобное

Другие вопросы по тегам