SQL HowTo: ื›ืชื•ื‘ ืœื•ืœืืช while ื™ืฉื™ืจื•ืช ื‘ืฉืื™ืœืชื”, ืื• "ืืœืžื ื˜ืจื™ ืชืœืช ื›ื™ื•ื•ื ื™"

ืžืขืช ืœืขืช, ืžืชืขื•ืจืจืช ื”ืžืฉื™ืžื” ืฉืœ ื—ื™ืคื•ืฉ ื ืชื•ื ื™ื ืงืฉื•ืจื™ื ื‘ืืžืฆืขื•ืช ืงื‘ื•ืฆืช ืžืคืชื—ื•ืช. ืขื“ ืฉื ืงื‘ืœ ืืช ื”ืžืกืคืจ ื”ื›ื•ืœืœ ื”ื ื“ืจืฉ ืฉืœ ืจืฉื•ืžื•ืช.

ื”ื“ื•ื’ืžื” ื”ื›ื™ "ื”ื—ื™ื™ื ื”ืืžื™ืชื™ื™ื" ื”ื™ื ืœื”ืฆื™ื’ 20 ื”ื‘ืขื™ื•ืช ื”ืขืชื™ืงื•ืช ื‘ื™ื•ืชืจ, ื‘ืจืฉื™ืžื” ื‘ืจืฉื™ืžืช ื”ืขื•ื‘ื“ื™ื (ืœื“ื•ื’ืžื”, ื‘ืชื•ืš ื—ื˜ื™ื‘ื” ืื—ืช). ืขื‘ื•ืจ "ืœื•ื—ื•ืช ืžื—ื•ื•ื ื™ื" ืฉื•ื ื™ื ืฉืœ ื ื™ื”ื•ืœ ืขื ืกื™ื›ื•ืžื™ื ืงืฆืจื™ื ืฉืœ ืชื—ื•ืžื™ ืขื‘ื•ื“ื”, ื ื•ืฉื ื“ื•ืžื” ื ื“ืจืฉ ืœืขืชื™ื ืงืจื•ื‘ื•ืช ืœืžื“ื™.

SQL HowTo: ื›ืชื•ื‘ ืœื•ืœืืช while ื™ืฉื™ืจื•ืช ื‘ืฉืื™ืœืชื”, ืื• "ืืœืžื ื˜ืจื™ ืชืœืช ื›ื™ื•ื•ื ื™"

ื‘ืžืืžืจ ื–ื” ื ื‘ื—ืŸ ื™ื™ืฉื•ื ื‘-PostgreSQL ืฉืœ โ€‹โ€‹ืคืชืจื•ืŸ "ื ืื™ื‘ื™" ืœื‘ืขื™ื” ื›ื–ื•, ืืœื’ื•ืจื™ืชื "ื—ื›ื" ื•ืžื•ืจื›ื‘ ืžืื•ื“ "ืœื•ืœืื”" ื‘-SQL ืขื ืชื ืื™ ื™ืฆื™ืื” ืžื”ื ืชื•ื ื™ื ืฉื ืžืฆืื•, ืฉื™ื›ื•ืœ ืœื”ื™ื•ืช ืฉื™ืžื•ืฉื™ ื”ืŸ ืœืคื™ืชื•ื— ื›ืœืœื™ ื•ื”ืŸ ืœืฉื™ืžื•ืฉ ื‘ืžืงืจื™ื ื“ื•ืžื™ื ืื—ืจื™ื.

