简体   繁体   中英

Get average rating for individual shop using mysql

I am joining two table ie shop table with rating table and want to fetch all shops list with individual shop rating, I have wriiten query for this but in output getting only those shops which have rating in rating table, but I want if shop don't have rating then show 0 else show as per table record.

shop table:-

id  shop name      
  1     shop_1      
  2     shop_2      
  3     shop_3      
  4     shop_4

rating table

id  shop_id  rating      
  1     1      3      
  2     4      2

Query:

$this->db->select('shop.*,shop.id as shop_id');
    $this->db->select('COALESCE(ROUND(AVG(rat.rating),1),0) as avgRate');
    $this->db->from('shop');
    $this->db->join('rating_reviews as rat', 'rat.shop=shop.id', 'left');
    $this->db->get()->result_array();

current output:

  id  shop_id      avgRate       
   1     1            3      
   2     4            2 

Expected output:

 id  shop_id      avgRate     
   1     1            3     
   2     2            0           //(no rating given for this shop)    
   3     3            0           //(no rating given for this shop)       
   4     4            2 

Aggregate functions

The reason you're running into an error is that functions like AVG | COUNT AVG | COUNT are aggregate functions that is yo say that they aggregate all of the data and typically output a single record/result.

For example:

  • Lets assume you have a table shops with id as the Primary Key
  • There are 35 shops in the table
  • There are no breaks in the Primary Key (eg it goes from 1 to 35)

The following queries will then return 1 row/result:

# Query                          # Output:  # FIELD_1 # FIELD_2 (second statement)
SELECT COUNT(shop.id) FROM shop             # 35
SELECT shop.id, COUNT(shop.id) FROM shop    # 1       # 35

Both of the above statements would return 1 result. The first simply the count for the number of shops ( 35 ) and the second would additionally output the id of the first shop ( 1 ).

Your query functions on the same princiiple, like COUNT the AVG function is an aggreagte function and will return 1 result/row from the query.

SELECT 
    shop.*, shop.id as shop_id,
    COALESCE(ROUND(AVG(rat.rating),1),0) as avgRate
FROM shop
    LEFT JOIN rating_reviews AS rat ON rat.shop=shop.id

-- Outputs: All fields from `shop` for the first shop followed by [shop_id] and [avgRate]
-- For example...
# shop.id # shop.name # shop.type   # shop_id # avgRate
# 1       # Tesco     # Supermarket # 1       # 3.5

There are however two ways to circumvent this behaviour:

Using GROUP BY

SELECT i, AVG(...) AS a FROM ... LEFT JOIN ... GROUP BY i

Nested SELECT statements

SELECT i, (SELECT AVG(...) FROM ... WHERE ...) AS a FROM ...

GROUP BY

SELECT 
    shop.*, shop.id as shop_id,
    COALESCE(ROUND(AVG(rat.rating),1),0) as avgRate
FROM shop
    LEFT JOIN rating_reviews AS rat ON rat.shop=shop.id
GROUP BY shop.id

Nested SELECTS

SELECT
    shop.*, shop.id as shop_id,
    (SELECT
         COALESCE(ROUND(AVG(rat.rating),1),0)
     FROM rating_reviews as rat
     WHERE rat.shop=shop.id
    ) as avgRate
FROM shop

I would like to suggest another approach, avoiding the join completely:

select
  distinct id,
  (
     select coalesce(round(avg(rat.rating), 1), 0)
     from rating_reviews as rat where shop.id=rat.shop
  ) as avgRate
from shop

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM