views:

384

answers:

2

I'm sorry if this is really basic, but:

I feel at some point I didn't have this issue, and now I am, so either I was doing something totally different before or my syntax has skipped a step.

I have, for example, a query that I need to return all rows with certain data along with another column that has the total of one of those columns. If things worked as I expected them, it would look like:

 SELECT
 order_id,
 cost,
 part_id,
 SUM(cost) AS total
 FROM orders 
 WHERE order_date BETWEEN xxx AND yyy

And I would get all the rows with my orders, with the total tacked on to the end of each one. I know the total would be the same each time, but that's expected. Right now to get that to work I'm using:

 SELECT
 order_id,
 cost,
 part_id,
 (SELECT SUM(cost)
 FROM orders
 WHERE order_date BETWEEN xxx AND yyy) AS total
 FROM orders 
 WHERE order_date BETWEEN xxx AND yyy

Essentially running the same query twice, once for the total, once for the other data. But if I wanted, say, the SUM and, I dunno, the average cost, I'd then be doing the same query 3 times, and that seems really wrong, which is why I'm thinking I'm making some really basic mistake.

Any help is really appreciated.

+6  A: 

You need to use GROUP BY as such to get your desired result:

SELECT
order_id,
part_id,
SUM(cost) AS total
FROM orders 
WHERE order_date BETWEEN xxx AND yyy
GROUP BY order_id, part_id

This will group your results. Note that since I assume that order_id and part_id is a compound PK, SUM(cost) in the above will probably be = cost (since you a grouping by a combination of two fields which is guarantied to be unique. The correlated subquery below will overcome this limitation).

Any non-aggregate rows fetched needs to be specified in the GROUP BY row.

For more information, you can read a tutorial about GROUP BY here:

MySQL Tutorial - Group By


EDIT: If you want to use a column as both aggregate and non-aggregate, or if you need to desegregate your groups, you will need to use a subquery as such:

SELECT
or1.order_id,
or1.cost,
or1.part_id,
(
  SELECT SUM(cost)
  FROM orders or2
  WHERE or1.order_id = or2.order_id
  GROUP BY or2.order_id
) AS total
FROM orders or1
WHERE or1.order_date BETWEEN xxx AND yyy
Andrew Moore
You are grouping by the cost, so SUM(cost) won't work.
Roee Adler
**@Rax Olgud:** This was me copy and pasting from the original `SELECT` without checking the fields. The mistake has been corrected.
Andrew Moore
And you do have edit rights. No need to hijack an answer for a simple typo.
Andrew Moore
@Andrew: No hijacking intended, I apologize.
Roee Adler
**@Rax Olgud:** No problem... Just know for future reference... Unless the answer basis is totally wrong, if you have edit rights, it's usually regarded as bad etiquette to post an answer with the same content as someone else just on the basis of a typo.
Andrew Moore
@Andrew: I disagree about the definition of typo but I don't want to get into any war. I deleted my answer and +1 to you.
Roee Adler
**@Rax Olgud:** What I did is copy the original query, then the columns not used in aggregate functions to put inside the `GROUP BY` without noticing `cost` was one of them...
Andrew Moore
I think you both are awesome. So to be clear, I need to mention any and all non-aggregate columns in the GROUP BY? meaning if I have it returning 10 columns, and only 1 is aggregate, I need a : GROUP BY col1, col2, col3, col4, col5, col6, col7, col8, col9 ?
Anthony
**@Anthony:** Correct, and obviously, you cannot use an aggregate column as an non-aggregate in the same query. I'll modify my answer to include that possibility.
Andrew Moore
Thing is, the more columns you add, the more your groups will be segregated... So usually, grouping by 9 columns will give you the same results as no group at all...
Andrew Moore
I haven't had a chance to test this, but based on my initial understanding of GROUP BY, I think if I have a unique key (which I do in this case) and I group by that, the other columns should be fine, since each "group" is a unique row. I'll post results in a few minutes. Also, based on your edit, I think I see what part of my issue was. I was using the sub-query like in your edit, but was doing the full query again, not just querying for rows that match the outer-query. Good job!
Anthony
A: 

You can use UNION statements to get all these things in one query.

(SELECT myvar FROM table WHERE this=that)
UNION
(SELECT AVG(myvar) FROM table WHERE this=that)
UNION
(SELECT SUM(myvar) FROM table WHERE this=that)

this will return n+2 rows, the last two rows being the average of n and the sum of n.

tharkun
But this still requires me to express my query 3 times, correct?
Anthony
well, this is considere one query. and if you're doing this in your code then it is much better than writing three queries, connecting three times and retrieving three datasets. with the union you will send one query to the server and will get a dataset which contains everything you want.
tharkun
A Union would definitely shrink my results by some order of magnitude, but my original frustration was based on having to run three identical sub-queries (three SELECTS that all return the same results), but based on this and Andrew's answer, I think I understand how I can run my "actual" query for the first part, and use UNION to do the other two sub-queries, but have those sub-queries just return the results which match the original.
Anthony