Групування або вікно


13

У мене є ситуація, на яку я думаю, що її можна вирішити за допомогою віконної функції, але я не впевнений.

Уявіть наступну таблицю

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 )
  ;

Я хотів би створити нову групу при кожній зміні стовпця id_type. EG 1-а група з 7:19:21 до 7:19:25, 2-а старт і фініш о 7:19:26 тощо.
Після роботи я хочу включити більше критеріїв для визначення груп.

На даний момент, використовуючи запит нижче ...

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;

Я отримую такий результат:

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

Поки я хотів би:

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

Після того, як я вирішу цей перший крок, я додам більше стовпців, які використовуватиму як правила для розбиття груп, і ці інші стануть нульовими.

Версія Postgres: 8.4 (У нас є Postgres з Postgis, тому її нелегко оновити. Функції Postgis змінюють імена та є інші проблеми, але, сподіваємось, ми вже переписуємо все, і нова версія використовуватиме нову версію 9.X з postgis 2.x)


Відповіді:


4

За кілька пунктів,

  • Не називайте не тимчасову таблицю, tmpяка стає просто заплутаною.
  • Не використовуйте текст для часових позначок (ви робите це у своєму прикладі, що ми можемо сказати, оскільки часова марка не урізана і є .0)
  • Не називайте поле, в якому є час date. Якщо у нього є дата та час, це часова марка (і зберігати її як одну)

Краще використовувати віконну функцію ..

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);

Виходи

 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)

Пояснення

Спочатку нам потрібні скидання. Ми їх генеруємо 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)

Тоді ми розраховуємо отримати групи.

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)

Тоді ми загорнути в підзапит GROUP BYі ORDERі виберіть хв макс (діапазон)

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

16

1. Віконні функції плюс підзапити

Порахуйте кроки для формування груп, схожих на ідею Евана , із змінами та виправленнями:

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);

Це передбачає, що є стовпці NOT NULL. Ще потрібно зробити більше.

Крім того, припускаючи, dateщо визначено UNIQUE, інакше вам потрібно додати перемикач до ORDER BYпунктів, щоб отримати детерміновані результати. Як: ORDER BY date, id.

Детальне пояснення (відповідь на дуже схоже запитання):

Зокрема, зверніть увагу:

  • У споріднених випадках lag()з трьома параметрами може бути важливим елегантно покрити кутовий випадок першого (або останнього) ряду. (Третій параметр використовується за замовчуванням, якщо немає попереднього (наступного) ряду.

    lag(id_type, 1, id_type) OVER ()

    Оскільки ми зацікавлені тільки у фактичному зміні в id_type( TRUE), це не має значення в даному конкретному випадку. NULLі FALSEобидва не рахуються як step.

  • count(step OR NULL) OVER (ORDER BY date)це найкоротший синтаксис, який також працює в Postgres 9.3 або старіші. count()рахує лише ненульові значення ...

    У сучасних Postgres чистішим, еквівалентним синтаксисом був би:

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

    Деталі:

2. Відняти дві функції вікна, один підзапит

Схожа на ідею Еріка з модифікаціями:

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);

Якщо dateце визначено UNIQUE, як я вже згадував вище (ви ніколи не уточнювали), dense_rank()було б безглуздо, оскільки результат такий же, як row_number()і для останнього, значно дешевший.

Якщо dateце не визначено UNIQUE(і ми не знаємо, що увімкнено єдині дублікати (date, id_type)), усі ці запити є безглуздими, оскільки результат є довільним.

Крім того, підзапит зазвичай дешевший ніж CTE в Postgres. Використовуйте CTE лише тоді, коли вони вам потрібні .

Відповідні відповіді з додатковими поясненнями:

У пов’язаних випадках, коли в таблиці вже є запущений номер, ми можемо виконати функцію одного вікна:

3. Найвища продуктивність з функцією plpgsql

Оскільки це питання стало несподівано популярним, я додам ще одне рішення, щоб продемонструвати найкращі показники.

У SQL є багато складних інструментів для створення рішень із коротким та елегантним синтаксисом. Але декларативна мова має свої межі для більш складних вимог, які передбачають процесуальні елементи.

Процедурна функція на стороні сервера швидше для цього , ніж що - небудь писав до сих пір , тому що це потрібно тільки одного послідовне сканування над столом і однією операцією сортування . Якщо доступний індекс підходить, навіть лише одне сканування, призначене лише для індексу.

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;

Виклик:

SELECT * FROM f_tmp_groups();

Тест за допомогою:

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

Ви можете зробити функцію загальною для поліморфних типів та передати назви типів та стовпців таблиці. Деталі:

Якщо ви не хочете або не можете зберегти функцію для цього, навіть заплатите за створення тимчасової функції на ходу. Витрати на кілька мс.


dbfiddle для Postgres 9.6, порівнюючи продуктивність для всіх трьох.Будівля натестовому випадку Джекамодифікована.

dbfiddle для Postgres 8.4, де різниці в продуктивності ще більше.


Прочитайте це кілька разів - все ще не знаєте, про що ви говорите з трьома відставаннями аргументів або коли вам доведеться використовувати count(x or null)або навіть те, що там робиться. Може бути , ви могли б показати деякі зразки , де це необхідно, тому що це тут не потрібно. І що було б ключовим вимогою покриття цих кутових справ. До речі, я змінив свій нижчий внесок на оновлення лише для прикладу pl ​​/ pgsql. Це дійсно круто. (Але, як правило, я проти відповідей, які узагальнюють інші відповіді або охоплюють кутові випадки, - хоча я ненавиджу говорити, що це кутовий випадок, тому що я його не розумію).
Еван Керролл

