Raggruppamento o finestra


13

Ho una situazione che penso possa essere risolta usando la funzione finestra ma non ne sono sicuro.

Immagina la seguente tabella

CREATE TABLE tmp
  ( date timestamp,        
    id_type integer
  ) ;

INSERT INTO tmp 
    ( date, id_type )
VALUES
    ( '2017-01-10 07:19:21.0', 3 ),
    ( '2017-01-10 07:19:22.0', 3 ),
    ( '2017-01-10 07:19:23.1', 3 ),
    ( '2017-01-10 07:19:24.1', 3 ),
    ( '2017-01-10 07:19:25.0', 3 ),
    ( '2017-01-10 07:19:26.0', 5 ),
    ( '2017-01-10 07:19:27.1', 3 ),
    ( '2017-01-10 07:19:28.0', 5 ),
    ( '2017-01-10 07:19:29.0', 5 ),
    ( '2017-01-10 07:19:30.1', 3 ),
    ( '2017-01-10 07:19:31.0', 5 ),
    ( '2017-01-10 07:19:32.0', 3 ),
    ( '2017-01-10 07:19:33.1', 5 ),
    ( '2017-01-10 07:19:35.0', 5 ),
    ( '2017-01-10 07:19:36.1', 5 ),
    ( '2017-01-10 07:19:37.1', 5 )
  ;

Vorrei avere un nuovo gruppo ad ogni modifica sulla colonna id_type. Ad esempio, 1 ° gruppo dalle 7:19:21 alle 7:19:25, 2o inizio e fine alle 7:19:26 e così via.
Dopo che funziona, voglio includere più criteri per definire i gruppi.

In questo momento, usando la query qui sotto ...

SELECT distinct 
    min(min(date)) over w as begin, 
    max(max(date)) over w as end,   
    id_type
from tmp
GROUP BY id_type
WINDOW w as (PARTITION BY id_type)
order by  begin;

Ottengo il seguente risultato:

begin                   end                     id_type
2017-01-10 07:19:21.0   2017-01-10 07:19:32.0   3
2017-01-10 07:19:26.0   2017-01-10 07:19:37.1   5

Mentre mi piacerebbe:

begin                   end                     id_type
2017-01-10 07:19:21.0   2017-01-10 07:19:25.0   3
2017-01-10 07:19:26.0   2017-01-10 07:19:26.0   5
2017-01-10 07:19:27.1   2017-01-10 07:19:27.1   3
2017-01-10 07:19:28.0   2017-01-10 07:19:29.0   5
2017-01-10 07:19:30.1   2017-01-10 07:19:30.1   3
2017-01-10 07:19:31.0   2017-01-10 07:19:31.0   5
2017-01-10 07:19:32.0   2017-01-10 07:19:32.0   3
2017-01-10 07:19:33.1   2017-01-10 07:19:37.1   5

Dopo aver risolto questo primo passaggio, aggiungerò più colonne da utilizzare come regole per interrompere i gruppi e questi altri saranno nullable.

Versione di Postgres: 8.4 (Abbiamo Postgres con Postgis, quindi non è facile aggiornarlo. Le funzioni di Postgis cambiano nome e ci sono altri problemi, ma spero che stiamo già scrivendo tutto e la nuova versione userà una nuova versione 9.X con postgis 2.x)


Risposte:


4

Per alcuni punti,

  • Non chiamare una tabella non temporanea tmpche diventa confusa.
  • Non usare il testo per i timestamp (lo stai facendo nel tuo esempio, perché possiamo dire che il timestamp non è stato troncato e ha .0)
  • Non chiamare un campo che ha tempo date . Se ha data e ora, è un timestamp (e memorizzalo come uno)

Meglio usare una funzione di finestra.

SELECT id_type, grp, min(date), max(date)
FROM (
  SELECT date, id_type, count(is_reset) OVER (ORDER BY date) AS grp
  FROM (
    SELECT date, id_type, CASE WHEN lag(id_type) OVER (ORDER BY date) <> id_type THEN 1 END AS is_reset
    FROM tmp
  ) AS t
) AS g
GROUP BY id_type, grp
ORDER BY min(date);

Uscite

 id_type | grp |          min          |          max          
