SQL HowTo: tuhi tika i te wa poto ki roto i te patai, ki te "Tuatahi toru-taahiraa"

I ia wa, ka puta te mahi ki te rapu i nga raraunga e pa ana ki te whakamahi i te huinga taviri. tae noa ki te whiwhi i te tapeke o nga rekoata e hiahiatia ana.

Ko te tauira tino "ora mau" ko te whakaatu 20 nga raruraru tawhito, kua whakarārangitia i runga i te rarangi o nga kaimahi (hei tauira, i roto i te wehenga kotahi). Mo nga momo "papatohu" whakahaere me nga whakarāpopototanga poto o nga waahi mahi, he maha nga wa e hiahiatia ana he kaupapa rite.

SQL HowTo: tuhi tika i te wa poto ki roto i te patai, ki te "Tuatahi toru-taahiraa"

I roto i tenei tuhinga ka titiro tatou ki te whakatinanatanga i roto i te PostgreSQL o te otinga "kuware" ki taua raruraru, he "maamaa" me te tino uaua algorithm. "koropiko" i roto i te SQL me te ahua puta mai i nga raraunga kua kitea, ka whai hua mo te whanaketanga whanui me te whakamahi i etahi atu keehi rite.

Me tango he huinga raraunga whakamatautau mai Tuhinga o mua. Hei aukati i nga rekoata kua whakaatuhia mai i te "peke" mai i te waa ki te wa ka rite nga uara kua tohua, whakawhänuihia te tohu kaupapa ma te taapiri i te matua matua. I te wa ano, ka whakawhiwhia e tenei te ahurei me te whakapumau i a maatau he maamaa te raupapa o te raupapa:

CREATE INDEX ON task(owner_id, task_date, id);
-- а старый - удалим
DROP INDEX task_owner_id_task_date_idx;

Ko te mea e rangona ana, ko te tuhituhinga

Tuatahi, me tuhi te ahua ngawari o te tono, me te tuku i nga ID o nga kaihaka. huānga hei tawhā tāuru:

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: tuhi tika i te wa poto ki roto i te patai, ki te "Tuatahi toru-taahiraa"
[Tirohia i te explain.tensor.ru]

He iti te pouri - 20 noa nga rekoata i tonohia e matou, engari i whakahokia mai e Index Scan ki a matou 960 raina, katahi ano me whakariterite... Me ngana ki te panui iti.

unnest + ARRAY

Ko te whakaaro tuatahi hei awhina i a tatou mena ka hiahia tatou 20 noa iho te whakariterite rekoata, katahi ka panui noa kaua e neke ake i te 20 kua tohua kia rite tonu te raupapa mo ia kī. pai, taupū pai (owner_id, task_date, id) kei a matou.

Me whakamahi ano i te tikanga mo te tango me te "te hora ki nga pou" rekoata ripanga whakauru, pera i roto Tuhinga whakamutunga. Ka taea hoki te tono kopa ki roto i te huinga ma te whakamahi i te mahi 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: tuhi tika i te wa poto ki roto i te patai, ki te "Tuatahi toru-taahiraa"
[Tirohia i te explain.tensor.ru]

Aue, kua pai ake! 40% tere me te 4.5 wa iti ake nga raraunga Me panui e au.

Te whakakaha o nga rekoata ripanga ma te CTETukua ahau ki te kukume i to whakaaro ki tera i etahi wa Ko te ngana ki te mahi tonu me nga mara o te rekoata i muri i te rapunga i roto i te patai, me te kore e "takai" ki te CTE, ka taea "whakareatia" InitPlan kia rite ki te maha o enei mara:

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

Ko te rekoata ano i "titirohia" e 4 nga wa ... Tae noa ki te PostgreSQL 11, ka puta tenei whanonga i nga wa katoa, a ko te otinga ko te "takai" ki roto i te CTE, he tino rohe mo te kaitirotiro i enei putanga.

Kohikohi Recursive

I roto i te putanga o mua, i roto i te katoa tai'o tatou 200 raina mo te 20 e hiahiatia ana. Ehara i te 960, engari he iti ake - ka taea?

Kia tamata tatou ki te whakamahi i te matauranga e hiahiatia ana e tatou 20 katoa rekoata. Arā, ka huri noa i te panui raraunga kia tae ra ano ki te nui e hiahiatia ana.

Hipanga 1: Rārangi Tīmata

Ko te tikanga, ko ta maatau rarangi "whangai" o nga rekoata 20 me timata ki nga rekoata "tuatahi" mo tetahi o a maatau kii_id_ipurangi. No reira, i te tuatahi ka kitea e tatou penei "tino tuatahi" mo ia o nga ki ka tāpirihia ki te rārangi, ka kōmaka ki te raupapa e hiahia ana mātou - (task_date, id).

SQL HowTo: tuhi tika i te wa poto ki roto i te patai, ki te "Tuatahi toru-taahiraa"

Hipanga 2: Kimihia nga tuhinga "panuku".

Inaianei ki te tango tatou i te urunga tuatahi mai i ta tatou rarangi ka timata "taahihia" i te taha o te taurangi te pupuri i te kii rangatira_id, katahi ko nga rekoata katoa i kitea ko nga mea e whai ake nei i roto i te kowhiringa ka puta. O te akoranga, anake tae noa ki te whakawhiti i te ki o te reke urunga tuarua ki te rarangi.

Mena ka puta mai kua "whiti" tatou i te rekoata tuarua, na ko te panuitanga whakamutunga me tapiri ki te rarangi hei utu mo te mea tuatahi (me te owner_id kotahi), ka mutu ka whakariterite ano matou i te rarangi.

SQL HowTo: tuhi tika i te wa poto ki roto i te patai, ki te "Tuatahi toru-taahiraa"

Arā, ka mohio tonu tatou kia kaua e neke ake i te kotahi te urunga o te rarangi mo ia o nga taviri (mehemea ka pau nga whakaurunga kaore e "whiti", katahi ka ngaro noa te urunga tuatahi mai i te raarangi karekau he taapiri. ), me ratou kōmaka tonu i roto i te raupapa pikinga o te kī tono (task_date, id).

SQL HowTo: tuhi tika i te wa poto ki roto i te patai, ki te "Tuatahi toru-taahiraa"

Hipanga 3: tātari me te "whakawhanui" nga rekoata

I etahi o nga rarangi o ta maatau whiringa recursive, etahi rekoata rv he taarua - i te tuatahi ka kitea penei "te whakawhiti i te taitapa o te urunga tuarua o te rarangi", ka whakakapi hei tuatahi mai i te rarangi. Na me tātari te puta tuatahi.

Ko te patai whakamutunga whakamataku

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: tuhi tika i te wa poto ki roto i te patai, ki te "Tuatahi toru-taahiraa"
[Tirohia i te explain.tensor.ru]

No reira, tatou hokohoko 50% o nga raraunga panui mo te 20% o te wa mahi. Arā, mena kei a koe nga take ki te whakapono ka roa pea te panui (hei tauira, ko nga raraunga kaore i roto i te keteroki, me haere koe ki te kopae mo taua mea), na tenei ka iti ake te whakawhirinaki ki te panui. .

I tetahi take, ko te wa mahi i puta he pai ake i te waahanga tuatahi "naive". Engari ko tehea o enei waahanga e toru hei whakamahi maau.

Source: will.com

Tāpiri i te kōrero