Я б поставив їх у двох окремих запитаннях із самовідповіддю, тому що я впевнений, що я не єдиний, хто цікавиться, що count(x or null)робить. Я буду рада задати обидва питання, якщо ви хочете.
Еван Керролл


7

Це можна зробити як просте віднімання ROW_NUMBER()операцій (або якщо ваші дати не унікальні, хоча все ще унікальні для кожного id_type, тоді ви можете використовувати DENSE_RANK()замість цього, хоча це буде більш дорогий запит):

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
;

Дивіться цю роботу в DB Fiddle (або дивіться версію DENSE_RANK )

Результат:

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

Логічно, ви можете подумати про це як просте DENSE_RANK()з a PREORDER BY, тобто ви хочете, щоб DENSE_RANKусі елементи були класифіковані разом, і ви хочете, щоб вони були упорядковані за датами, ви просто повинні мати справу з набридливою проблемою того, що при кожній зміні дати DENSE_RANKзбільшуватиметься. Ви робите це, використовуючи вираз, як я вам показав вище. Уявіть, якби у вас був цей синтаксис: DENSE_RANK() OVER (PREORDER BY date, ORDER BY id_type)де PREORDERвирахування з ранжування виключено, і ORDER BYзараховується лише той .

Зауважте, що це важливо GROUP BYяк для генерованого Seqстовпця, так і для id_typeстовпця. SeqНЕ унікальний сам по собі, можуть бути перекриття - ви також повинні згрупувати id_type.

Для подальшого читання на цю тему:

Це перше посилання дає вам якийсь код, який ви можете використовувати, якщо ви хотіли, щоб дата початку або кінця була такою ж, як дата закінчення / початку попереднього або наступного періоду (так що пробілів немає). Плюс інші версії, які можуть допомогти вам у запиті. Хоча вони повинні бути перекладені з синтаксису SQL Server ...


6

На Postgres 8.4 ви можете використовувати функцію RECURSIVE .

Як вони це роблять

Рекурсивна функція додає рівень до кожного іншого id_type, вибираючи дати по черзі у порядку зменшення.

       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

Потім використовуйте групування MAX (дата), MIN (дата) за рівнем, id_type, щоб отримати бажаний результат.

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    |
+---------------------+---------------------+---------+

Перевірте це: http://rextester.com/WCOYFP6623


5

Ось ще один метод, схожий на метод Евана та Ервіна тим, що він використовує LAG для визначення островів. Він відрізняється від цих рішень тим, що використовує лише один рівень вкладання, не групування та значно більше віконних функцій:

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
;

is_startОбчислюваний стовпець в вкладеному SELECT , маркує початок кожного острова. Крім того, вкладений SELECT розкриває попередню дату кожного рядка та останню дату набору даних.

Для рядків, які є початками відповідних островів, попередня дата фактично є датою закінчення попереднього острова. Саме для цього і використовує головний SELECT. Він вибирає лише рядки, що відповідають is_start = 1умові, і для кожного повернутого рядка він показує власний рядок dateяк, beginа наступний рядок - prev_dateяк end. Оскільки в останньому рядку немає наступного рядка, він LEAD(prev_date)повертає нуль для нього, для якого функція COALESCE замінює останню дату набору даних.

Ви можете грати з цим рішенням на dbfiddle .

Вводячи додаткові стовпці, що ідентифікують острови, ви, ймовірно, захочете ввести підрозділ PARTITION BY до пункту OVER кожної функції вікна. Наприклад, якщо ви хочете виявити острови в групах, визначених a parent_id, вищезазначений запит, мабуть, повинен виглядати так:

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
;

І якщо ви вирішите скористатися рішенням Ервіна чи Евана, я вважаю, що до нього також потрібно буде додати подібну зміну.


5

Більше з академічного інтересу, ніж як практичне рішення, ви також можете досягти цього за допомогою визначеного користувачем сукупності . Як і інші рішення, це працюватиме навіть на Postgres 8.4, але, як коментували інші, будь ласка, оновіть, якщо зможете.

Сукупність обробляє nullтак, ніби вона є різною foo_type, тому запуски нулів будуть надані однакові grp- це може бути, а може і не бути тим, що ви хочете.

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;
start_at | end_at | foo_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

dbfiddle тут


4

Це можна зробити для того, RECURSIVE CTEщоб перейти "час початку" з одного ряду в інший та деякі додаткові (зручні) препарати.

Цей запит повертає бажаний результат:

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") 
)

після підготовки ... рекурсивна частина

, 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 ;

Ви можете перевірити це на веб-сайті http://rextester.com/POYM83542

Цей метод недостатньо масштабується. Для таблиці 8_641 рядків потрібно 7s, для таблиці, що вдвічі перевищує розмір, займає 28s. Ще кілька зразків показують часи виконання, схожі на O (n ^ 2).

Метод Евана Каррола займає менше 1 секунди (тобто: іди за цим!), І виглядає як O (n). Рекурсивні запити абсолютно неефективні, і їх слід вважати крайнім засобом.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.