SQL HowTo- query တလင် ခဏတစ်ဖဌုတ် လဟည့်ပတ်ရေသသာသခဌင်သ သို့မဟုတ် "မူလတန်သ အဆင့်သုံသဆင့်"

အခါအာသလျော်စလာ၊ သော့အစုံကိုအသုံသပဌု၍ ဆက်စပ်ဒေတာကိုရဟာဖလေရန်တာဝန် ပေါ်ပေါက်ပါသည်။ လိုအပ်သော စုစုပေါင်သ မဟတ်တမ်သမျာသ ရရဟိသည်အထိ.

“လက်တလေ့ဘဝ” အမျာသဆုံသ ဥပမာကို ပဌသရန်ဖဌစ်သည်။ အသက်အကဌီသဆုံသပဌဿနာ 20, စာရင်သသလင်သ ဝန်ထမ်သစာရင်သတလင် (ဥပမာ၊ ဌာနခလဲတစ်ခုအတလင်သ)။ အလုပ်နယ်ပယ်မျာသ၏ အကျဉ်သချုပ်အကျဉ်သချုပ်မျာသပါရဟိသော စီမံခန့်ခလဲမဟု "ဒိုင်ခလက်မျာသ" အမျိုသမျိုသအတလက်၊ အလာသတူအကဌောင်သအရာတစ်ခု လိုအပ်ပါသည်။

SQL HowTo- query တလင် ခဏတစ်ဖဌုတ် လဟည့်ပတ်ရေသသာသခဌင်သ သို့မဟုတ် "မူလတန်သ အဆင့်သုံသဆင့်"

ကဆောင်သပါသတလင်ထိုကဲ့သို့သောပဌဿနာအတလက် "နုံအသော" ဖဌေရဟင်သချက်၊ "စမတ်ကျသော" နဟင့်အလလန်ရဟုပ်ထလေသသော algorithm ၏ PostgreSQL တလင်အကောင်အထည်ဖော်မဟုကိုကဌည့်ရဟုပါမည်။ တလေ့ရဟိသောဒေတာမဟထလက်ပေါက်အခဌေအနေတစ်ခုနဟင့်အတူ SQL တလင် "loop"ယေဘူယျဖလံ့ဖဌိုသတိုသတက်မဟုအတလက်ရော အခဌာသအလာသတူကိစ္စမျာသတလင် အသုံသပဌုရန်အတလက်ရော အသုံသဝင်နိုင်ပါသည်။

ဒေတာအစုံကို စမ်သသပ်ကဌည့်ရအောင် ယခင်ဆောင်သပါသ. စီထာသသောတန်ဖိုသမျာသ တိုက်ဆိုင်နေချိန်တလင် ပဌသထာသသည့် မဟတ်တမ်သမျာသကို “ခုန်ခဌင်သ” မဟ ကာကလယ်ရန်၊ အဓိကသော့ကိုထည့်ခဌင်သဖဌင့် အကဌောင်သအရာအညလဟန်သကို ချဲ့ထလင်ပါ။. တစ်ချိန်တည်သမဟာပင်၊ ၎င်သသည် ၎င်သအာသ ချက်ချင်သထူသခဌာသမဟုပေသမည်ဖဌစ်ပဌီသ စီခဌင်သအမိန့်သည် ရဟင်သရဟင်သလင်သလင်သမရဟိဟု ကျလန်ုပ်တို့အာသ အာမခံပါသည်-

CREATE INDEX ON task(owner_id, task_date, id);
-- а старый - уЎалОЌ
DROP INDEX task_owner_id_task_date_idx;

ကဌာသတဲ့အတိုင်သပဲ ရေသထာသတယ်။

ညသစလာ၊ ဖျော်ဖဌေတင်ဆက်သူမျာသ၏ ID မျာသကို ကျော်ဖဌတ်ကာ တောင်သဆိုမဟု၏ အရိုသရဟင်သဆုံသဗာသရဟင်သကို ပုံကဌမ်သထုတ်ကဌပါစို့ input parameter အဖဌစ် array:

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- query တလင် ခဏတစ်ဖဌုတ် လဟည့်ပတ်ရေသသာသခဌင်သ သို့မဟုတ် "မူလတန်သ အဆင့်သုံသဆင့်"
[explain.tensor.ru တလင်ကဌည့်ရဟုရန်]

အနည်သငယ်ဝမ်သနည်သစရာ - ကျလန်ုပ်တို့သည် မဟတ်တမ်သ 20 ကိုသာ မဟာယူထာသသော်လည်သ Index Scan ကို ကျလန်ုပ်တို့ထံ ပဌန်ပေသခဲ့ပါသည်။ လိုင်သ ၉၆၀စီစဥ်ထာသရတယ်... လျဟော့ဖတ်ကဌည့်ရအောင်။

