SQL query sum multiplied 2 times what it should be - mysql

When I run this query, the votes sum is 2 times what it should be (sum=6 instead of 3). Can someone figure out the fix for this?
SELECT sum(votes.vote) AS sum
, my_votes.vote IS NOT NULL AS did_i_vote
, votes.parent_id, subject
, review_date
, item_id
, review_summary
, review, full_name
, reputation
, profile_picture
, accounts.acct_id
FROM votes
RIGHT JOIN items_purchased
on votes.parent_id=items_purchased.purchase_id
JOIN accounts
ON items_purchased.purchaser_account_id=accounts.acct_id
JOIN items
on items_purchased.item_id=items.folder_id
LEFT JOIN votes AS my_votes
ON my_votes.parent_id=items_purchased.purchase_id
AND my_votes.acct_id='3'
AND my_votes.column_name='purchase_id'
WHERE purchase_id='2'
AND deleted_p!=1 and pub_priv_p!=1
GROUP BY items_purchased.purchase_id
I'm pretty sure it has to do with the JOINs because if I get rid of JOIN items on items_purchased.item_id=items.folder_id then the sum=3. However, I need that JOIN in there somehow.
Thoughts?

Without a schema we can't tell, but this is a guess:
Check all of your join conditions - you're likely missing a condition causing that set of results to be 'duplicated'.
For example, if I have a table
`Foo` with columns `A` `B` and `C` - A and B are the PK;
`Bar` with columns `A` `B` and `Z` - A and B are the PK;
`Biz` with columns `Z` `GOAL`
And I wanted to count the number of GOALS per A, if I joined the Foo to Bar just using A and not B as well, I would likely get an erroneous count.
The easiest way to see this is to do a SELECT * and remove the group by

Related

SUM mixed with LEFT JOIN and GROUP BY in MYSQL query

I am having some trouble with a query that involves sum, a left join and a group by
$sql = "
SELECT h.id AS hid
, SUM(l.likes) AS likes
FROM homes h
LEFT
JOIN likes l
ON l.homeid = h.id
GROUP
BY h.id
";
Instead of summing the likes for each home, it is giving NULL if the home has no likes or the number 8873 if it has one like. I really can't understand the 8873.
Of note, there are a lot of likes in this table for other things in which case the value for l.homeid is NULL. Could that be throwing things off?
Edit:
I added another like for a homeid from a different user and now it is giving me 8906 instead of 8873 for those with 1 like and 17812 for the one with two likes. This is very strange. The data type for all the numbers is int(11). I am going to create a totally new table and see if that one does the same thing. I'm also going to remove a unique index I added recently.
Try moving the aggregation into a derived table and joining then joining that:
SELECT
h.id AS hid
, COALESCE( l.likes, 0 ) AS likes
FROM homes h
LEFT JOIN (
SELECT
homeid
, COUNT( likes ) AS likes
FROM likes
GROUP BY
homeid
) AS l ON h.id = l.homeid
Also try COUNT() instead of SUM()

MySQL Compare Result in WHERE clause