ื‘ื•ื ื ื™ืงื— ืžืขืจืš ื ืชื•ื ื™ ื‘ื“ื™ืงื” ืžืืžืจ ืงื•ื“ื. ื›ื“ื™ ืœืžื ื•ืข ืžื”ืจืฉื•ืžื•ืช ื”ืžื•ืฆื’ื•ืช "ืœืงืคื•ืฅ" ืžืขืช ืœืขืช ื›ืืฉืจ ื”ืขืจื›ื™ื ื”ืžืžื•ื™ื ื™ื ื—ื•ืคืคื™ื, ื”ืจื—ื‘ ืืช ืื™ื ื“ืงืก ื”ื ื•ืฉื ืขืœ ื™ื“ื™ ื”ื•ืกืคืช ืžืคืชื— ืจืืฉื™. ื™ื—ื“ ืขื ื–ืืช, ื–ื” ื™ืขื ื™ืง ืœื• ื‘ืื•ืคืŸ ืžื™ื™ื“ื™ ื™ื™ื—ื•ื“ื™ื•ืช ื•ื™ื‘ื˜ื™ื— ืœื ื• ืฉืกื“ืจ ื”ืžื™ื•ืŸ ื”ื•ื ื—ื“ ืžืฉืžืขื™:

CREATE INDEX ON task(owner_id, task_date, id);
-- ะฐ ัั‚ะฐั€ั‹ะน - ัƒะดะฐะปะธะผ
DROP INDEX task_owner_id_task_date_idx;

ื›ืžื• ืฉื ืฉืžืข, ื›ืš ื›ืชื•ื‘

ืจืืฉื™ืช, ื‘ื•ืื• ื ืฉืจื˜ื˜ ืืช ื”ื’ืจืกื” ื”ืคืฉื•ื˜ื” ื‘ื™ื•ืชืจ ืฉืœ ื”ื‘ืงืฉื”, ืชื•ืš ื”ืขื‘ืจืช ืชืขื•ื“ื•ืช ื”ื–ื”ื•ืช ืฉืœ ื”ืžื‘ืฆืขื™ื ืžืขืจืš ื›ืคืจืžื˜ืจ ืงืœื˜:

SELECT
  *
FROM
  task
WHERE
  owner_id = ANY('{1,2,4,8,16,32,64,128,256,512}'::integer[])
ORDER BY
  task_date, id
LIMIT 20;

SQL HowTo: ื›ืชื•ื‘ ืœื•ืœืืช while ื™ืฉื™ืจื•ืช ื‘ืฉืื™ืœืชื”, ืื• "ืืœืžื ื˜ืจื™ ืชืœืช ื›ื™ื•ื•ื ื™"
[ื”ืกืชื›ืœ ื‘-explain.tensor.ru]

ืงืฆืช ืขืฆื•ื‘ - ื”ื–ืžื ื• ืจืง 20 ืชืงืœื™ื˜ื™ื, ืื‘ืœ ืื™ื ื“ืงืก ืกืจื™ืงื” ื”ื—ื–ื™ืจื” ืœื ื• 960 ืฉื•ืจื•ืช, ืฉืื– ื’ื ื”ื™ื” ืฆืจื™ืš ืœืžื™ื™ืŸ... ื‘ื•ืื• ื ื ืกื” ืœืงืจื•ื ืคื—ื•ืช.

unnest + ARRAY

ื”ืฉื™ืงื•ืœ ื”ืจืืฉื•ืŸ ืฉื™ืขื–ื•ืจ ืœื ื• ื”ื•ื ืื ื ืฆื˜ืจืš ืจืง 20 ืžืžื•ื™ื ื™ื ืจืฉื•ืžื•ืช, ื•ืื– ืคืฉื•ื˜ ืœืงืจื•ื ืœื ื™ื•ืชืจ ืž-20 ืžืžื•ื™ื ื™ื ื‘ืื•ืชื• ืกื“ืจ ืขื‘ื•ืจ ื›ืœ ืื—ื“ ืžึทืคึฐืชึตื—ึท. ื˜ื•ึนื‘, ืื™ื ื“ืงืก ืžืชืื™ื (owner_id, task_date, id) ื™ืฉ ืœื ื•.

