Mysql
 sql >> Datenbank >  >> RDS >> Mysql

Lücken und Inseln auf 2 Spalten - wenn Spalte A aufeinanderfolgend und Spalte B identisch sind

An deiner Abfrage ist nicht viel zu ändern. Sie müssen grundsätzlich name auswählen und number in der Unterabfrage und sortieren in der gleichen Reihenfolge. Dann können Sie nach name, number - rn gruppieren in der äußeren Abfrage.

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;

Ergebnis:

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

Normalerweise plädiere ich gegen die Verwendung von Session-Variablen auf diese Weise. Der Grund dafür ist, dass solche Lösungen von der internen Implementierung abhängen und durch Versionsaktualisierungen oder Einstellungsänderungen beschädigt werden können. Zum Beispiel:Einmal entschied sich MariaDB, die ORDER BY-Klausel in Unterabfragen ohne LIMIT zu ignorieren. Aus diesem Grund habe ich ein riesiges LIMIT eingefügt.

Ich habe auch number ersetzt mit first_number in der äußeren ORDER BY-Klausel, um Probleme mit dem ONLY_FULL_GROUP_BY-Modus zu vermeiden.

Eine stabilere Methode zum Generieren von Zeilennummern ist die Verwendung einer AOTO_INCREMENT-Spalte in einer temporären Tabelle:

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;

Die abschließende SELECT-Abfrage ist identisch mit der äußeren Abfrage oben:

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

In einer neueren Version (ab MariaDB 10.2) können Sie ROW_NUMBER() verwenden Fensterfunktion statt:

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