I imagine I'm missing something pretty obvious here.
I'm trying to display a list of 'bookings' where the total charges is higher than the total payments for the booking. The charges and payments are stored in separate tables linked using foreign keys.
My query so far is:
SELECT `booking`.`id`,
SUM(`booking_charge`.`amount`) AS `charges`,
SUM(`booking_payment`.`amount`) AS `payments`
FROM `booking`
LEFT JOIN `booking_charge` ON `booking`.`id` = `booking_charge`.`booking_id`
LEFT JOIN `booking_payment` ON `booking`.`id` = `booking_payment`.`booking_id`
WHERE `charges` > `payments` ///this is the incorrect part
GROUP BY `booking`.`id`
My tables look something like this:
Booking (ID)
Booking_Charge (Booking_ID, Amount)
Booking_Payment (Booking_ID, Amount)
MySQL doesn't seem to like comparing the results from these two tables, I'm not sure what I'm missing but I'm sure it's something which would be possible.
try HAVING instead of WHERE like this
SELECT `booking`.`id`,
SUM(`booking_charge`.`amount`) AS `charges`,
SUM(`booking_payment`.`amount`) AS `payments`
FROM `booking`
LEFT JOIN `booking_charge` ON `booking`.`id` = `booking_charge`.`booking_id`
LEFT JOIN `booking_payment` ON `booking`.`id` = `booking_payment`.`booking_id`
GROUP BY `booking`.`id`
HAVING `charges` > `payments`
One of the problems with the query is the cross join between rows from `_charge` and rows from `_payment`. It's a semi-Cartesian join. Each row returned from `_charge` will be matched with each row returned from `_payment`, for a given `booking_id`.
Consider a simple example:
Let's put a single row in `_charge` for $40 for a particular `booking_id`.
And put two rows into `_payment` for $20 each, for the same `booking_id`.
The query will would return total charges of $80. (= 2 x $40). If there were instead five rows in \'_payment\' for $10 each, the query would return a total charges of $200 ( = 5 x $40)
There's a couple of approaches to addressing that issue. One approach is to do the aggregation in an inline view, and return the total of the charges and payments as a single row for each booking_id, and then join those to the booking table. With at most one row per booking_id, the cross join doesn't give rise to the problem of "duplicating" rows from _charge and/or _payment.
For example:
SELECT b.id
, IFNULL(c.amt,0) AS charges
, IFNULL(p.amt,0) AS payments
FROM booking b
LEFT
JOIN ( SELECT bc.booking_id
, SUM(bc.amount) AS amt
FROM booking_charge bc
GROUP BY bc.booking_id
) c
ON c.booking_id = b.id
LEFT
JOIN ( SELECT bp.booking_id
, SUM(bp.amount) AS amt
FROM booking_payment bp
GROUP BY bp.booking_id
) p
ON p.booking_id = b.id
WHERE IFNULL(c.amt,0) > IFNULL(p.amt,0)
We could make use of a HAVING clause, in place of the WHERE.
The query in this answer is not the only way to get the result, nor is it the most efficient. There are other query patterns that will return an equivalent result.

MYSQL Counting amount of rows (if exists) from another table

I'm sure this question makes no sense, sorry for that, best way I can explain it is visually.
My tables are:
category, comment, member and review.
I have a query which selects information from the 3 latest reviews
SELECT `reviewID` , `reviewTitle` , `reviewContent` , `reviewDate` , `gameRating` , `reviewImage` , `firstName` , `lastName` , `categoryName`
FROM member
INNER JOIN review
USING ( memberID )
INNER JOIN category
USING ( categoryID )
ORDER BY `reviewDate` DESC
LIMIT 3
result
Each review is assigned a reviewID, comments are also assigned a reviewID to determine which review the comment is for. I want to also count the amount of comments per review. Comments tables includes:
commentID reviewID memberID commentDate commentContent
I've tried
SELECT `reviewID`, `reviewTitle`, `reviewContent`, `reviewDate`, `gameRating`, `reviewImage`, `firstName`, `lastName`, `categoryName`, count(commentID) AS comments
FROM member INNER JOIN
review
USING (memberID) INNER JOIN
category
USING (categoryID) INNER JOIN
comment USING (reviewID)
ORDER BY `reviewDate` DESC
LIMIT 3
But it only gives this result
which is correct as that review has 2 comments, but the other 2 reviews have 0 comments so I assume it should just return null instead of not displaying the other reviews all together? Any help would be greatly appreciated.
You probably just need left joins:
SELECT member.*, count(commentID) AS comments
FROM member LEFT JOIN
review
USING (memberID) LEFT JOIN
category
USING (categoryID) LEFT JOIN
comment
USING (reviewID)
GROUP BY memberId
ORDER BY `reviewDate` DESC
LIMIT 3
You need a GROUP BY, but you should also remove all the non-member columns.

SQL query inner joins and limit to max 3 most recent