ื‘ื•ืื• ื ืฉืชืžืฉ ื‘ืื•ืชื• ืžื ื’ื ื•ืŸ ืœื—ื™ืœื•ืฅ ื•"ื”ืชืคืฉื˜ื•ืช ืœืขืžื•ื“ื•ืช" ืจืฉื•ืžืช ื˜ื‘ืœื” ืื™ื ื˜ื’ืจืœื™ืช, ื›ืžื• ื‘ ื”ืžืืžืจ ื”ืื—ืจื•ืŸ. ืื ื• ื™ื›ื•ืœื™ื ื’ื ืœื™ื™ืฉื ืงื™ืคื•ืœ ืœืžืขืจืš ื‘ืืžืฆืขื•ืช ื”ืคื•ื ืงืฆื™ื” ARRAY():

WITH T AS (
  SELECT
    unnest(ARRAY(
      SELECT
        t
      FROM
        task t
      WHERE
        owner_id = unnest
      ORDER BY
        task_date, id
      LIMIT 20 -- ะพะณั€ะฐะฝะธั‡ะธะฒะฐะตะผ ั‚ัƒั‚...
    )) r
  FROM
    unnest('{1,2,4,8,16,32,64,128,256,512}'::integer[])
)
SELECT
  (r).*
FROM
  T
ORDER BY
  (r).task_date, (r).id
LIMIT 20; -- ... ะธ ั‚ัƒั‚ - ั‚ะพะถะต

SQL HowTo: ื›ืชื•ื‘ ืœื•ืœืืช while ื™ืฉื™ืจื•ืช ื‘ืฉืื™ืœืชื”, ืื• "ืืœืžื ื˜ืจื™ ืชืœืช ื›ื™ื•ื•ื ื™"
[ื”ืกืชื›ืœ ื‘-explain.tensor.ru]

ื”ื•, ื”ืจื‘ื” ื™ื•ืชืจ ื˜ื•ื‘ ื›ื‘ืจ! ืžื”ื™ืจ ื™ื•ืชืจ ื‘-40% ื•ืคื™ 4.5 ืคื—ื•ืช ื ืชื•ื ื™ื ื”ื™ื™ืชื™ ื—ื™ื™ื‘ ืœืงืจื•ื ืื•ืชื•.

ื”ืชืžืžืฉื•ืช ืฉืœ ืจืฉื•ืžื•ืช ื˜ื‘ืœื” ื‘ืืžืฆืขื•ืช CTEื”ืจืฉื• ืœื™ ืœื”ืคื ื•ืช ืืช ืชืฉื•ืžืช ืœื‘ื›ื ืœืขื•ื‘ื“ื” ื–ืืช ื‘ืžืงืจื™ื ืžืกื•ื™ืžื™ื ื ื™ืกื™ื•ืŸ ืœืขื‘ื•ื“ ืžื™ื“ ืขื ื”ืฉื“ื•ืช ืฉืœ ืจืฉื•ืžื” ืœืื—ืจ ื—ื™ืคื•ืฉ ืื—ืจื™ื” ื‘ืฉืื™ืœืชืช ืžืฉื ื”, ืžื‘ืœื™ "ืœืขื˜ื•ืฃ" ืื•ืชื” ื‘-CTE, ื™ื›ื•ืœ ืœื”ื•ื‘ื™ืœ ืœ "ืœื”ื›ืคื™ืœ" ืืช InitPlan ืคืจื•ืคื•ืจืฆื™ื•ื ืœื™ ืœืžืกืคืจ ืฉืœ ืื•ืชื ืฉื“ื•ืช:

SELECT
  ((
    SELECT
      t
    FROM
      task t
    WHERE
      owner_id = 1
    ORDER BY
      task_date, id
    LIMIT 1
  ).*);

