Mysql
 sql >> Base de Dados >  >> RDS >> Mysql

Lacunas e ilhas em 2 colunas - se a coluna A for consecutiva e a coluna B idêntica


Não há muito o que mudar em sua consulta. Você basicamente precisa selecionar name e number na subconsulta e classifique na mesma ordem. Então você pode agrupar por name, number - rn na consulta externa.
SELECT
    min(number) first_number,
    max(number) last_number,
    count(*) AS no_records,
    name
FROM (
    SELECT c.*, @rn := @rn + 1 rn
    from (
        SELECT name, number
        FROM `table`
        WHERE cc = 1
        ORDER BY name, number
        LIMIT 99999999999999999
    ) AS c
    CROSS JOIN (SELECT @rn := 0) r
) c
GROUP BY name, number - rn 
ORDER BY first_number ASC, name ASC;

Resultado:
first_number  last_number  no_records  name
           1            2           2  Apple
           3            3           1  Bean
          10           12           3  Hello
          14           14           1  Deer
          14           14           1  Door
          15           15           1  Hello
          17           17           1  Hello

db<>fiddle

Eu costumo defender contra o uso de variáveis ​​de sessão dessa maneira. A razão é que tais soluções dependem de implementação interna e podem ser quebradas por atualizações de versão ou alterações de configurações. Por exemplo:Uma vez que o MariaDB decidiu ignorar a cláusula ORDER BY em subconsultas sem LIMIT. É por isso que incluí um LIMIT enorme.

Também substituí number com first_number na cláusula ORDER BY externa para evitar problemas com o modo ONLY_FULL_GROUP_BY.

Uma maneira mais estável de gerar números de linha é usar uma coluna AOTO_INCREMENT em uma tabela temporária:
drop temporary table if exists tmp_tbl;

create temporary table tmp_tbl (
  rn int unsigned auto_increment primary key,
  name varchar(64) not null,
  number int not null
);

insert into tmp_tbl (name, number)
  select name, number
  from `table`
  order by name, number;

A consulta SELECT final é idêntica à consulta externa acima:
SELECT
    min(number) first_number,
    max(number) last_number,
    count(*) AS no_records,
    name
FROM tmp_tbl
GROUP BY name, number - rn 
ORDER BY first_number ASC, name ASC;

db<>fiddle

Em uma versão mais recente (a partir do MariaDB 10.2) você pode usar ROW_NUMBER() função de janela em vez disso:
SELECT
    min(number) first_number,
    max(number) last_number,
    count(*) AS no_records,
    name
FROM (
    SELECT
        name,
        number,
        row_number() OVER (ORDER BY name, number) as rn
    FROM `table`
    WHERE cc = 1
) c
GROUP BY name, number - rn 
ORDER BY first_number ASC, name ASC;

db<>fiddle