N primeiros registros de cada grupo
===
###### tags: `SQL`
# O Problema
Em alguns casos pode ser necessário trazer apenas os primeiros (ou últimos, etc) registros de cada grupo de registros de uma consulta.
Por exemplo, imaginemos uma situação em que temos a lista dos 50 carros mais vendidos no país e queremos os 2 mais vendidos por fabricante.
| Posição | Fabricante | Automóvel | Vendas |
| -------- | -------- | -------- | -------- |
| 1 | Chevrolet | Onix | 210458 |
| 2 | Hyundai | HB20 | 105506 |
| 3 | Ford | Ka | 103286 |
| 4 | Volkswagen | Gol | 77612 |
| 5 | Chevrolet | Prisma | 71735 |
| 6 | Volkswagen | Polo | 69584 |
| 7 | Renault | Kwid | 67320 |
| 8 | Fiat | Strada | 67227 |
| 9 | Fiat | Argo | 63011 |
| 10 | Jeep | Compass | 60284 |
| 11 | Toyota | Corolla | 59062 |
| 12 | Fiat | Toro | 58477 |
| 13 | Renault | Sandero | 52401 |
| 14 | Fiat | Mobi | 49491 |
| 15 | Hyundai | Creta | 48976 |
| 16 | Honda | HR-V | 47959 |
| 17 | Nissan | Kicks | 46812 |
| 18 | Jeep | Renegade | 46344 |
| 19 | Volkswagen | Saveiro | 45920 |
| 20 | Volkswagen | Virtus | 41634 |
| 21 | Toyota | Hilux | 39278 |
| 22 | Volkswagen | Fox/CrossFox | 39260 |
| 23 | Ford | Ka Sedan | 39027 |
| 24 | Ford | EcoSport | 34497 |
| 25 | Volkswagen | Voyage | 32683 |
| 26 | Hyundai | HB20S | 32155 |
| 27 | Chevrolet | S10 | 31761 |
| 28 | Fiat | Cronos | 29307 |
| 29 | Nissan | Versa | 27993 |
| 30 | Toyota | Etios Hatch | 27847 |
| 31 | Honda | Fit | 27359 |
| 32 | Renault | Captur | 26504 |
| 33 | Chevrolet | Tracker | 26100 |
| 34 | Honda | Civic | 25942 |
| 35 | Chevrolet | Spin | 25192 |
| 36 | Renault | Duster | 23579 |
| 37 | Renault | Logan | 22471 |
| 38 | Chevrolet | Cobalt | 21488 |
| 39 | Toyota | Etios Sedan | 21207 |
| 40 | Volkswagen | Up! | 20563 |
| 41 | Ford | Ranger | 20552 |
| 42 | Chevrolet | Cruze Sedan | 19828 |
| 43 | Volkswagen | Amarok | 18766 |
| 44 | Toyota | Yaris Hatch | 18584 |
| 45 | Fiat | Siena | 17470 |
| 46 | Fiat | Uno | 15151 |
| 47 | Honda | City | 14900 |
| 48 | Honda | WR-V | 14797 |
| 49 | Ford | Fiesta | 14505 |
| 50 | Toyota | Yaris Sedan | 13674 |
# Dados para a consulta
Para montar nossa consulta vamos usar o banco de dados PostgreSQL.
Vamos criar a tabela:
```sql=
CREATE TABLE carros
(
id bigserial NOT NULL,
posicao numeric NOT NULL,
fabricante character varying NOT NULL,
automovel character varying NOT NULL,
vendas numeric NOT NULL,
CONSTRAINT carros_pk PRIMARY KEY (id),
CONSTRAINT carros_posicao_check CHECK (posicao > 0::numeric),
CONSTRAINT carros_vendas_check CHECK (vendas > 0::numeric)
)
WITH (
OIDS=FALSE
);
```
Vamos popular a tabela:
```sql=
INSERT INTO carros(posicao, fabricante, automovel, vendas)
VALUES (1,'Chevrolet','Onix','210458'),
(2,'Hyundai','HB20','105506'),
(3,'Ford','Ka','103286'),
(4,'Volkswagen','Gol','77612'),
(5,'Chevrolet','Prisma','71735'),
(6,'Volkswagen','Polo','69584'),
(7,'Renault','Kwid','67320'),
(8,'Fiat','Strada','67227'),
(9,'Fiat','Argo','63011'),
(10,'Jeep','Compass','60284'),
(11,'Toyota','Corolla','59062'),
(12,'Fiat','Toro','58477'),
(13,'Renault','Sandero','52401'),
(14,'Fiat','Mobi','49491'),
(15,'Hyundai','Creta','48976'),
(16,'Honda','HR-V','47959'),
(17,'Nissan','Kicks','46812'),
(18,'Jeep','Renegade','46344'),
(19,'Volkswagen','Saveiro','45920'),
(20,'Volkswagen','Virtus','41634'),
(21,'Toyota','Hilux','39278'),
(22,'Volkswagen','Fox/CrossFox','39260'),
(23,'Ford','Ka Sedan','39027'),
(24,'Ford','EcoSport','34497'),
(25,'Volkswagen','Voyage','32683'),
(26,'Hyundai','HB20S','32155'),
(27,'Chevrolet','S10','31761'),
(28,'Fiat','Cronos','29307'),
(29,'Nissan','Versa','27993'),
(30,'Toyota','Etios Hatch','27847'),
(31,'Honda','Fit','27359'),
(32,'Renault','Captur','26504'),
(33,'Chevrolet','Tracker','26100'),
(34,'Honda','Civic','25942'),
(35,'Chevrolet','Spin','25192'),
(36,'Renault','Duster','23579'),
(37,'Renault','Logan','22471'),
(38,'Chevrolet','Cobalt','21488'),
(39,'Toyota','Etios Sedan','21207'),
(40,'Volkswagen','Up!','20563'),
(41,'Ford','Ranger','20552'),
(42,'Chevrolet','Cruze Sedan','19828'),
(43,'Volkswagen','Amarok','18766'),
(44,'Toyota','Yaris Hatch','18584'),
(45,'Fiat','Siena','17470'),
(46,'Fiat','Uno','15151'),
(47,'Honda','City','14900'),
(48,'Honda','WR-V','14797'),
(49,'Ford','Fiesta','14505'),
(50,'Toyota','Yaris Sedan','13674');
```
Pronto, agora podemos passar para a nossa consulta.
# Criando a consulta
Vamos primeiro montar a consulta ordenando os carros por fabricante e por vendas.
```sql=
SELECT posicao, fabricante, automovel, vendas
FROM carros
ORDER BY fabricante, vendas DESC;
```
E termos a tabela da seguinte maneira:
| Posição | Fabricante | Automóvel | Vendas |
| -------- | -------- | -------- | -------- |
| 1 | Chevrolet | Onix | 210458 |
| 5 | Chevrolet | Prisma | 71735 |
| 27 | Chevrolet | S10 | 31761 |
| 33 | Chevrolet | Tracker | 26100 |
| 35 | Chevrolet | Spin | 25192 |
| 38 | Chevrolet | Cobalt | 21488 |
| 42 | Chevrolet | Cruze Sedan | 19828 |
| 8 | Fiat | Strada | 67227 |
| 9 | Fiat | Argo | 63011 |
| 12 | Fiat | Toro | 58477 |
| 14 | Fiat | Mobi | 49491 |
| 28 | Fiat | Cronos | 29307 |
| 45 | Fiat | Siena | 17470 |
| 46 | Fiat | Uno | 15151 |
| 3 | Ford | Ka | 103286 |
| 23 | Ford | Ka Sedan | 39027 |
| 24 | Ford | EcoSport | 34497 |
| 41 | Ford | Ranger | 20552 |
| 49 | Ford | Fiesta | 14505 |
| 16 | Honda | HR-V | 47959 |
| 31 | Honda | Fit | 27359 |
| 34 | Honda | Civic | 25942 |
| 47 | Honda | City | 14900 |
| 48 | Honda | WR-V | 14797 |
| 2 | Hyundai | HB20 | 105506 |
| 15 | Hyundai | Creta | 48976 |
| 26 | Hyundai | HB20S | 32155 |
| 10 | Jeep | Compass | 60284 |
| 18 | Jeep | Renegade | 46344 |
| 17 | Nissan | Kicks | 46812 |
| 29 | Nissan | Versa | 27993 |
| 7 | Renault | Kwid | 67320 |
| 13 | Renault | Sandero | 52401 |
| 32 | Renault | Captur | 26504 |
| 36 | Renault | Duster | 23579 |
| 37 | Renault | Logan | 22471 |
| 11 | Toyota | Corolla | 59062 |
| 21 | Toyota | Hilux | 39278 |
| 30 | Toyota | Etios Hatch | 27847 |
| 39 | Toyota | Etios Sedan | 21207 |
| 44 | Toyota | Yaris Hatch | 18584 |
| 50 | Toyota | Yaris Sedan | 13674 |
| 4 | Volkswagen | Gol | 77612 |
| 6 | Volkswagen | Polo | 69584 |
| 19 | Volkswagen | Saveiro | 45920 |
| 20 | Volkswagen | Virtus | 41634 |
| 22 | Volkswagen | Fox/CrossFox | 39260 |
| 25 | Volkswagen | Voyage | 32683 |
| 40 | Volkswagen | Up! | 20563 |
| 43 | Volkswagen | Amarok | 18766 |
Podemos ver a ordenação por fabricante e sabemos os 2 carros vendidos por fabricante mas queremos exibir apenas os 2 mais vendidos por fabricante.
Como fazer?
# Funções de "Janela" do PostgreSQL
No PostgreSQL temos funções de "janela" ou "[Window Functions](https://www.postgresql.org/docs/9.6/functions-window.html)".
Estas funções nos permitem fazer cálculos em conjuntos de linhas relacionadas à consulta atual.
# Usando a função row_number()
Vamos usar a função *row_number()* que vai nos numerar cada linha de cada grupo começando por 1.
A consulta fica:
```sql=
SELECT posicao, fabricante, automovel, vendas,
row_number() over (partition by fabricante order by vendas DESC) as pos_grupo
FROM teste.carros
```
E os dados:
| Posição | Fabricante | Automóvel | Vendas | Pos. Grupo |
| -------- | -------- | -------- | -------- | -------- |
| 1 | Chevrolet | Onix | 210458 | 1 |
| 5 | Chevrolet | Prisma | 71735 | 2 |
| 27 | Chevrolet | S10 | 31761 | 3 |
| 33 | Chevrolet | Tracker | 26100 | 4 |
| 35 | Chevrolet | Spin | 25192 | 5 |
| 38 | Chevrolet | Cobalt | 21488 | 6 |
| 42 | Chevrolet | Cruze Sedan | 19828 | 7 |
| 8 | Fiat | Strada | 67227 | 1 |
| 9 | Fiat | Argo | 63011 | 2 |
| 12 | Fiat | Toro | 58477 | 3 |
| 14 | Fiat | Mobi | 49491 | 4 |
| 28 | Fiat | Cronos | 29307 | 5 |
| 45 | Fiat | Siena | 17470 | 6 |
| 46 | Fiat | Uno | 15151 | 7 |
| 3 | Ford | Ka | 103286 | 1 |
| 23 | Ford | Ka Sedan | 39027 | 2 |
| 24 | Ford | EcoSport | 34497 | 3 |
| 41 | Ford | Ranger | 20552 | 4 |
| 49 | Ford | Fiesta | 14505 | 5 |
| 16 | Honda | HR-V | 47959 | 1 |
| 31 | Honda | Fit | 27359 | 2 |
| 34 | Honda | Civic | 25942 | 3 |
| 47 | Honda | City | 14900 | 4 |
| 48 | Honda | WR-V | 14797 | 5 |
| 2 | Hyundai | HB20 | 105506 | 1 |
| 15 | Hyundai | Creta | 48976 | 2 |
| 26 | Hyundai | HB20S | 32155 | 3 |
| 10 | Jeep | Compass | 60284 | 1 |
| 18 | Jeep | Renegade | 46344 | 2 |
| 17 | Nissan | Kicks | 46812 | 1 |
| 29 | Nissan | Versa | 27993 | 2 |
| 7 | Renault | Kwid | 67320 | 1 |
| 13 | Renault | Sandero | 52401 | 2 |
| 32 | Renault | Captur | 26504 | 3 |
| 36 | Renault | Duster | 23579 | 4 |
| 37 | Renault | Logan | 22471 | 5 |
| 11 | Toyota | Corolla | 59062 | 1 |
| 21 | Toyota | Hilux | 39278 | 2 |
| 30 | Toyota | Etios Hatch | 27847 | 3 |
| 39 | Toyota | Etios Sedan | 21207 | 4 |
| 44 | Toyota | Yaris Hatch | 18584 | 5 |
| 50 | Toyota | Yaris Sedan | 13674 | 6 |
| 4 | Volkswagen | Gol | 77612 | 1 |
| 6 | Volkswagen | Polo | 69584 | 2 |
| 19 | Volkswagen | Saveiro | 45920 | 3 |
| 20 | Volkswagen | Virtus | 41634 | 4 |
| 22 | Volkswagen | Fox/CrossFox | 39260 | 5 |
| 25 | Volkswagen | Voyage | 32683 | 6 |
| 40 | Volkswagen | Up! | 20563 | 7 |
| 43 | Volkswagen | Amarok | 18766 | 8 |
# Passo final
O último passo é filtrar as posições que queremos dentro do grupo.
Para isso vamos fazer uma consulta usando a consulta anterior como *subquery* e vamos limitar a coluna pos_grupo para menor do que 3.
```sql=
SELECT posicao, fabricante, automovel, vendas
FROM (
SELECT posicao, fabricante, automovel, vendas,
row_number() over (partition by fabricante order by vendas DESC) as pos_grupo
FROM teste.carros
) g
WHERE pos_grupo<3
```
Pronto!
| Posição | Fabricante | Automóvel | Vendas |
| -------- | -------- | -------- | -------- |
| 1 | Chevrolet | Onix | 210458 |
| 5 | Chevrolet | Prisma | 71735 |
| 8 | Fiat | Strada | 67227 |
| 9 | Fiat | Argo | 63011 |
| 3 | Ford | Ka | 103286 |
| 23 | Ford | Ka Sedan | 39027 |
| 16 | Honda | HR-V | 47959 |
| 31 | Honda | Fit | 27359 |
| 2 | Hyundai | HB20 | 105506 |
| 15 | Hyundai | Creta | 48976 |
| 10 | Jeep | Compass | 60284 |
| 18 | Jeep | Renegade | 46344 |
| 17 | Nissan | Kicks | 46812 |
| 29 | Nissan | Versa | 27993 |
| 7 | Renault | Kwid | 67320 |
| 13 | Renault | Sandero | 52401 |
| 11 | Toyota | Corolla | 59062 |
| 21 | Toyota | Hilux | 39278 |
| 4 | Volkswagen | Gol | 77612 |
| 6 | Volkswagen | Polo | 69584 |