Result  (cost=4.77..4.78 rows=1 width=16) (actual time=0.063..0.063 rows=1 loops=1)
  Buffers: shared hit=16
  InitPlan 1 (returns $0)
    ->  Limit  (cost=0.42..1.19 rows=1 width=48) (actual time=0.031..0.032 rows=1 loops=1)
          Buffers: shared hit=4
          ->  Index Scan using task_owner_id_task_date_id_idx on task t  (cost=0.42..387.57 rows=500 width=48) (actual time=0.030..0.030 rows=1 loops=1)
                Index Cond: (owner_id = 1)
                Buffers: shared hit=4
  InitPlan 2 (returns $1)
    ->  Limit  (cost=0.42..1.19 rows=1 width=48) (actual time=0.008..0.009 rows=1 loops=1)
          Buffers: shared hit=4
          ->  Index Scan using task_owner_id_task_date_id_idx on task t_1  (cost=0.42..387.57 rows=500 width=48) (actual time=0.008..0.008 rows=1 loops=1)
                Index Cond: (owner_id = 1)
                Buffers: shared hit=4
  InitPlan 3 (returns $2)
    ->  Limit  (cost=0.42..1.19 rows=1 width=48) (actual time=0.008..0.008 rows=1 loops=1)
          Buffers: shared hit=4
          ->  Index Scan using task_owner_id_task_date_id_idx on task t_2  (cost=0.42..387.57 rows=500 width=48) (actual time=0.008..0.008 rows=1 loops=1)
                Index Cond: (owner_id = 1)
                Buffers: shared hit=4"
  InitPlan 4 (returns $3)
    ->  Limit  (cost=0.42..1.19 rows=1 width=48) (actual time=0.009..0.009 rows=1 loops=1)
          Buffers: shared hit=4
          ->  Index Scan using task_owner_id_task_date_id_idx on task t_3  (cost=0.42..387.57 rows=500 width=48) (actual time=0.009..0.009 rows=1 loops=1)
                Index Cond: (owner_id = 1)
                Buffers: shared hit=4

ืื•ืชื• ืจืฉื•ืžื” "ื ื‘ื“ืงื”" 4 ืคืขืžื™ื... ืขื“ PostgreSQL 11, ื”ืชื ื”ื’ื•ืช ื–ื• ืžืชืจื—ืฉืช ื‘ืื•ืคืŸ ืงื‘ื•ืข, ื•ื”ืคืชืจื•ืŸ ื”ื•ื "ืœืขื˜ื•ืฃ" ืื•ืชื• ื‘-CTE, ืฉื”ื•ื ื’ื‘ื•ืœ ืžื•ื—ืœื˜ ืœืื•ืคื˜ื™ืžื™ื–ืจ ื‘ื’ืจืกืื•ืช ืืœื•.

ืžืฆื‘ืจ ืจืงื•ืจืกื™ื‘ื™

ื‘ื’ืจืกื” ื”ืงื•ื“ืžืช, ื‘ืกืš ื”ื›ืœ ืงืจืื ื• 200 ืฉื•ืจื•ืช ืœืžืขืŸ ื”-20 ื”ื ื“ืจืฉื™ื. ืœื 960, ืืœื ืืคื™ืœื• ืคื—ื•ืช - ื”ืื ื–ื” ืืคืฉืจื™?

ื‘ื•ืื• ื ื ืกื” ืœื”ืฉืชืžืฉ ื‘ื™ื“ืข ืฉืื ื—ื ื• ืฆืจื™ื›ื™ื ืกื”"ื› ืจืฉื•ืžื•ืช. ื›ืœื•ืžืจ, ื ื—ื–ื•ืจ ืขืœ ืงืจื™ืืช ื ืชื•ื ื™ื ืจืง ืขื“ ืฉื ื’ื™ืข ืœื›ืžื•ืช ืฉืื ื• ืฆืจื™ื›ื™ื.

ืฉืœื‘ 1: ืจืฉื™ืžืช ื”ืชื—ืœื•ืช

ื‘ืจื•ืจ, ืจืฉื™ืžืช ื”"ืžื˜ืจื”" ืฉืœื ื• ืฉืœ 20 ืจืฉื•ืžื•ืช ืฆืจื™ื›ื” ืœื”ืชื—ื™ืœ ื‘ืจืฉื•ืžื•ืช ื”"ืจืืฉื•ื ื•ืช" ืขื‘ื•ืจ ืื—ื“ ืžืžืคืชื—ื•ืช ื”-owner_id ืฉืœื ื•. ืœื›ืŸ, ืจืืฉื™ืช ื ืžืฆื ื›ืืœื” "ืจืืฉื•ืŸ ืžืื•ื“" ืขื‘ื•ืจ ื›ืœ ืื—ื“ ืžื”ืžืคืชื—ื•ืช ื•ืœื”ื•ืกื™ืฃ ืื•ืชื• ืœืจืฉื™ืžื”, ืœืžื™ื™ืŸ ืื•ืชื• ืœืคื™ ื”ืกื“ืจ ืฉืื ื—ื ื• ืจื•ืฆื™ื - (ืชืืจื™ืš_ืžืฉื™ืžื”, ืžื–ื”ื”).