I have the following query:
SELECT * FROM `product` INNER JOIN `shop`
ON `product`.shop_id= `shop`.id;
I wanted to get all of the products from all the shops I have, but I wanted to get 3 products max from each shop. Is there a way to specify MAX on each joins?
Here's my product table:
Here's my shop table:
Try this:
SELECT *
FROM (SELECT *
FROM (SELECT *, IF(#shop = (#shop:=p.shop_id), #id:=#id + 1, #id := 1) temp
FROM `product` p, (SELECT #shop:=0, #id:=1) AS A
ORDER BY p.shop_id, p.updated DESC) AS B
WHERE temp <= 3) AS C
INNER JOIN `shop` s ON C.shop_id= s.id;
Query:
SELECT *
FROM `product` p
INNER JOIN `shop` s
ON `p`.shop_id= `s`.id
WHERE p.id IN (SELECT p2.id
FROM `product` p2
WHERE p2.shop_id = s.id
ORDER BY p2.updated DESC
LIMIT 3)
OR maybe:
SELECT *
FROM `product` p
INNER JOIN `shop` s
ON `p`.shop_id= `s`.id
WHERE EXISTS (SELECT p2.id
FROM `product` p2
WHERE p2.shop_id = s.id
ORDER BY p2.updated DESC
LIMIT 3)
Specifying limits within a subquery is a bit challenging in MySQL (not impossible, but a bit complicated).
If you just want the three most recent product ids for each shop, and you can live with them on one row, then you can use group_concat(). The query is much simpler:
SELECT shop.*,
substring_index(group_concat(product.id order by product.updated desc), ',', 3) as ThreeProducts
FROM `product` INNER JOIN
`shop`
ON `product`.shop_id= `shop`.id
group by shop.id;
The results will place the product ids in a single field like this: '1,2,3'.
It is important to know the tables definitions in terms of primary keys, foreign keys, etc to come up with a SQL to solve the problem. From the images it is not clear if product.id is unique or not. I suspect there is possibly a data model definition issue here.
If the tables are not normalized to a necessary extent, it will be very difficult (sometime not possible) to read appropriate data back.
A reasonably normalized tables should look like.
Product(id primary key, ....)
Shop(id primary key,....)
and a relation table say.
Shop_Product (shop_id references Shop(id), prod_id references Product(id), ...)
It will be helpful to help you out if you could send table definitions.
try to use limit in your code. It may work

MySql check oldest record against current record with nested queries

My head is spinning trying to figure out the SQL query I need to use. I have tried a variety of nested queries and self joins and everything is failing me. I assume I am WAY over thinking it.
Basic idea: I have a single table, let's call it OrderTable and there are four (relevant) fields in the table: OrderId, CustomerId, RestaurantId, OrderTime
When a customer places an order, the date/time stamp is added to the OrderTime field. The other three fields are just integers, with the OrderId field as the primary key (and auto-increment).
I am trying to write a query that will return a list of CustomerId records where the first record (earliest date) for that customer is a specific date (let's say '2012-03-14') and the RestaurantId is a specific number (let's say 29).
At the moment i have what i can only assume is an overly complicated way of doing it. Also, i currently get an error "#1242 - Subquery returns more than 1 row" when there is more than one record matching my subquery.
Can anyone help me with a more elegant solution? Thanks!
CURRENT QUERY:
SELECT `CustomerId`
FROM `OrderTable`
WHERE `OrderTime` LIKE '%2012-03-14%'
AND `RestaurantId`='29'
AND `OrderId`=(SELECT `OrderId`
FROM `OrderTable`
WHERE `RestaurantId`='29'
GROUP BY `CustomerId`
ORDER BY `OrderTime` ASC
)
EDIT: John Totet Woo was probably right on this one, but still refer to the second part of my post to avoid the LIKE clause :)
I might be slightly confused on what you're asking for, but if you change the subquery from '=' to IN, do you get what you're after?
SELECT `CustomerId`
FROM `OrderTable`
WHERE `OrderTime` LIKE '%2012-03-14%'
AND `RestaurantId`='29'
AND `OrderId` IN (SELECT `OrderId`
FROM `OrderTable`
WHERE `RestaurantId`='29'
GROUP BY `CustomerId`
ORDER BY `OrderTime` ASC
)
What was mostly bothering me though, is that you can use
AND DATE(OrderTime) = '2012-03-14'
Instead of the LIKE
I guess I'm confused. Why can't you just select all the records that equal your OrderTime and Restaurant ID like this?
SELECT * FROM OrderTable WHERE OrderTime = "2012-03-14" AND RestaurantID = "29";
Would that not give you the list you want?
Your current can return multiple values. The = sign need only one value from your subquery and in order to do that, you need to limit the values of your subquery by using the LIMIT keyword.
SELECT `CustomerId`
FROM `OrderTable`
WHERE `OrderTime` LIKE '%2012-03-14%' AND
`RestaurantId`='29' AND
`OrderId`= (
SELECT `OrderId`
FROM `OrderTable`
WHERE `RestaurantId`='29'
ORDER BY `OrderTime` ASC
LIMIT 1
)
you can also do it this way:
SELECT DISTINCT CustomerID
FROM OrderTable
WHERE OrderID IN
(
SELECT OrderID
FROM OrderTable
WHERE RestaurantID = 29 AND
DATE(OrderTime) = DATE(2012-03-14)
) b
the simpliest solution among all is this:
SELECT DISTINCT CustomerID
FROM OrderTable
WHERE RestaurantID = 29 AND
DATE(OrderTime) = DATE(2012-03-14)