---------+-----+-----------------------+-----------------------
       3 |   0 | 2017-01-10 07:19:21.0 | 2017-01-10 07:19:25.0
       5 |   1 | 2017-01-10 07:19:26.0 | 2017-01-10 07:19:26.0
       3 |   2 | 2017-01-10 07:19:27.1 | 2017-01-10 07:19:27.1
       5 |   3 | 2017-01-10 07:19:28.0 | 2017-01-10 07:19:29.0
       3 |   4 | 2017-01-10 07:19:30.1 | 2017-01-10 07:19:30.1
       5 |   5 | 2017-01-10 07:19:31.0 | 2017-01-10 07:19:31.0
       3 |   6 | 2017-01-10 07:19:32.0 | 2017-01-10 07:19:32.0
       5 |   7 | 2017-01-10 07:19:33.1 | 2017-01-10 07:19:37.1
(8 rows)

spiegazione

Per prima cosa abbiamo bisogno di ripristini .. Li generiamo con lag()

SELECT date, id_type, CASE WHEN lag(id_type) OVER (ORDER BY date) <> id_type THEN 1 END AS is_reset
FROM tmp
ORDER BY date;

         date          | id_type | is_reset 
-----------------------+---------+----------
 2017-01-10 07:19:21.0 |       3 |         
 2017-01-10 07:19:22.0 |       3 |         
 2017-01-10 07:19:23.1 |       3 |         
 2017-01-10 07:19:24.1 |       3 |         
 2017-01-10 07:19:25.0 |       3 |         
 2017-01-10 07:19:26.0 |       5 |        1
 2017-01-10 07:19:27.1 |       3 |        1
 2017-01-10 07:19:28.0 |       5 |        1
 2017-01-10 07:19:29.0 |       5 |         
 2017-01-10 07:19:30.1 |       3 |        1
 2017-01-10 07:19:31.0 |       5 |        1
 2017-01-10 07:19:32.0 |       3 |        1
 2017-01-10 07:19:33.1 |       5 |        1
 2017-01-10 07:19:35.0 |       5 |         
 2017-01-10 07:19:36.1 |       5 |         
 2017-01-10 07:19:37.1 |       5 |         
(16 rows)

Quindi contiamo per ottenere gruppi.

SELECT date, id_type, count(is_reset) OVER (ORDER BY date) AS grp
FROM (
  SELECT date, id_type, CASE WHEN lag(id_type) OVER (ORDER BY date) <> id_type THEN 1 END AS is_reset
  FROM tmp
  ORDER BY date
) AS t
ORDER BY date

         date          | id_type | grp 
-----------------------+---------+-----
 2017-01-10 07:19:21.0 |       3 |   0
 2017-01-10 07:19:22.0 |       3 |   0
 2017-01-10 07:19:23.1 |       3 |   0
 2017-01-10 07:19:24.1 |       3 |   0
 2017-01-10 07:19:25.0 |       3 |   0
 2017-01-10 07:19:26.0 |       5 |   1
 2017-01-10 07:19:27.1 |       3 |   2
 2017-01-10 07:19:28.0 |       5 |   3
 2017-01-10 07:19:29.0 |       5 |   3
 2017-01-10 07:19:30.1 |       3 |   4
 2017-01-10 07:19:31.0 |       5 |   5
 2017-01-10 07:19:32.0 |       3 |   6
 2017-01-10 07:19:33.1 |       5 |   7
 2017-01-10 07:19:35.0 |       5 |   7
 2017-01-10 07:19:36.1 |       5 |   7
 2017-01-10 07:19:37.1 |       5 |   7
(16 rows)

Poi ci avvolgiamo in una selezione secondaria GROUP BYed ORDERe selezioniamo il min max (intervallo)

SELECT id_type, grp, min(date), max(date)
FROM (
  .. stuff
) AS g
GROUP BY id_type, grp
ORDER BY min(date);

16

1. Funzioni finestra più sottoquery

Conta i passaggi per formare gruppi, simili all'idea di Evan , con modifiche e correzioni:

SELECT id_type
     , min(date) AS begin
     , max(date) AS end
     , count(*)  AS row_ct  -- optional addition
FROM  (
   SELECT date, id_type, count(step OR NULL) OVER (ORDER BY date) AS grp
   FROM  (
      SELECT date, id_type
           , lag(id_type, 1, id_type) OVER (ORDER BY date) <> id_type AS step
      FROM   tmp
      ) sub1
   ) sub2
GROUP  BY id_type, grp
ORDER  BY min(date);

Questo presuppone che le colonne coinvolte lo siano NOT NULL. Altrimenti devi fare di più.

