Search notes:

SQL: GROUP BY / KEEP … DENSE_RANK

Some aggregate functions can be followed by the keywords KEEP and DENSE_RANK:
max(expr) KEEP (DENSE_RANK first order by anotherExpr).
Such a construct returns the value of expr whose record within a group has the lowest value for anotherExpr.
I hope the following example demonstrates this behavior.
Here's the data. The value of ID groups the data into three groups, A, B and C.
For each group, we want to select the maximum value of VAL and the corresponding values (of the same record) for TXT and NUM. For example, in group B, the maximum value of ID is 7 and the corresponding value for TXT and NUM are de and 28.
First, we need a table:
create table some_data (
   id  char    (1) not null,
   val number  (1) not null,
   txt varchar2(4) not null,
   num number  (2) not null
);
Github repository about-SQL, path: /select/group-by/keep-dense_rank/create-table.sql
And some data in the table.
insert into some_data values ('A', 5, 'fgh' , 14);
insert into some_data values ('A', 4, 'lmn' , 22);
insert into some_data values ('A', 9, 'abc' , 19);

insert into some_data values ('B', 2, 'ijk' , 35);
insert into some_data values ('B', 3, 'op'  , 15);
insert into some_data values ('B', 7, 'de'  , 28);
insert into some_data values ('B', 6, 'xyz' , 47);

insert into some_data values ('C', 4, 'tuvw', 33);
insert into some_data values ('C', 2, 'qrs' , 37);
Github repository about-SQL, path: /select/group-by/keep-dense_rank/insert-data.sql
Then, we're ready to select the data as outlined above:
select
   id,
   min(val) keep (dense_rank first order by val desc) max_val_for_id       ,
   min(txt) keep (dense_rank first order by val desc) txt_for_max_val_in_id,
   min(num) keep (dense_rank first order by val desc) num_for_max_val_in_id
from
   some_data
group by
   id
order by
   id
;
Github repository about-SQL, path: /select/group-by/keep-dense_rank/select-data.sql
The result set (here displayed in Oracle SQL Developer):

TODO

Clean up old stuff

Colum values of row with max value

create table tq84_a (
  a  varchar2(10),
  b  number
);

insert into tq84_a values ('a', 1);
insert into tq84_a values ('b', 2);
insert into tq84_a values ('a', 4);
insert into tq84_a values ('b', 3);

select
  max(a) keep (dense_rank first order by b desc)  a,
  max(b) keep (dense_rank first order by b desc)  b
from
  tq84_a
;

drop table tq84_a purge;

GROUP BY vs OVER

create table tq84_group_by_vs_over (
  a varchar2(3),
  b number
);


insert into tq84_group_by_vs_over values ('foo', 42);
insert into tq84_group_by_vs_over values ('foo', 10);
insert into tq84_group_by_vs_over values ('foo', 13);

insert into tq84_group_by_vs_over values ('bar',  7);
insert into tq84_group_by_vs_over values ('bar', 39);
insert into tq84_group_by_vs_over values ('bar', 88);

insert into tq84_group_by_vs_over values ('baz', 21);

----------------------------------------------------

select
  max(b) keep (dense_rank first order by b desc) over (partition by a) max_b_for_a,
  --                                             ^^^^^^^^^^^^^^^^^^^^^
  --                                             Missing in 2nd query
  a
from tq84_group_by_vs_over
;

----------------------------------------------------

select
  max(b) keep (dense_rank first order by b desc)  max_b_for_a,
  a
from tq84_group_by_vs_over
  group by a -- <<< Missing in 1st query
;

----------------------------------------------------

drop table tq84_group_by_vs_over purge;

See also

… KEEP (DENSE_RANK …) can be followed by OVER in which case it becomes an analytic function.
The arg_max(v, w) and arg_min(v, w) aggregate functions in DuckDB have a similar purpose, but are more legible (imho).

Index

Fatal error: Uncaught PDOException: SQLSTATE[HY000]: General error: 8 attempt to write a readonly database in /home/httpd/vhosts/renenyffenegger.ch/php/web-request-database.php:78 Stack trace: #0 /home/httpd/vhosts/renenyffenegger.ch/php/web-request-database.php(78): PDOStatement->execute(Array) #1 /home/httpd/vhosts/renenyffenegger.ch/php/web-request-database.php(30): insert_webrequest_('/notes/developm...', 1758201035, '216.73.216.150', 'Mozilla/5.0 App...', NULL) #2 /home/httpd/vhosts/renenyffenegger.ch/httpsdocs/notes/development/databases/SQL/select/group-by/keep-dense_rank/index(170): insert_webrequest() #3 {main} thrown in /home/httpd/vhosts/renenyffenegger.ch/php/web-request-database.php on line 78