SQL HowTo: ื›ืชื•ื‘ ืœื•ืœืืช while ื™ืฉื™ืจื•ืช ื‘ืฉืื™ืœืชื”, ืื• "ืืœืžื ื˜ืจื™ ืชืœืช ื›ื™ื•ื•ื ื™"

ืฉืœื‘ 2: ืžืฆื ืืช ื”ืขืจื›ื™ื "ื”ื‘ืื™ื".

ืขื›ืฉื™ื• ืื ื ื™ืงื— ืืช ื”ืขืจืš ื”ืจืืฉื•ืŸ ืžื”ืจืฉื™ืžื” ืฉืœื ื• ื•ื ืชื—ื™ืœ "ืฆืขื“" ื”ืœืื” ืœืื•ืจืš ื”ืื™ื ื“ืงืก ืฉืžื™ืจื” ืขืœ ืžืคืชื— owner_id, ืื– ื›ืœ ื”ืจืฉื•ืžื•ืช ืฉื ืžืฆืื• ื”ืŸ ื‘ื“ื™ื•ืง ื”ื‘ืื•ืช ื‘ื‘ื—ื™ืจื” ืฉื”ืชืงื‘ืœื”. ื›ืžื•ื‘ืŸ, ืจืง ืขื“ ืฉื ื—ืฆื” ืืช ืžืคืชื— ื”ืชื—ืช ืขืจืš ืฉื ื™ ื‘ืจืฉื™ืžื”.

ืื ื™ืชื‘ืจืจ ืฉ"ื—ืฆื™ื ื•" ืืช ื”ืฉื™ื ื”ืฉื ื™, ืื– ื™ืฉ ืœื”ื•ืกื™ืฃ ืืช ื”ืขืจืš ื”ืื—ืจื•ืŸ ืฉื ืงืจื ืœืจืฉื™ืžื” ื‘ืžืงื•ื ื”ืจืืฉื•ืŸ (ืขื ืื•ืชื• owner_id), ืฉืœืื—ืจื™ื• ืื ื• ืžืžื™ื™ื ื™ื ืžื—ื“ืฉ ืืช ื”ืจืฉื™ืžื” ืฉื•ื‘.

SQL HowTo: ื›ืชื•ื‘ ืœื•ืœืืช while ื™ืฉื™ืจื•ืช ื‘ืฉืื™ืœืชื”, ืื• "ืืœืžื ื˜ืจื™ ืชืœืช ื›ื™ื•ื•ื ื™"

ื›ืœื•ืžืจ, ืชืžื™ื“ ื ืงื‘ืœ ืฉืœืจืฉื™ืžื” ืื™ืŸ ื™ื•ืชืจ ืžื›ื ื™ืกื” ืื—ืช ืœื›ืœ ืื—ื“ ืžื”ืžืคืชื—ื•ืช (ืื ื ื’ืžืจื• ื”ืขืจื›ื™ื ื•ืœื "ื ื—ืฆื”", ื”ืจื™ ืฉื”ืขืจืš ื”ืจืืฉื•ืŸ ืžื”ืจืฉื™ืžื” ืคืฉื•ื˜ ื™ื™ืขืœื ื•ืœื ื™ืชื•ื•ืกืฃ ื›ืœื•ื ), ื•ื”ื ืชืžื™ื“ ืžืกื•ื“ืจ ื‘ืกื“ืจ ืขื•ืœื” ืฉืœ ืžืคืชื— ื”ืืคืœื™ืงืฆื™ื” (ืชืืจื™ืš_ืžืฉื™ืžื”, ืžื–ื”ื”).

