简体   繁体   中英

max in one column and min in another column

For example, if Column A and Column B have values:

+---+---+
| A | B |
+---+---+
| 2 | 1 |
| 5 | 1 | 
| 6 | 1 |
| 1 | 2 |
| 5 | 2 |
| 0 | 2 |
| 2 | 3 |
| 7 | 3 |
| 4 | 3 |
| 5 | 4 |
+---+---+

From each group of B, I want to get the highest number from A. However, I don't want to include results where the number in B is higher, yet has a smaller A value than the previous one. I know this doesn't make sense in words, but this is what I want the final result to look like:

+---+---+
| A | B |
+---+---+
| 6 | 1 |
| 7 | 3 |
+---+---+

So far I have something like "select max(a), b from table1 group by b" but this doesn't omit the ones where B is higher but the max A is smaller. I know that I could just peruse the results of that query in PHP and remove the ones where the A value is smaller than the previous A value, but I want to put it all in the mysql query if possible.

This technique joins the table against the aggregated version of itself, but the join is offset by one , so that every row is joined to the knowledge of the previous-B's MAX(A) value. It then matches rows where the current A is greater than any of those, and if it doesn't find any, it doesn't include the row. We then aggregate the final selection to get the results you are after.

SELECT 
       MAX(source_row.A) as A, 
       source_row.B
  FROM ab as source_row
  LEFT JOIN (SELECT MAX(A) as A, B FROM ab GROUP BY B) AS one_back 
    ON one_back.B = source_row.B-1 
 WHERE (one_back.A IS NULL) 
    OR one_back.A < source_row.A
 GROUP BY B

I have tested this :-)

edit: extra insight

I wanted to share a little insight into how I come up with these kind of solutions; 'cause I think it's important for folks to start to "think in sets"... that's the best advice I ever read regarding JOINS, that you need to envision the intermediate "sets" that your query was working with. To illustrate this, here is a representation of the intermediate "set" that is the critical part of this query; it is the table as it exists "joined" to the aggregated version of itself off-by-one.

+------+------+------------+------------+
| A    | B    | one_back.B | one_back.A |
+------+------+------------+------------+
|    2 |    1 |       NULL |       NULL |
|    5 |    1 |       NULL |       NULL |
|    6 |    1 |       NULL |       NULL |
|    1 |    2 |          1 |          6 |
|    5 |    2 |          1 |          6 |
|    0 |    2 |          1 |          6 |
|    2 |    3 |          2 |          5 |
|    7 |    3 |          2 |          5 |
|    4 |    3 |          2 |          5 |
|    5 |    4 |          3 |          7 |
+------+------+------------+------------+

And then the set as it actually is created in-memory (the full join'd version is never fully in memory, since MySQL can eliminate rows as soon as it knows they are not going to "make the cut":

+------+------+------------+------------+
| A    | B    | one_back.B | one_back.A |
+------+------+------------+------------+
|    2 |    1 |       NULL |       NULL |
|    5 |    1 |       NULL |       NULL |
|    6 |    1 |       NULL |       NULL |
|    7 |    3 |          2 |          5 |
+------+------+------------+------------+

And then, of course, it aggregates the results from there into the final form, selecting only the A and B from the original rows.

A simpler solution would be to use a variable to store the value of a from the previous row and make the comparison on each iteration. This also accounts for the case where you might have gaps in the b column, where numbers aren't exactly in perfect sequential order:

SELECT @val:=a.a AS a, a.b
FROM
(
  SELECT MAX(a) AS a, b
  FROM tbl
  GROUP BY b
) a
WHERE a.a > IFNULL(@val,-1)
Select Z.a, Z.b from
(select a, b, rank() over (order by b) as ranker from (select max(a) a, b  from table1 group by b) Y) Z left join
(select a, b, rank() over (order by b) as ranker from (select max(a) a, b  from table1 group by b) Y1) Z1
on Z.ranker = Z1.ranker + 1
where Z.a > isnull(Z1.a, -100000)

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