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 |