Set columns to null or empty in JOIN query - mysql

Order Table
ORDER_ID|ORDER_DT
1 |12-12-2016
Product Table
PRODUCT ID | ORDER ID
1 | 1
2 | 1
using SELECT * FROM order o LEFT JOIN product p ON o.order_id = p.order_id;
results
PRODUCT ID | ORDER ID|ORDER_DT
1 | 1 |12-12-2016
2 | 1 |12-12-2016
3 | 1 |12-12-2016
but I need to have this kind of results,noticed that I have still the same nos. of rows but duplicates in order table columns was emptied.
PRODUCT ID | ORDER ID|ORDER_DT
1 | 1 |12-12-2016
2 | |
3 | |

You can do it with temoorary variables, e.g.:
SELECT product_id, orders.order_id, order_dt, IF(#previous = orders.order_id, 0, orders.order_id) AS oid, #previous:= orders.order_id
FROM orders
LEFT JOIN products
ON orders.order_id = products.order_id,
(SELECT #previous := -1) a;
And wrap it into another SELECT query to get the required columns, e.g.:
SELECT r.product_id, IF(r.oid = 0, '', r.oid) AS order_id, r.order_dt
FROM (
SELECT product_id, orders.order_id, order_dt, IF(#previous = orders.order_id, 0, orders.order_id) AS oid, #previous:= orders.order_id
FROM orders
LEFT JOIN products
ON orders.order_id = products.order_id,
(SELECT #previous := -1) a
) r;
Here's the SQL Fiddle.

Related

SQL: Join with LIKE and min()

I'm using the query below to fetch the lowest priced row(s) matching the LIKE.
Suppliers
id | name
----------------------------------------
1 | sup1
2 | sup2
Prices
id | article_number | price | supplier_id
------------------------------------------------
1 | 12345678 | 312 | 1
2 | 12345678 | 219 | 2
3 | 87654321 | 312 | 1
select prices.* from prices
inner join
(
select min(price) as price,
article_number as article_number from prices group by article_number
)
min_prices on prices.price = min_prices.price
and prices.article_number = min_prices.article_number
WHERE prices.article_number LIKE '".$q."%'
Although now also want to fetch the suppliers.name from Suppliers-table:
select prices.*, suppliers.name from prices, suppliers
inner join
(
select min(price) as price,
prices.article_number as article_number from prices group by prices.article_number
)
min_prices on price = min_prices.price
and article_number = min_prices.article_number
WHERE
prices.article_number LIKE '".$q."%' AND
prices.supplier_id = suppliers.id"
This returns 18-times the amount of rows it is supposed to...?
Also the table consists of 10+ millions rows, so efficiency is very important.
For performance, add index on article_number, and use prices.article_number LIKE '".$q."%' in sub query. You'd better use JOIN not , (this is old way) to combine two tables. Like this:
select prices.*, suppliers.name
from prices
inner join (
select min(price) as price, prices.article_number as article_number
from prices
where prices.article_number like '".$q."%'
group by prices.article_number
) min_prices
on price = min_prices.price
and article_number = min_prices.article_number
inner join suppliers
on prices.supplier_id = suppliers.id
where prices.article_number like '".$q."%'

MySQL: Getting the MIN and the MAX of a table and both of their titles

I have a large database of products. It has a one to many relationship to another table of prices. I can easily get, with one query, the MIN, MAX and AVG of a particular category.
SELECT
MIN(gbp.price) AS min,
ROUND(AVG(gbp.price),2) AS ave,
MAX(gbp.price) AS max
FROM sku AS s
INNER JOIN price gbp ON gbp.sid = s.id
However, I also want to be able to get the title of the product it relates to as well - I cannot get this resolved despite multiple searches and rewrites.
My data is similar to...
prod_id | title
===============
1 | prod1
2 | prod2
3 | prod3
4 | prod4
5 | prod5
6 | prod6
7 | prod7
price_id | prod_id | price | price_date
=======================================
1 | 1 | 2.99 | 2015/02/01
2 | 1 | 3.99 | 2015/02/12
3 | 2 | 12.99 | 2015/02/01
4 | 3 | 15.99 | 2015/02/01
5 | 4 | 29.99 | 2015/02/01
6 | 5 | 29.99 | 2015/02/01
7 | 5 | 24.99 | 2015/02/12
8 | 6 | 2.99 | 2015/02/01
9 | 7 | 99.99 | 2015/02/01
10 | 7 | 89.99 | 2015/02/12
I am going to presume that other people may want a query writing similar to this, so I am going to ask for two answers.
First one "simply" to return this...
min | min_title | ave | max | max_title
============================================
2.99 | prod1 | 31.39 | 99.99 | prod7
However, the real answer I want (despite the fact I cannot even solve the above) is where it gets even trickier.
The actual results I want is in the table below...
min | min_title | ave | max | max_title
============================================
2.99 | prod6 | 25.85 | 89.99 | prod7
The min is 2.99 for prod6 as the 2.99 price for prod1 has expired.
The max is 89.99 for prod7 as the 99.99 price for prod7 has expired.
The ave is 25.85 because of above and because the price for prod5 is 24.99.
I am not expecting answers for everything, just answering the first question (in bold) will likely lead me to the answer for the second part (as I have similar queries that get the latest price etc).
SELECT t1.min, s.title AS min_title, t1.ave, t1.max, s2.title AS max_title
FROM (SELECT
MIN(gbp.price) AS min,
ROUND(AVG(gbp.price),2) AS ave,
MAX(gbp.price) AS max
FROM sku AS s
INNER JOIN price gbp ON (gbp.sid = s.id)
) t1
INNER JOIN (SELECT gbp.price, MAX(gbp.prod_id) AS MaxProdID
FROM price gbp
WHERE NOT EXISTS(
SELECT p2.price_id
FROM price p2
WHERE p2.price_id > gbp.price_id
AND p2.prod_id = gpb.prod_id
)
GROUP BY gbp.price
) minprice ON (minprice.price = t1.min)
INNER JOIN sku s ON (s.id = minprice.MaxProdID)
INNER JOIN (SELECT gbp.price, MAX(gbp.prod_id) AS MaxProdID
FROM price gbp
WHERE NOT EXISTS(
SELECT p2.price_id
FROM price p2
WHERE p2.price_id > gbp.price_id
AND p2.prod_id = gpb.prod_id
)
GROUP BY gbp.price
) maxprice ON (maxprice.price = t1.max)
INNER JOIN sku s2 ON (s2.id = maxprice.MaxProdID)
To solve your first output just use join to get those values:
SELECT min, mint.title, ave, max, maxt.title
FROM (
SELECT
MIN(gbp.price) AS min,
ROUND(AVG(gbp.price),2) AS ave,
MAX(gbp.price) AS max
FROM (SELECT price
FROM price AS gbp
INNER JOIN sku s2 ON gbp.sid = s2.id
ORDER BY prdate DESC
LIMIT 0, 1) AS s
INNER JOIN price gbp ON gbp.sid = s.id
) inq
JOIN price minp ON inq.min = minp.price
JOIN price maxp on inq.max = maxp.price
JOIN prod mint ON minp.prod_id = mint.prod_id
JOIN prod maxt ON maxp.prod_id = maxt.prod_id
I don't understand the rules for your second output.
This is essentially two different queries (or three if you count the average). The cross join is just horizontally splicing together the two results for min and max. They could all obviously be separated and executed individually.
with current_prices as (
select price_id, prod_id, price
from prices
where price_date = (
select max(price_date)
from prices as prices2
where prices2.prod_id = prices.prod_id
)
),
min_current_prices as (
select price, min(prod_id) as prod_d /* arbitrary selected representative */
from current_prices
where price = (
select min(price)
from current_prices
)
group by price
),
max_current_prices as (
select price, min(prod_id) as prod_id /* arbitrary selected representative */
from current_prices
where price = (
select max(price)
from current_prices
)
group by price
)
select
m1.price, prod1.title,
(select avg(price) from current_prices) as ave,
m2.price, prod2.title
from
min_current_prices as m1 inner join products as prod1 on prod1.prod_id = m1.prod_id
max_current_prices as m2 inner join products as prod2 on prod2.prod_id = m2.prod_id
I feel that this seems too complicated and yet you're asking for something very unusual. There clearly could be products with the same min/max price so this is going to cause problems when there is more than one at either end.
If your platform doesn't support WITH then just substitute the full query instead:
select
min_current_price.price as min_price, min_prod.title as min_title,
(
select avg(price)
from prices
where price_date = (
select max(price_date)
from prices as prices2
where prices2.prod_id = prices.prod_id
)
) as ave,
max_current_price.price as max_price, max_prod.title as max_title
from
(
select price, min(prod_id) as prod_id /* arbitrarily selected representative */
from (
select *
from prices
where price_date = (
select max(price_date)
from prices as prices2
where prices2.prod_id = prices.prod_id
)
) as current_prices
where price = (
select min(price)
from prices
where price_date = (
select max(price_date)
from prices as prices2
where prices2.prod_id = prices.prod_id
)
)
group by price
) as min_current_price
cross join
(
select price, min(prod_id) as prod_id /* arbitrarily selected representative */
from (
select *
from prices
where price_date = (
select max(price_date)
from prices as prices2
where prices2.prod_id = prices.prod_id
)
) as current_prices
where price = (
select max(price)
from prices
where price_date = (
select max(price_date)
from prices as prices2
where prices2.prod_id = prices.prod_id
)
)
group by price
) as max_current_price
inner join products as min_prod on min_prod.prod_id = min_current_price.prod_id
inner join products as max_prod on max_prod.prod_id = max_current_price.
Here's a hack for doing it in mysql using limits and sorting methods:
select
minprice.price as min_price, minprod.title as min_title,
(
select avg(price)
from prices
where price_date = (
select max(price_date)
from prices as prices2
where prices2.prod_id = prices.prod_id
)
) as ave,
maxprice.price as max_price, maxprod.title as max_title
from
(
select price_id, price, prod_id
from prices
where not exists ( /* another way of excluding expired prices */
select 1 from prices as p2
where p2.prod_id = prices.prod_id and p2.price_date > prices.prod_id
)
order by price asc
limit 0, 1
) as minprice,
(
select price_id, price, prod_id
from prices
where not exists (
select 1 from prices as p2
where p2.prod_id = prices.prod_id and p2.price_date > prices.prod_id
)
order by price desc
limit 0, 1
) as maxprice
inner join prod as minprod on minprod.prod_id = minprice.prod_id
inner join prod as maxprod on min.prod_id = maxprice.prod_id

MySQL INNER JOIN from second table (TOP10)

$stmt = $conn->prepare('SELECT a.*, c.*, SUM(a.money+b.RESULT) AS ARESULT
FROM users a
INNER JOIN bankaccounts c
ON a.id = c.owner
INNER JOIN
(
SELECT owner, SUM(amount) AS RESULT
FROM bankaccounts
GROUP BY owner
) b ON a.id = b.owner
ORDER BY ARESULT DESC LIMIT 10');
What's problem, it show wrong only one record? I want list max 10 records - like TOP 10 richest who has [money+(all his bankaccounts amount)]
Lets say.. I have 2 tables.
Table: users
ID | username | money
1 | richman | 500
2 | richman2 | 600
Table: bankaccounts
ID | owner | amount
65 | 1 | 50
68 | 1 | 50
29 | 2 | 400
So it would list:
richman2 1000$
richman 600$
Try using a subqueries...
$stmt = $conn->prepare('SELECT a.*,
IFNULL((SELECT SUM(amount) FROM bankaccounts b WHERE b.owner=a.id),0) AS BANK_MONEY,
(IFNULL(a.money,0) + IFNULL((SELECT SUM(amount) FROM bankaccounts c WHERE c.owner=a.id),0)) AS ARESULT
FROM users a
ORDER BY ARESULT DESC LIMIT 0, 10');
EDIT: Added a field for bank account totals
EDIT2: Added IFNULL to SQL statement in case user is not in BankAccounts table
Try this:
SELECT a.*, (a.money + b.RESULT) AS ARESULT
FROM users a
INNER JOIN (SELECT owner, SUM(amount) AS RESULT
FROM bankaccounts
GROUP BY owner
) b ON a.id = b.owner
ORDER BY ARESULT DESC
LIMIT 10

Unknown column in mysql subquery

I am trying to get the avg of an item so I am using a subquery.
Update: I should have been clearer initially, but i want the avg to be for the last 5 items only
First I started with
SELECT
y.id
FROM (
SELECT *
FROM (
SELECT *
FROM products
WHERE itemid=1
) x
ORDER BY id DESC
LIMIT 15
) y;
Which runs but is fairly useless as it just shows me the ids.
I then added in the below
SELECT
y.id,
(SELECT AVG(deposit) FROM (SELECT deposit FROM products WHERE id < y.id ORDER BY id DESC LIMIT 5)z) AVGDEPOSIT
FROM (
SELECT *
FROM (
SELECT *
FROM products
WHERE itemid=1
) x
ORDER BY id DESC
LIMIT 15
) y;
When I do this I get the error Unknown column 'y.id' in 'where clause', upon further reading here I believe this is because when the queries go down to the next level they need to be joined?
So I tried the below ** removed un needed suquery
SELECT
y.id,
(SELECT AVG(deposit) FROM (
SELECT deposit
FROM products
INNER JOIN y as yy ON products.id = yy.id
WHERE id < yy.id
ORDER BY id DESC
LIMIT 5)z
) AVGDEPOSIT
FROM (
SELECT *
FROM products
WHERE itemid=1
ORDER BY id DESC
LIMIT 15
) y;
But I get Table 'test.y' doesn't exist. Am I on the right track here? What do I need to change to get what I am after here?
The example can be found here in sqlfiddle.
CREATE TABLE products
(`id` int, `itemid` int, `deposit` int);
INSERT INTO products
(`id`, `itemid`, `deposit`)
VALUES
(1, 1, 50),
(2, 1, 75),
(3, 1, 90),
(4, 1, 80),
(5, 1, 100),
(6, 1, 75),
(7, 1, 75),
(8, 1, 90),
(9, 1, 90),
(10, 1, 100);
Given my data in this example, my expected result is below, where there is a column next to each ID that has the avg of the previous 5 deposits.
id | AVGDEPOSIT
10 | 86 (deposit value of (id9+id8+id7+id6+id5)/5) to get the AVG
9 | 84
8 | 84
7 | 84
6 | 79
5 | 73.75
I'm not an MySQL expert (in MS SQL it could be done easier), and your question looks a bit unclear for me, but it looks like you're trying to get average of previous 5 items.
If you have Id without gaps, it's easy:
select
p.id,
(
select avg(t.deposit)
from products as t
where t.itemid = 1 and t.id >= p.id - 5 and t.id < p.id
) as avgdeposit
from products as p
where p.itemid = 1
order by p.id desc
limit 15
If not, then I've tri tried to do this query like this
select
p.id,
(
select avg(t.deposit)
from (
select tt.deposit
from products as tt
where tt.itemid = 1 and tt.id < p.id
order by tt.id desc
limit 5
) as t
) as avgdeposit
from products as p
where p.itemid = 1
order by p.id desc
limit 15
But I've got exception Unknown column 'p.id' in 'where clause'. Looks like MySQL cannot handle 2 levels of nesting of subqueries.
But you can get 5 previous items with offset, like this:
select
p.id,
(
select avg(t.deposit)
from products as t
where t.itemid = 1 and t.id > coalesce(p.prev_id, -1) and t.id < p.id
) as avgdeposit
from
(
select
p.id,
(
select tt.id
from products as tt
where tt.itemid = 1 and tt.id <= p.id
order by tt.id desc
limit 1 offset 6
) as prev_id
from products as p
where p.itemid = 1
order by p.id desc
limit 15
) as p
sql fiddle demo
This is my solution. It is easy to understand how it works, but at the same time it can't be optimized much since I'm using some string functions, and it's far from standard SQL. If you only need to return a few records, it could be still fine.
This query will return, for every ID, a comma separated list of previous ID, ordered in ascending order:
SELECT p1.id, p1.itemid, GROUP_CONCAT(p2.id ORDER BY p2.id DESC) previous_ids
FROM
products p1 LEFT JOIN products p2
ON p1.itemid=p2.itemid AND p1.id>p2.id
GROUP BY
p1.id, p1.itemid
ORDER BY
p1.itemid ASC, p1.id DESC
and it will return something like this:
| ID | ITEMID | PREVIOUS_IDS |
|----|--------|-------------------|
| 10 | 1 | 9,8,7,6,5,4,3,2,1 |
| 9 | 1 | 8,7,6,5,4,3,2,1 |
| 8 | 1 | 7,6,5,4,3,2,1 |
| 7 | 1 | 6,5,4,3,2,1 |
| 6 | 1 | 5,4,3,2,1 |
| 5 | 1 | 4,3,2,1 |
| 4 | 1 | 3,2,1 |
| 3 | 1 | 2,1 |
| 2 | 1 | 1 |
| 1 | 1 | (null) |
then we can join the result of this query with the products table itself, and on the join condition we can use FIND_IN_SET(src, csvalues) that return the position of the src string inside the comma separated values:
ON FIND_IN_SET(id, previous_ids) BETWEEN 1 AND 5
and the final query looks like this:
SELECT
list_previous.id,
AVG(products.deposit)
FROM (
SELECT p1.id, p1.itemid, GROUP_CONCAT(p2.id ORDER BY p2.id DESC) previous_ids
FROM
products p1 INNER JOIN products p2
ON p1.itemid=p2.itemid AND p1.id>p2.id
GROUP BY
p1.id, p1.itemid
) list_previous LEFT JOIN products
ON list_previous.itemid=products.itemid
AND FIND_IN_SET(products.id, previous_ids) BETWEEN 1 AND 5
GROUP BY
list_previous.id
ORDER BY
id DESC
Please see fiddle here. I won't recommend using this trick for big tables, but for small sets of data it is fine.
This is maybe not the simplest solution, but it does do the job and is an interesting variation and in my opinion transparent. I simulate the analytical functions that I know from Oracle.
As we do not assume the id to be consecutive the counting of the rows is simulated by increasing #rn each row. Next products table including the rownum is joint with itself and only the rows 2-6 are used to build the average.
select p2id, avg(deposit), group_concat(p1id order by p1id desc), group_concat(deposit order by p1id desc)
from ( select p2.id p2id, p1.rn p1rn, p1.deposit, p2.rn p2rn, p1.id p1id
from (select p.*,#rn1:=#rn1+1 as rn from products p,(select #rn1 := 0) r) p1
, (select p.*,#rn2:=#rn2+1 as rn from products p,(select #rn2 := 0) r) p2 ) r
where p2rn-p1rn between 1 and 5
group by p2id
order by p2id desc
;
Result:
+------+--------------+---------------------------------------+------------------------------------------+
| p2id | avg(deposit) | group_concat(p1id order by p1id desc) | group_concat(deposit order by p1id desc) |
+------+--------------+---------------------------------------+------------------------------------------+
| 10 | 86.0000 | 9,8,7,6,5 | 90,90,75,75,100 |
| 9 | 84.0000 | 8,7,6,5,4 | 90,75,75,100,80 |
| 8 | 84.0000 | 7,6,5,4,3 | 75,75,100,80,90 |
| 7 | 84.0000 | 6,5,4,3,2 | 75,100,80,90,75 |
| 6 | 79.0000 | 5,4,3,2,1 | 100,80,90,75,50 |
| 5 | 73.7500 | 4,3,2,1 | 80,90,75,50 |
| 4 | 71.6667 | 3,2,1 | 90,75,50 |
| 3 | 62.5000 | 2,1 | 75,50 |
| 2 | 50.0000 | 1 | 50 |
+------+--------------+---------------------------------------+------------------------------------------+
SQL Fiddle Demo: http://sqlfiddle.com/#!2/c13bc/129
I want to thank this answer on how to simulate analytical functions in mysql: MySQL get row position in ORDER BY
It looks like you just want:
SELECT
id,
(SELECT AVG(deposit)
FROM (
SELECT deposit
FROM products
ORDER BY id DESC
LIMIT 5) last5
) avgdeposit
FROM products
The inner query gets the last 5 rows added to product, the query that wraps that gets the average for their deposits.
I'm going to simplify your query a bit so I can explain it.
SELECT
y.id,
(
SELECT AVG(deposit) FROM
(
SELECT deposit
FROM products
LIMIT 5
) z
) AVGDEPOSIT
FROM
(
SELECT *
FROM
(
SELECT *
FROM products
) x
LIMIT 15
) y;
My guess would be that you just need to insert some AS keywords in there. I'm sure someone else will come up with something more elegant, but for now you can try it out.
SELECT
y.id,
(
SELECT AVG(deposit) FROM
(
SELECT deposit
FROM products
LIMIT 5
) z
) AS AVGDEPOSIT
FROM
(
SELECT *
FROM
(
SELECT *
FROM products
) AS x
LIMIT 15
) y;
Here's one way to do it in MySQL:
SELECT p.id
, ( SELECT AVG(deposit)
FROM ( SELECT #rownum:=#rownum+1 rn, deposit, id
FROM ( SELECT #rownum:=0 ) r
, products
ORDER BY id ) t
WHERE rn BETWEEN p.rn-5 AND p.rn-1 ) avgdeposit
FROM ( SELECT #rownum1:=#rownum1+1 rn, id
FROM ( SELECT #rownum1:=0 ) r
, products
ORDER BY id ) p
WHERE p.rn >= 5
ORDER BY p.rn DESC;
It's a shame MySQL doesn't support the WITH clause or windowing functions. Having both would greatly simplify the query to the following:
WITH tbl AS (
SELECT id, deposit, ROW_NUMBER() OVER(ORDER BY id) rn
FROM products
)
SELECT id
, ( SELECT AVG(deposit)
FROM tbl
WHERE rn BETWEEN t.rn-5 AND t.rn-1 )
FROM tbl t
WHERE rn >= 5
ORDER BY rn DESC;
The latter query runs fine in Postgres.
2 possible solutions here
Firstly using user variables to add a sequence number. Do this twice, and join the second set to the first where the sequence number is between the id - 1 and the id - 5. Then just use AVG. No correlated sub queries.
SELECT Sub3.id, Sub3.itemid, Sub3.deposit, AVG(Sub4.deposit)
FROM
(
SELECT Sub1.id, Sub1.itemid, Sub1.deposit, #Seq:=#Seq+1 AS Sequence
FROM
(
SELECT id, itemid, deposit
FROM products
ORDER BY id DESC
) Sub1
CROSS JOIN
(
SELECT #Seq:=0
) Sub2
) Sub3
LEFT OUTER JOIN
(
SELECT Sub1.id, Sub1.itemid, Sub1.deposit, #Seq1:=#Seq1+1 AS Sequence
FROM
(
SELECT id, itemid, deposit
FROM products
ORDER BY id DESC
) Sub1
CROSS JOIN
(
SELECT #Seq1:=0
) Sub2
) Sub4
ON Sub4.Sequence BETWEEN Sub3.Sequence + 1 AND Sub3.Sequence + 5
GROUP BY Sub3.id, Sub3.itemid, Sub3.deposit
ORDER BY Sub3.id DESC
Second one is cruder, and uses a correlated sub query (which is likely to perform poorly as the amount of data increases). Does a normal select but for the last column it has a sub query that refers to the id in the main select.
SELECT id, itemid, deposit, (SELECT AVG(P2.deposit) FROM products P2 WHERE P2.id BETWEEN P1.id - 5 AND p1.id - 1 ORDER BY id DESC LIMIT 5)
FROM products P1
ORDER BY id DESC
Is this what you are after?
SELECT m.id
, AVG(d.deposit)
FROM products m
, products d
WHERE d.id < m.id
AND d.id >= m.id - 5
GROUP BY m.id
ORDER BY m.id DESC
;
But can't be that simple. Firstly, the table cannot just contain one itemid (hence your WHERE clause); Second, the id cannot be sequential/without gaps within an itemid. Thirdly, you probably want to produce something that runs across itemid and not one itemid at a time. So here it is.
SELECT itemid
, m_id as id
, AVG(d.deposit) as deposit
FROM (
SELECT itemid
, m_id
, d_id
, d.deposit
, #seq := (CASE WHEN m_id = d_id THEN 0 ELSE #seq + 1 END) seq
FROM (
SELECT m.itemid
, m.id m_id
, d.id d_id
, d.deposit
FROM products m
, products d
WHERE m.itemid = d.itemid
AND d.id <= m.id
ORDER BY m.id DESC
, d.id DESC) d
, (SELECT #seq := 0) s
) d
WHERE seq BETWEEN 1 AND 5
GROUP BY itemid
, m_id
ORDER BY itemid
, m_id DESC
;

MySQL - SUM/COUNT Different Columns From Different Tables

I have a peculiar problem. Given three tables.
product
- id
- title
product_rating
- product_id
- rating
product_view
- product_id
I want to grab products with a SUM of their ratings (which will either be a +1 or -1 value) and their total product count.
SELECT
p.`id`, p.`title`,
SUM( pr.`rating` ) AS rating,
COUNT( pv.`product_id` ) AS views
FROM
`product` AS p
LEFT JOIN `product_rating` AS pr ON ( pr.`product_id` = p.`id` )
LEFT JOIN `product_view` AS pv ON ( pv.`product_id` = p.`id` )
GROUP BY
p.`id`
ORDER BY rating DESC
In the table I have 1 rating, 9 views. The query, however, is returning 9 rating, 9 views. I understand why it's happening (it's summing the rating for each product_view), but I don't know how to get around it.
Any suggestions would be greatly appreciated.
Sample data:
product
------------
id | title
1 | Globber
product_rating
-------------------
product_id | rating
1 | 1
product_view
------------
product_id
1
1
1
1
1
1
1
1
1
Try
SELECT p.id, p.title, r.rating, v.views
FROM product p LEFT JOIN
(
SELECT product_id, SUM(rating) rating
FROM product_rating
GROUP BY product_id
) r ON p.id = r.product_id LEFT JOIN
(
SELECT product_id, COUNT(*) views
FROM product_view
GROUP BY product_id
) v ON p.id = v.product_id
ORDER BY r.rating DESC
Sample output:
| ID | TITLE | RATING | VIEWS |
---------------------------------
| 1 | Globber | 1 | 9 |
Here is SQLFiddle demo
How to do that?
SELECT tbl.pid,tbl.ptitle, SUM(tbl.rating) as Rate, COUNT (tbl.views) as ViewList FROM (SELECT
p.`id` as pid, p.`title` as ptitle,
pr.`rating` AS rating,
pv.`product_id` AS views
FROM
`product` AS p
LEFT JOIN `product_rating` AS pr ON ( pr.`product_id` = p.`id` )
LEFT JOIN `product_view` AS pv ON ( pv.`product_id` = p.`id` ) ) as tbl
GROUP BY
tbl.`pid`
ORDER BY tbl.Rate DESC