SQL HowTo: ื›ืชื•ื‘ ืœื•ืœืืช while ื™ืฉื™ืจื•ืช ื‘ืฉืื™ืœืชื”, ืื• "ืืœืžื ื˜ืจื™ ืชืœืช ื›ื™ื•ื•ื ื™"

ืฉืœื‘ 3: ืกื™ื ื•ืŸ ื•"ื”ืจื—ื‘" ืจืฉื•ืžื•ืช

ื‘ื—ืœืง ืžื”ืฉื•ืจื•ืช ืฉืœ ื”ื‘ื—ื™ืจื” ื”ืจืงื•ืจืกื™ื‘ื™ืช ืฉืœื ื•, ื›ืžื” ืจืฉื•ืžื•ืช rv ืžืฉื•ื›ืคืœื™ื - ืชื—ื™ืœื” ืื ื• ืžื•ืฆืื™ื ื›ื’ื•ืŸ "ื—ืฆื™ื™ืช ื”ื’ื‘ื•ืœ ืฉืœ ื”ืขืจืš ื”ืฉื ื™ ื‘ืจืฉื™ืžื”", ื•ืœืื—ืจ ืžื›ืŸ ืžื—ืœื™ืคื™ื ืื•ืชื• ื‘ืชื•ืจ ื”ืจืืฉื•ืŸ ืžื”ืจืฉื™ืžื”. ืื– ืฆืจื™ืš ืœืกื ืŸ ืืช ื”ื”ืชืจื—ืฉื•ืช ื”ืจืืฉื•ื ื”.

ื”ืฉืื™ืœืชื” ื”ืกื•ืคื™ืช ื”ืžืคื—ื™ื“ื”