Supponendo anche datedi essere definitoUNIQUE , altrimenti è necessario aggiungere un tiebreaker al fileORDER BY clausole per ottenere risultati deterministici. Come: ORDER BY date, id.

Spiegazione dettagliata (risposta a una domanda molto simile):

Nota in particolare:

  • In casi correlati, lag()con 3 parametri può essere essenziale per coprire elegantemente il caso angolare della prima (o dell'ultima) riga. (Il terzo parametro viene utilizzato come predefinito se non è presente alcuna riga (successiva) precedente.

    lag(id_type, 1, id_type) OVER ()

    Poiché siamo interessati solo a un effettivo cambiamento di id_type( TRUE), non importa in questo caso particolare. NULLed FALSEentrambi non contano comestep .

  • count(step OR NULL) OVER (ORDER BY date) è la sintassi più breve che funziona anche in Postgres 9.3 o precedenti. count()conta solo valori non nulli ...

    Nei moderni Postgres, la sintassi più pulita ed equivalente sarebbe:

    count(step) FILTER (WHERE step) OVER (ORDER BY date)

    Dettagli:

2. Sottrai due funzioni finestra, una sottoquery

Simile all'idea di Erik con modifiche:

SELECT min(date) AS begin
     , max(date) AS end
     , id_type
FROM  (
   SELECT date, id_type
        , row_number() OVER (ORDER BY date)
        - row_number() OVER (PARTITION BY id_type ORDER BY date) AS grp
   FROM   tmp
   ) sub
GROUP  BY id_type, grp
ORDER  BY min(date);

Se datedefinito UNIQUE, come ho detto sopra (non hai mai chiarito), dense_rank()sarebbe inutile, poiché il risultato è lo stesso dirow_number() e quest'ultimo è sostanzialmente più economico.

Se nondate è definito (e non sappiamo che gli unici duplicati sono attiviUNIQUE(date, id_type) ), tutte queste query sono inutili, poiché il risultato è arbitrario.

Inoltre, una sottoquery è generalmente più economica di una CTE in Postgres. Utilizzare CTE solo quando è necessario .

Risposte correlate con ulteriori spiegazioni:

Nei casi correlati in cui abbiamo già un numero corrente nella tabella, possiamo accontentarci di una singola finestra:

3. Massime prestazioni con la funzione plpgsql

Poiché questa domanda è diventata inaspettatamente popolare, aggiungerò un'altra soluzione per dimostrare le massime prestazioni.

SQL ha molti strumenti sofisticati per creare soluzioni con sintassi breve ed elegante. Ma un linguaggio dichiarativo ha i suoi limiti per requisiti più complessi che coinvolgono elementi procedurali.

Una funzione procedurale sul lato server è più veloce di tutto ciò che è stato pubblicato finora perché richiede solo una singola scansione sequenziale sul tavolo e una singola operazione di ordinamento . Se è disponibile un indice di adattamento, anche solo una singola scansione dell'indice.

CREATE OR REPLACE FUNCTION f_tmp_groups()
  RETURNS TABLE (id_type int, grp_begin timestamp, grp_end timestamp) AS
$func$
DECLARE
   _row  tmp;                       -- use table type for row variable
BEGIN
   FOR _row IN
      TABLE tmp ORDER BY date       -- add more columns to make order deterministic
   LOOP
      CASE _row.id_type = id_type 
      WHEN TRUE THEN                -- same group continues
         grp_end := _row.date;      -- remember last date so far
      WHEN FALSE THEN               -- next group starts
         RETURN NEXT;               -- return result for last group
         id_type   := _row.id_type;
         grp_begin := _row.date;
         grp_end   := _row.date;
      ELSE                          -- NULL for 1st row
         id_type   := _row.id_type; -- remember row data for starters
         grp_begin := _row.date;
         grp_end   := _row.date;
      END CASE;
   END LOOP;

   RETURN NEXT;                     -- return last result row      
END
$func$ LANGUAGE plpgsql;

Chiamata:

SELECT * FROM f_tmp_groups();

Prova con:

EXPLAIN (ANALYZE, TIMING OFF)  -- to focus on total performance
SELECT * FROM  f_tmp_groups();

È possibile rendere generica la funzione con tipi polimorfici e passare il tipo di tabella e i nomi delle colonne. Dettagli:

Se non vuoi o non puoi perseguire una funzione per questo, pagherebbe persino per creare una funzione temporanea al volo. Costa qualche ms.


dbfiddle per Postgres 9.6, confrontando le prestazioni per tutti e tre. Costruito sulcaso di test di Jack, modificato.

dbfiddle per Postgres 8.4, dove le differenze di prestazioni sono ancora maggiori.


Leggi questo alcune volte - non sei ancora sicuro di cosa stai parlando con il ritardo dei tre argomenti o quando dovresti usare count(x or null)o anche cosa sta facendo lì. Forse potresti mostrare alcuni esempi dove è richiesto, perché non è richiesto qui. E quale sarebbe il requisito chiave per coprire quei casi d'angolo. A proposito, ho cambiato il mio downvote in upvote solo per l'esempio pl / pgsql. È davvero fantastico. (Ma, generalmente, sono contrario alle risposte che riassumono altre risposte o coprono casi angolari - anche se odio dire che questo è un caso angolare perché non lo capisco).
Evan Carroll

Le metterei in due domande separate con risposta autonoma perché sono sicuro di non essere il solo a chiedermi cosa count(x or null)faccia. Sarò felice di fare entrambe le domande se preferisci.
Evan Carroll,


7

Puoi farlo come una semplice sottrazione di ROW_NUMBER()operazioni (o se le tue date non sono univoche, anche se comunque uniche per id_type, puoi DENSE_RANK()invece utilizzare , anche se sarà una query più costosa):

WITH IdTypes AS (
   SELECT
      date,
      id_type,
      Row_Number() OVER (ORDER BY date)
         - Row_Number() OVER (PARTITION BY id_type ORDER BY date)
         AS Seq
   FROM
      tmp
)
SELECT
   Min(date) AS begin,
   Max(date) AS end,
   id_type
FROM IdTypes
GROUP BY id_type, Seq
ORDER BY begin
;

Guarda questo lavoro su DB Fiddle (o vedi la versione DENSE_RANK )

Risultato:

begin                  end                    id_type
---------------------  ---------------------  -------
2017-01-10 07:19:21    2017-01-10 07:19:25    3
2017-01-10 07:19:26    2017-01-10 07:19:26    5
2017-01-10 07:19:27.1  2017-01-10 07:19:27.1  3
2017-01-10 07:19:28    2017-01-10 07:19:29    5
2017-01-10 07:19:30.1  2017-01-10 07:19:30.1  3
2017-01-10 07:19:31    2017-01-10 07:19:31    5
2017-01-10 07:19:32    2017-01-10 07:19:32    3
2017-01-10 07:19:33.1  2017-01-10 07:19:37.1  5

Logicamente, puoi pensarlo come un semplice DENSE_RANK()con un PREORDER BY, cioè, vuoi il DENSE_RANKdi tutti gli elementi che sono classificati insieme e li vuoi ordinati per data, devi solo affrontare il fastidioso problema del fatto che ad ogni modifica della data, DENSE_RANKaumenterà. Lo fai usando l'espressione come ti ho mostrato sopra. Immagina di avere questa sintassi: DENSE_RANK() OVER (PREORDER BY date, ORDER BY id_type)dove PREORDERè escluso dal calcolo della classifica e solo ilORDER BY viene conteggiato .

Si noti che è importante GROUP BYsia per la Seqcolonna generata che per la id_typecolonna. SeqNON è unico di per sé, possono esserci sovrapposizioni - devi anche raggruppare perid_type .

Per ulteriori approfondimenti su questo argomento:

Quel primo link ti dà un codice che puoi usare se vuoi che la data di inizio o fine sia la stessa della data di fine / inizio del periodo precedente o successivo (quindi non ci sono spazi vuoti). Inoltre altre versioni che potrebbero aiutarti nella tua query. Sebbene debbano essere tradotti dalla sintassi di SQL Server ...


6

Su Postgres 8.4 puoi usare a funzione RECURSIVE .

Come lo fanno

La funzione ricorsiva aggiunge un livello a ogni diverso id_type, selezionando le date una per una in ordine decrescente.

       date           | id_type | lv
--------------------------------------
2017-01-10 07:19:21.0      3       8
2017-01-10 07:19:22.0      3       8
2017-01-10 07:19:23.1      3       8
2017-01-10 07:19:24.1      3       8
2017-01-10 07:19:25.0      3       8
2017-01-10 07:19:26.0      5       7
2017-01-10 07:19:27.1      3       6
2017-01-10 07:19:28.0      5       5
2017-01-10 07:19:29.0      5       5
2017-01-10 07:19:30.1      3       4
2017-01-10 07:19:31.0      5       3
2017-01-10 07:19:32.0      3       2
2017-01-10 07:19:33.1      5       1
2017-01-10 07:19:35.0      5       1
2017-01-10 07:19:36.1      5       1
2017-01-10 07:19:37.1      5       1

Quindi utilizzare il raggruppamento MAX (data), MIN (data) per livello, id_type per ottenere il risultato desiderato.

with RECURSIVE rdates as 
(
    (select   date, id_type, 1 lv 
     from     yourTable
     order by date desc
     limit 1
    )
    union
    (select    d.date, d.id_type,
               case when r.id_type = d.id_type 
                    then r.lv 
                    else r.lv + 1 
               end lv    
    from       yourTable d
    inner join rdates r
    on         d.date < r.date
    order by   date desc
    limit      1)
)
select   min(date) StartDate,
         max(date) EndDate,
         id_type
from     rdates
group by lv, id_type
;

+---------------------+---------------------+---------+
| startdate           |       enddate       | id_type |
+---------------------+---------------------+---------+
| 10.01.2017 07:19:21 | 10.01.2017 07:19:25 |    3    |
| 10.01.2017 07:19:26 | 10.01.2017 07:19:26 |    5    |
| 10.01.2017 07:19:27 | 10.01.2017 07:19:27 |    3    |
| 10.01.2017 07:19:28 | 10.01.2017 07:19:29 |    5    |
| 10.01.2017 07:19:30 | 10.01.2017 07:19:30 |    3    |
| 10.01.2017 07:19:31 | 10.01.2017 07:19:31 |    5    |
| 10.01.2017 07:19:32 | 10.01.2017 07:19:32 |    3    |
| 10.01.2017 07:19:33 | 10.01.2017 07:19:37 |    5    |
+---------------------+---------------------+---------+

Controllalo: http://rextester.com/WCOYFP6623


5

Ecco un altro metodo, che è simile a quello di Evan ed Erwin in quanto utilizza GAL per determinare le isole. Si differenzia da quelle soluzioni in quanto utilizza solo un livello di annidamento, nessun raggruppamento e molte più funzioni della finestra:

SELECT
  id_type,
  date AS begin,
  COALESCE(
    LEAD(prev_date) OVER (ORDER BY date ASC),
    last_date
  ) AS end
FROM
  (
    SELECT
      id_type,
      date,
      LAG(date) OVER (ORDER BY date ASC) AS prev_date,
      MAX(date) OVER () AS last_date,
      CASE id_type
        WHEN LAG(id_type) OVER (ORDER BY date ASC)
        THEN 0
        ELSE 1
      END AS is_start
    FROM
      tmp
  ) AS derived
WHERE
  is_start = 1
ORDER BY
  date ASC
;

Il is_start colonna calcolata in SELECT nidificato segna l'inizio di ciascuna isola. Inoltre, SELECT nidificato espone la data precedente di ogni riga e l'ultima data del set di dati.

Per le righe che rappresentano l'inizio delle rispettive isole, la data precedente è effettivamente la data di fine dell'isola precedente. Questo è ciò che viene utilizzato dal SELECT principale. Seleziona solo le righe corrispondenti alla is_start = 1condizione e per ogni riga restituita mostra le righe datecome begine le righe seguenti prev_datecome end. Poiché l'ultima riga non ha una riga successiva,LEAD(prev_date) restituisce un valore nullo per essa, per cui la funzione COALESCE sostituisce l'ultima data del set di dati.

Puoi giocare con questa soluzione su dbfiddle .

Quando si introducono colonne aggiuntive che identificano le isole, è probabile che si desideri introdurre una clausola secondaria PARTITION BY alla clausola OVER di ciascuna funzione della finestra. Ad esempio, se si desidera rilevare le isole all'interno di gruppi definiti da un parent_id, la query sopra dovrà probabilmente apparire così:

SELECT
  parent_id,
  id_type,
  date AS begin,
  COALESCE(
    LEAD(prev_date) OVER (PARTITION BY parent_id ORDER BY date ASC),
    last_date
  ) AS end
FROM
  (
    SELECT
      parent_id,
      id_type,
      date,
      LAG(date) OVER (PARTITION BY parent_id ORDER BY date ASC) AS prev_date,
      MAX(date) OVER (PARTITION BY parent_id) AS last_date,
      CASE id_type
        WHEN LAG(id_type) OVER (PARTITION BY parent_id ORDER BY date ASC)
        THEN 0
        ELSE 1
      END AS is_start
    FROM
      tmp
  ) AS derived
WHERE
  is_start = 1
ORDER BY
  date ASC
;

E se decidi di optare per la soluzione di Erwin o di Evan, credo che occorrerà aggiungere anche un cambiamento simile.


5

Più per interesse accademico che come soluzione pratica, puoi anche raggiungere questo obiettivo con un aggregato definito dall'utente . Come le altre soluzioni, questo funzionerà anche su Postgres 8.4, ma come altri hanno commentato, si prega di aggiornare se possibile.

L'aggregato nullviene foo_typegestito come se fosse un diverso , quindi le serie di valori null sarebbero fornite uguali grp, che potrebbero essere o meno ciò che desideri.

create function grp_sfunc(integer[],integer) returns integer[] language sql as $$
  select array[$1[1]+($1[2] is distinct from $2 or $1[3]=0)::integer,$2,1];
$$;
create function grp_finalfunc(integer[]) returns integer language sql as $$
  select $1[1];
$$;
create aggregate grp(integer)(
  sfunc = grp_sfunc
, stype = integer[]
, finalfunc = grp_finalfunc
, initcond = '{0,0,0}'
);
select min(foo_at) begin_at, max(foo_at) end_at, foo_type
from (select *, grp(foo_type) over (order by foo_at) from foo) z
group by grp, foo_type
order by 1;
begin_at | end_at | foo_type
: -------------------- | : -------------------- | -------:
10/01/2017 07:19:21 | 10/01/2017 07:19:25 | 3
10/01/2017 07:19:26 | 10/01/2017 07:19:26 | 5
10/01/2017 07: 19: 27.1 | 10/01/2017 07: 19: 27.1 | 3
10/01/2017 07:19:28 | 10/01/2017 07:19:29 | 5
10/01/2017 07: 19: 30.1 | 10/01/2017 07: 19: 30.1 | 3
10/01/2017 07:19:31 | 10/01/2017 07:19:31 | 5
10/01/2017 07:19:32 | 10/01/2017 07:19:32 | 3
10/01/2017 07: 19: 33.1 | 10/01/2017 07: 19: 37.1 | 5

dbfiddle qui


4

Questo può essere fatto RECURSIVE CTEper passare il "tempo di inizio" da una riga alla successiva e alcuni preparativi extra (di praticità).

Questa query restituisce il risultato desiderato:

WITH RECURSIVE q AS
(
    SELECT
        id_type,
        "date",
        /* We compute next id_type for convenience, plus row_number */
        row_number()  OVER (w) AS rn,
        lead(id_type) OVER (w) AS next_id_type
    FROM
        t
    WINDOW
        w AS (ORDER BY "date") 
)

dopo la preparazione ... parte ricorsiva

, rec AS 
(
    /* Anchor */
    SELECT
        q.rn,
        q."date" AS "begin",
        /* When next_id_type is different from Look also at **next** row to find out whether we need to mark an end */
        case when q.id_type is distinct from q.next_id_type then q."date" END AS "end",
        q.id_type
    FROM
        q
    WHERE
        rn = 1

    UNION ALL

    /* Loop */
    SELECT
        q.rn,
        /* We keep copying 'begin' from one row to the next while type doesn't change */
        case when q.id_type = rec.id_type then rec.begin else q."date" end AS "begin",
        case when q.id_type is distinct from q.next_id_type then q."date" end AS "end",
        q.id_type
    FROM
        rec
        JOIN q ON q.rn = rec.rn+1
)
-- We filter the rows where "end" is not null, and project only needed columns
SELECT
    "begin", "end", id_type
FROM
    rec
WHERE
    "end" is not null ;

Puoi verificarlo su http://rextester.com/POYM83542

Questo metodo non si adatta bene. Per una tabella di righe 8_641, ci vogliono 7 secondi, per una tabella doppia di quella dimensione, ci vogliono 28 secondi. Alcuni altri esempi mostrano tempi di esecuzione simili a O (n ^ 2).

Il metodo di Evan Carrol richiede meno di 1 s (cioè: provalo!) E sembra O (n). Le query ricorsive sono assolutamente inefficienti e dovrebbero essere considerate l'ultima risorsa.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.