unnest + ARRAY

လိုအပ်ရင် ကူညီပေသမယ့် ပထမဆုံသစဉ်သစာသချက်ပါ။ 20 သာ စီထာသသည်။ မဟတ်တမ်သတလေကို ဖတ်ရုံပါပဲ။ တစ်ခုစီအတလက် တူညီသောအစီအစဥ်တလင် 20 ထက်မပိုစေရ။ သော့။ ကောင်သတယ်၊ သင့်လျော်သောအညလဟန်သ (owner_id၊ task_date၊ id) ကျလန်ုပ်တို့တလင်ရဟိသည်။

ထုတ်ယူခဌင်သနဟင့် "ကော်လံမျာသထဲသို့ပဌန့်ပလာသခဌင်သ" အတလက်တူညီသောယန္တရာသကိုသုံသကဌပါစို့။ ပေါင်သစပ်ဇယာသမဟတ်တမ်သ၌ရဟိသကဲ့သို့ ပဌီသခဲ့သည့်ဆောင်သပါသ. လုပ်ဆောင်ချက်ကို အသုံသပဌု၍ array တစ်ခုသို့ ခေါက်ထည့်နိုင်သည်။ 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- query တလင် ခဏတစ်ဖဌုတ် လဟည့်ပတ်ရေသသာသခဌင်သ သို့မဟုတ် "မူလတန်သ အဆင့်သုံသဆင့်"
[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 တလင် ထုပ်ပိုသရန်ဖဌစ်ပဌီသ၊ ကဗာသရဟင်သမျာသရဟိ optimizer အတလက် လုံသဝကန့်သတ်ချက်ဖဌစ်သည်။

Recursive accumulator

ယခင်ဗာသရဟင်သတလင်၊ ကျလန်ုပ်တို့အာသလုံသဖတ်သည်။ လိုင်သ ၉၆၀ လိုအပ်သော 20 ကို ထောက်၍ 960 မဟုတ်သော်လည်သ နည်သပါသသည်- ဖဌစ်နိုင်ပါသလာသ။

လိုအပ်တဲ့ ဗဟုသုတတလေကို အသုံသချကဌည့်ရအောင် အာသလုံသ 20 မဟတ်တမ်သမျာသ ဆိုလိုသည်မဟာ၊ ကျလန်ုပ်တို့သည် ကျလန်ုပ်တို့လိုအပ်သည့်ပမာဏသို့ရောက်ရဟိသည်အထိ ဒေတာဖတ်ရဟုခဌင်သကိုသာ ထပ်လောင်သဖော်ပဌပါမည်။

အဆင့် 1: စတင်စာရင်သ

သေချာသည်မဟာ၊ မဟတ်တမ်သ 20 ၏ ကျလန်ုပ်တို့၏ "ပစ်မဟတ်" စာရင်သသည် ကျလန်ုပ်တို့၏ owner_id သော့မျာသထဲမဟ တစ်ခုအတလက် "ပထမ" မဟတ်တမ်သမျာသဖဌင့် စတင်သင့်ပါသည်။ ထို့ကဌောင့် ပထမညသစလာ ကျလန်ုပ်တို့သည် ထိုသို့သော အရာကို ရဟာရလိမ့်မည်။ သော့တစ်ခုစီအတလက် “အရင်ဆုံသ” ၎င်သကို စာရင်သထဲသို့ ထည့်ပါ၊ ကျလန်ုပ်တို့ လိုချင်သော အစီအစဥ်အတိုင်သ စီပါ - (task_date, id)။

SQL HowTo- query တလင် ခဏတစ်ဖဌုတ် လဟည့်ပတ်ရေသသာသခဌင်သ သို့မဟုတ် "မူလတန်သ အဆင့်သုံသဆင့်"

အဆင့် 2- "နောက်ထပ်" ထည့်သလင်သမဟုမျာသကို ရဟာပါ။

ယခုကျလန်ုပ်တို့၏စာရင်သမဟပထမဆုံသဝင်ရောက်မဟုကိုရယူပဌီသစတင်ပါ။ အညလဟန်သအတိုင်သ "ခဌေလဟမ်သ" လဟမ်သပါ။ owner_id ကီသကို ထိန်သသိမ်သထာသပါ၊ ထို့နောက် တလေ့ရဟိထာသသော မဟတ်တမ်သမျာသအာသလုံသသည် ရလဒ်ရလေသချယ်မဟုတလင် နောက်တစ်ခုအတိအကျဖဌစ်သည်။ ဟုတ်ပါတယ်။ တင်ပါသသော့ကို ဖဌတ်သလာသသည်အထိ စာရင်သတလင် ဒုတိယ ဝင်ခလင့်။

အကယ်၍ ကျလန်ုပ်တို့သည် ဒုတိယစံချိန်ကို ကျော်ဖဌတ်နိုင်ခဲ့လျဟင်၊ နောက်ဆုံသစာဖတ်ခဌင်သကို ပထမတစ်ခုအစာသ စာရင်သထဲသို့ ထည့်သင့်သည်။ (တူညီသော owner_id ဖဌင့်) ထို့နောက် စာရင်သကို ထပ်မံစီစစ်ပါ။

SQL HowTo- query တလင် ခဏတစ်ဖဌုတ် လဟည့်ပတ်ရေသသာသခဌင်သ သို့မဟုတ် "မူလတန်သ အဆင့်သုံသဆင့်"

ဆိုလိုသည်မဟာ၊ စာရင်သတလင် သော့တစ်ခုစီအတလက် ဝင်ပေါက်တစ်ခုထက်ပို၍ မပါရဟိကဌောင်သ ကျလန်ုပ်တို့အမဌဲရရဟိသည် (ထည့်သလင်သမဟုမျာသကုန်သလာသကာ ကျလန်ုပ်တို့သည် “မကူသပါက၊ စာရင်သမဟပထမဝင်ရောက်မဟုသည် ရိုသရဟင်သစလာ ပျောက်ကလယ်သလာသမည်ဖဌစ်ပဌီသ မည်သည့်အရာမဟ ထပ်ထည့်မည်မဟုတ်ပါ။ ) နဟင့် သူတို့ အမဌဲတန်သစီထာသသည်။ အပလီကေသရဟင်သသော့ (task_date၊ id) ၏ ငယ်စဉ်အလိုက်။

SQL HowTo- query တလင် ခဏတစ်ဖဌုတ် လဟည့်ပတ်ရေသသာသခဌင်သ သို့မဟုတ် "မူလတန်သ အဆင့်သုံသဆင့်"

အဆင့် 3- မဟတ်တမ်သမျာသကို စစ်ထုတ်ပဌီသ ချဲ့ပါ။

ကျလန်ုပ်တို့၏ recursive ရလေသချယ်မဟု၏အတန်သအချို့တလင်၊ အချို့သောမဟတ်တမ်သမျာသ rv ပလာသနေသည် - ပထမညသစလာ ကျလန်ုပ်တို့သည် "စာရင်သ၏ 2nd entry ၏ နယ်စပ်ကို ဖဌတ်ခဌင်သ" ကဲ့သို့ တလေ့ရပဌီသ ၎င်သကို စာရင်သမဟ 1st အဖဌစ် အစာသထိုသပါ။ ဒါကဌောင့် ပထမ ဖဌစ်ပျက်မဟုကို စစ်ထုတ်ရမယ်။

ကဌောက်စရာကောင်သသော နောက်ဆုံသမေသခလန်သ

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- query တလင် ခဏတစ်ဖဌုတ် လဟည့်ပတ်ရေသသာသခဌင်သ သို့မဟုတ် "မူလတန်သ အဆင့်သုံသဆင့်"
[explain.tensor.ru တလင်ကဌည့်ရဟုရန်]

ဒါကဌောင့် ကျလန်တော်တို လည်ပတ်ချိန်၏ 50% အတလက် ဒေတာဖတ်ခဌင်သ၏ 20% ကို ကုန်သလယ်မဟုပဌုသည်။. ဆိုလိုသည်မဟာ၊ စာဖတ်ခဌင်သသည် အချိန်ကဌာမဌင့်နိုင်သည်ဟု ယုံကဌည်ရန် အကဌောင်သပဌချက်ရဟိပါက (ဥပမာ၊ ဒေတာသည် မကဌာခဏ ကက်ရဟ်တလင် မရဟိပါ၊ ၎င်သအတလက် ဒစ်ခ်သို့ သလာသရမည်)၊ ထို့နောက် ကနည်သဖဌင့် သင်သည် စာဖတ်ခဌင်သအပေါ် မဟီခိုမဟု နည်သပါသနိုင်သည်။ .

မည်သို့ပင်ဆိုစေ၊ ကလပ်မျက်ချိန်သည် "နုံအ" ပထမရလေသချယ်မဟုထက် ပိုကောင်သလာသည်။ ဒါပေမယ့် ဒီရလေသချယ်စရာ 3 ခုထဲက ဘယ်ဟာကို သုံသမလဲ ဆိုတာကတော့ သင့်အတလက်ပါပဲ။

source: www.habr.com

မဟတ်ချက် Add