WITH RECURSIVE T AS (
  -- #1 : ะทะฐะฝะพัะธะผ ะฒ ัะฟะธัะพะบ "ะฟะตั€ะฒั‹ะต" ะทะฐะฟะธัะธ ะฟะพ ะบะฐะถะดะพะผัƒ ะธะท ะบะปัŽั‡ะตะน ะฝะฐะฑะพั€ะฐ
  WITH wrap AS ( -- "ะผะฐั‚ะตั€ะธะฐะปะธะทัƒะตะผ" record'ั‹, ั‡ั‚ะพะฑั‹ ะพะฑั€ะฐั‰ะตะฝะธะต ะบ ะฟะพะปัะผ ะฝะต ะฒั‹ะทั‹ะฒะฐะปะพ ัƒะผะฝะพะถะตะฝะธั InitPlan/SubPlan
    WITH T AS (
      SELECT
        (
          SELECT
            r
          FROM
            task r
          WHERE
            owner_id = unnest
          ORDER BY
            task_date, id
          LIMIT 1
        ) r
      FROM
        unnest('{1,2,4,8,16,32,64,128,256,512}'::integer[])
    )
    SELECT
      array_agg(r ORDER BY (r).task_date, (r).id) list -- ัะพั€ั‚ะธั€ัƒะตะผ ัะฟะธัะพะบ ะฒ ะฝัƒะถะฝะพะผ ะฟะพั€ัะดะบะต
    FROM
      T
  )
  SELECT
    list
  , list[1] rv
  , FALSE not_cross
  , 0 size
  FROM
    wrap
UNION ALL
  -- #2 : ะฒั‹ั‡ะธั‚ั‹ะฒะฐะตะผ ะทะฐะฟะธัะธ 1-ะณะพ ะฟะพ ะฟะพั€ัะดะบัƒ ะบะปัŽั‡ะฐ, ะฟะพะบะฐ ะฝะต ะฟะตั€ะตัˆะฐะณะฝะตะผ ั‡ะตั€ะตะท ะทะฐะฟะธััŒ 2-ะณะพ
  SELECT
    CASE
      -- ะตัะปะธ ะฝะธั‡ะตะณะพ ะฝะต ะฝะฐะนะดะตะฝะพ ะดะปั ะบะปัŽั‡ะฐ 1-ะน ะทะฐะฟะธัะธ
      WHEN X._r IS NOT DISTINCT FROM NULL THEN
        T.list[2:] -- ัƒะฑะธั€ะฐะตะผ ะตะต ะธะท ัะฟะธัะบะฐ
      -- ะตัะปะธ ะผั‹ ะะ• ะฟะตั€ะตัะตะบะปะธ ะฟั€ะธะบะปะฐะดะฝะพะน ะบะปัŽั‡ 2-ะน ะทะฐะฟะธัะธ
      WHEN X.not_cross THEN
        T.list -- ะฟั€ะพัั‚ะพ ะฟั€ะพั‚ัะณะธะฒะฐะตะผ ั‚ะพั‚ ะถะต ัะฟะธัะพะบ ะฑะตะท ะผะพะดะธั„ะธะบะฐั†ะธะน
      -- ะตัะปะธ ะฒ ัะฟะธัะบะต ัƒะถะต ะฝะตั‚ 2-ะน ะทะฐะฟะธัะธ
      WHEN T.list[2] IS NULL THEN
        -- ะฟั€ะพัั‚ะพ ะฒะพะทะฒั€ะฐั‰ะฐะตะผ ะฟัƒัั‚ะพะน ัะฟะธัะพะบ
        '{}'
      -- ะฟะตั€ะตัะพั€ั‚ะธั€ะพะฒั‹ะฒะฐะตะผ ัะปะพะฒะฐั€ัŒ, ัƒะฑะธั€ะฐั 1-ัŽ ะทะฐะฟะธััŒ ะธ ะดะพะฑะฐะฒะปัั ะฟะพัะปะตะดะฝัŽัŽ ะธะท ะฝะฐะนะดะตะฝะฝั‹ั…
      ELSE (
        SELECT
          coalesce(T.list[2] || array_agg(r ORDER BY (r).task_date, (r).id), '{}')
        FROM
          unnest(T.list[3:] || X._r) r
      )
    END
  , X._r
  , X.not_cross
  , T.size + X.not_cross::integer
  FROM
    T
  , LATERAL(
      WITH wrap AS ( -- "ะผะฐั‚ะตั€ะธะฐะปะธะทัƒะตะผ" record
        SELECT
          CASE
            -- ะตัะปะธ ะฒัะต-ั‚ะฐะบะธ "ะฟะตั€ะตัˆะฐะณะฝัƒะปะธ" ั‡ะตั€ะตะท 2-ัŽ ะทะฐะฟะธััŒ
            WHEN NOT T.not_cross
              -- ั‚ะพ ะฝัƒะถะฝะฐั ะทะฐะฟะธััŒ - ะฟะตั€ะฒะฐั ะธะท ัะฟะฟะธัะบะฐ
              THEN T.list[1]
            ELSE ( -- ะตัะปะธ ะฝะต ะฟะตั€ะตัะตะบะปะธ, ั‚ะพ ะบะปัŽั‡ ะพัั‚ะฐะปัั ะบะฐะบ ะฒ ะฟั€ะตะดั‹ะดัƒั‰ะตะน ะทะฐะฟะธัะธ - ะพั‚ั‚ะฐะปะบะธะฒะฐะตะผัั ะพั‚ ะฝะตะต
              SELECT
                _r
              FROM
                task _r
              WHERE
                owner_id = (rv).owner_id AND
                (task_date, id) > ((rv).task_date, (rv).id)
              ORDER BY
                task_date, id
              LIMIT 1
            )
          END _r
      )
      SELECT
        _r
      , CASE
          -- ะตัะปะธ 2-ะน ะทะฐะฟะธัะธ ัƒะถะต ะฝะตั‚ ะฒ ัะฟะธัะบะต, ะฝะพ ะผั‹ ั…ะพั‚ัŒ ั‡ั‚ะพ-ั‚ะพ ะฝะฐัˆะปะธ
          WHEN list[2] IS NULL AND _r IS DISTINCT FROM NULL THEN
            TRUE
          ELSE -- ะฝะธั‡ะตะณะพ ะฝะต ะฝะฐัˆะปะธ ะธะปะธ "ะฟะตั€ะตัˆะฐะณะฝัƒะปะธ"
            coalesce(((_r).task_date, (_r).id) < ((list[2]).task_date, (list[2]).id), FALSE)
        END not_cross
      FROM
        wrap
    ) X
  WHERE
    T.size < 20 AND -- ะพะณั€ะฐะฝะธั‡ะธะฒะฐะตะผ ั‚ัƒั‚ ะบะพะปะธั‡ะตัั‚ะฒะพ
    T.list IS DISTINCT FROM '{}' -- ะธะปะธ ะฟะพะบะฐ ัะฟะธัะพะบ ะฝะต ะบะพะฝั‡ะธะปัั
)
-- #3 : "ั€ะฐะทะฒะพั€ะฐั‡ะธะฒะฐะตะผ" ะทะฐะฟะธัะธ - ะฟะพั€ัะดะพะบ ะณะฐั€ะฐะฝั‚ะธั€ะพะฒะฐะฝ ะฟะพ ะฟะพัั‚ั€ะพะตะฝะธัŽ
SELECT
  (rv).*
FROM
  T
WHERE
  not_cross; -- ะฑะตั€ะตะผ ั‚ะพะปัŒะบะพ "ะฝะตะฟะตั€ะตัะตะบะฐัŽั‰ะธะต" ะทะฐะฟะธัะธ

SQL HowTo: ื›ืชื•ื‘ ืœื•ืœืืช while ื™ืฉื™ืจื•ืช ื‘ืฉืื™ืœืชื”, ืื• "ืืœืžื ื˜ืจื™ ืชืœืช ื›ื™ื•ื•ื ื™"
[ื”ืกืชื›ืœ ื‘-explain.tensor.ru]

ืœืคื™ื›ืš, ืื ื—ื ื• ื ืกื—ืจ ื‘-50% ืžืงืจื™ืื•ืช ื”ื ืชื•ื ื™ื ื‘ืžืฉืš 20% ืžื–ืžืŸ ื”ื‘ื™ืฆื•ืข. ื›ืœื•ืžืจ, ืื ื™ืฉ ืœืš ืกื™ื‘ื•ืช ืœื”ืืžื™ืŸ ืฉื”ืงืจื™ืื” ืขืฉื•ื™ื” ืœื”ื™ืžืฉืš ื–ืžืŸ ืจื‘ (ืœื“ื•ื’ืžื”, ื”ื ืชื•ื ื™ื ืœืจื•ื‘ ืื™ื ื ื‘ืงื•ื‘ืฅ ื”-Cache, ื•ืืชื” ืฆืจื™ืš ืœืœื›ืช ืœื“ื™ืกืง ื‘ืฉื‘ื™ืœ ื–ื”), ืื– ื‘ื“ืจืš ื–ื• ืืชื” ื™ื›ื•ืœ ืœื”ื™ื•ืช ืคื—ื•ืช ืชืœื•ื™ ื‘ืงืจื™ืื” .

ื‘ื›ืœ ืžืงืจื”, ื–ืžืŸ ื”ื‘ื™ืฆื•ืข ื”ืชื‘ืจืจ ื›ื˜ื•ื‘ ื™ื•ืชืจ ืžืืฉืจ ื‘ืื•ืคืฆื™ื” ื”ืจืืฉื•ื ื” ื”"ื ืื™ื‘ื™ืช". ืื‘ืœ ื‘ืื™ื–ื• ืžืฉืœื•ืฉ ื”ืืคืฉืจื•ื™ื•ืช ื”ืืœื” ืœื”ืฉืชืžืฉ ืชืœื•ื™ ื‘ืš.

ืžืงื•ืจ: www.habr.com

ื”ื•ืกืคืช ืชื’ื•ื‘ื”