рдЖрдЬрд╛рд░реА SQL рдкреНрд░рд╢реНрдирд╛рдВрд╕рд╛рдареА рдкрд╛рдХрдХреГрддреА

рдорд╣рд┐рдиреНрдпрд╛рдкреВрд░реНрд╡реА рдЖрдореНрд╣реА рдШреЛрд╖рдгрд╛ рдХреЗрд▓реА explain.tensor.ru - рд╕рд╛рд░реНрд╡рдЬрдирд┐рдХ рдХреНрд╡реЗрд░реА рдпреЛрдЬрдирд╛ рдкрд╛рд░реНрд╕ рдЖрдгрд┐ рд╡реНрд╣рд┐рдЬреНрдпреБрдЕрд▓рд╛рдпрдЭ рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА рд╕реЗрд╡рд╛ PostgreSQL рд▓рд╛.

рддреЗрд╡реНрд╣рд╛рдкрд╛рд╕реВрди рддреБрдореНрд╣реА рддреЗ 6000 рд╣реВрди рдЕрдзрд┐рдХ рд╡реЗрд│рд╛ рд╡рд╛рдкрд░рд▓реЗ рдЖрд╣реЗ, рдкрд░рдВрддреБ рд╕реБрд▓рдн рд╡реИрд╢рд┐рд╖реНрдЯреНрдпрд╛рдВрдкреИрдХреА рдПрдХ рдХрджрд╛рдЪрд┐рдд рд▓рдХреНрд╖ рди рджрд┐рд▓рд╛ рдЧреЗрд▓реЗрд▓рд╛ рдЕрд╕реЗрд▓. рд╕рдВрд░рдЪрдирд╛рддреНрдордХ рд╕рдВрдХреЗрдд, рдЬреЗ рдпрд╛рд╕рд╛рд░рдЦреЗ рдХрд╛рд╣реАрддрд░реА рджрд┐рд╕рддреЗ:

рдЖрдЬрд╛рд░реА SQL рдкреНрд░рд╢реНрдирд╛рдВрд╕рд╛рдареА рдкрд╛рдХрдХреГрддреА

рддреНрдпрд╛рдВрдЪреЗ рдРрдХрд╛ рдЖрдгрд┐ рддреБрдордЪреНрдпрд╛ рд╡рд┐рдирдВрддреНрдпрд╛ "рд░реЗрд╢рдореА рдЧреБрд│рдЧреБрд│реАрдд рд╣реЛрддреАрд▓". ЁЯЩВ

рдкрд░рдВрддреБ рдЧрдВрднреАрд░рдкрдгреЗ, рд╕рдВрд╕рд╛рдзрдирд╛рдВрдЪреНрдпрд╛ рдмрд╛рдмрддреАрдд рд╡рд┐рдирдВрддреА рд╕рдВрде рдЖрдгрд┐ "рдЦрд╛рджрд╛рдб" рдХрд░рдгрд╛рд▒реНрдпрд╛ рдЕрдиреЗрдХ рдкрд░рд┐рд╕реНрдерд┐рддреА, рддреЗ рд╡реИрд╢рд┐рд╖реНрдЯреНрдпрдкреВрд░реНрдг рдЖрд╣реЗрдд рдЖрдгрд┐ рдпреЛрдЬрдиреЗрдЪреА рд░рдЪрдирд╛ рдЖрдгрд┐ рдбреЗрдЯрд╛ рджреНрд╡рд╛рд░реЗ рдУрд│рдЦрд▓реЗ рдЬрд╛рдК рд╢рдХрддрд╛рдд.

рдпрд╛ рдкреНрд░рдХрд░рдгрд╛рдд, рдкреНрд░рддреНрдпреЗрдХ рд╡реИрдпрдХреНрддрд┐рдХ рд╡рд┐рдХрд╕рдХрд╛рд▓рд╛ рддреНрдпрд╛рдЪреНрдпрд╛ рд╕реНрд╡рдд: рдЪреНрдпрд╛ рдЕрдиреБрднрд╡рд╛рд╡рд░ рдЕрд╡рд▓рдВрдмреВрди рд░рд╛рд╣реВрди, рд╕реНрд╡рддрдГрд╣реВрди рдСрдкреНрдЯрд┐рдорд╛рдпрдЭреЗрд╢рди рдкрд░реНрдпрд╛рдп рд╢реЛрдзрдгреНрдпрд╛рдЪреА рдЧрд░рдЬ рдирд╛рд╣реА - рдЖрдореНрд╣реА рддреНрдпрд╛рд▓рд╛ рд╕рд╛рдВрдЧреВ рд╢рдХрддреЛ рдХреА рдпреЗрдереЗ рдХрд╛рдп рдШрдбрдд рдЖрд╣реЗ, рдХрд╛рд░рдг рдХрд╛рдп рдЕрд╕реВ рд╢рдХрддреЗ рдЖрдгрд┐ рдЙрдкрд╛рдп рдХрд╕рд╛ рд╢реЛрдзрд╛рдпрдЪрд╛. рдЬреЗ рдЖрдореНрд╣реА рдХреЗрд▓реЗ.

рдЖрдЬрд╛рд░реА SQL рдкреНрд░рд╢реНрдирд╛рдВрд╕рд╛рдареА рдкрд╛рдХрдХреГрддреА

рдЪрд▓рд╛ рдпрд╛ рдкреНрд░рдХрд░рдгрд╛рдВрд╡рд░ рдмрд╛рд░рдХрд╛рдИрдиреЗ рдирдЬрд░ рдЯрд╛рдХреВрдпрд╛ - рддреЗ рдХрд╕реЗ рдкрд░рд┐рднрд╛рд╖рд┐рдд рдХреЗрд▓реЗ рдЬрд╛рддрд╛рдд рдЖрдгрд┐ рддреЗ рдХреЛрдгрддреНрдпрд╛ рд╢рд┐рдлрд╛рд░рд╕реА рджреЗрддрд╛рдд.

рд╡рд┐рд╖рдпрд╛рдордзреНрдпреЗ рдЕрдзрд┐рдХ рдЪрд╛рдВрдЧрд▓реНрдпрд╛ рдкреНрд░рдХрд╛рд░реЗ рд╡рд┐рд╕рд░реНрдЬрдирд╛рд╕рд╛рдареА, рдЖрдкрдг рдкреНрд░рдердо рд╕рдВрдмрдВрдзрд┐рдд рдмреНрд▓реЙрдХ рдХрдбреВрди рдРрдХреВ рд╢рдХрддрд╛ PGConf.Russia 2020 рдпреЗрдереЗ рдорд╛рдЭрд╛ рдЕрд╣рд╡рд╛рд▓, рдЖрдгрд┐ рддреНрдпрд╛рдирдВрддрд░рдЪ рдкреНрд░рддреНрдпреЗрдХ рдЙрджрд╛рд╣рд░рдгрд╛рдЪреНрдпрд╛ рддрдкрд╢реАрд▓рд╡рд╛рд░ рд╡рд┐рд╢реНрд▓реЗрд╖рдгрд╛рдХрдбреЗ рдЬрд╛:

#1: рдЕрдиреБрдХреНрд░рдордгрд┐рдХрд╛ "рдЕрдВрдбрд░рд╕реЙрд░реНрдЯрд┐рдВрдЧ"

рдЬреЗрд╡реНрд╣рд╛ рдЙрджреНрднрд╡рддреЗ

рдХреНрд▓рд╛рдпрдВрдЯ "LLC Kolokolchik" рд╕рд╛рдареА рд╢реЗрд╡рдЯрдЪреЗ рдмреАрдЬрдХ рджрд░реНрд╢рд╡рд╛.

рдХрд╕реЗ рдУрд│рдЦрд╛рд╡реЗ

-> Limit
   -> Sort
      -> Index [Only] Scan [Backward] | Bitmap Heap Scan

рд╢рд┐рдлрд╛рд░рд╕реА

рдирд┐рд░реНрджреЗрд╢рд╛рдВрдХ рд╡рд╛рдкрд░рд▓реЗ рдХреНрд░рдорд╡рд╛рд░реА рдлреАрд▓реНрдбрд╕рд╣ рд╡рд┐рд╕реНрддреГрдд рдХрд░рд╛.

рдЙрджрд╛рд╣рд░рдг:

CREATE TABLE tbl AS
SELECT
  generate_series(1, 100000) pk  -- 100K "╤Д╨░╨║╤В╨╛╨▓"
, (random() * 1000)::integer fk_cli; -- 1K ╤А╨░╨╖╨╜╤Л╤Е ╨▓╨╜╨╡╤И╨╜╨╕╤Е ╨║╨╗╤О╤З╨╡╨╣

CREATE INDEX ON tbl(fk_cli); -- ╨╕╨╜╨┤╨╡╨║╤Б ╨┤╨╗╤П foreign key

SELECT
  *
FROM
  tbl
WHERE
  fk_cli = 1 -- ╨╛╤В╨▒╨╛╤А ╨┐╨╛ ╨║╨╛╨╜╨║╤А╨╡╤В╨╜╨╛╨╣ ╤Б╨▓╤П╨╖╨╕
ORDER BY
  pk DESC -- ╤Е╨╛╤В╨╕╨╝ ╨▓╤Б╨╡╨│╨╛ ╨╛╨┤╨╜╤Г "╨┐╨╛╤Б╨╗╨╡╨┤╨╜╤О╤О" ╨╖╨░╨┐╨╕╤Б╤М
LIMIT 1;

рдЖрдЬрд╛рд░реА SQL рдкреНрд░рд╢реНрдирд╛рдВрд╕рд╛рдареА рдкрд╛рдХрдХреГрддреА
[explain.tensor.ru рдкрд╣рд╛]

рддреБрдореНрд╣реА рддрд╛рдмрдбрддреЛрдм рд▓рдХреНрд╖рд╛рдд рдШреЗрдК рд╢рдХрддрд╛ рдХреА рдЕрдиреБрдХреНрд░рдордгрд┐рдХреЗрджреНрд╡рд╛рд░реЗ 100 рдкреЗрдХреНрд╖рд╛ рдЬрд╛рд╕реНрдд рд░реЗрдХреЙрд░реНрдб рд╡рдЬрд╛ рдХреЗрд▓реЗ рдЧреЗрд▓реЗ рд╣реЛрддреЗ, рдЬреЗ рдирдВрддрд░ рд╕рд░реНрд╡ рдХреНрд░рдорд╡рд╛рд░реА рд▓рд╛рд╡рд▓реЗ рдЧреЗрд▓реЗ рд╣реЛрддреЗ рдЖрдгрд┐ рдирдВрддрд░ рдлрдХреНрдд рдПрдХрдЪ рдЙрд░рд▓рд╛ рд╣реЛрддрд╛.

рдЖрдореНрд╣реА рдирд┐рд░рд╛рдХрд░рдг рдХрд░рддреЛ:

DROP INDEX tbl_fk_cli_idx;
CREATE INDEX ON tbl(fk_cli, pk DESC); -- ╨┤╨╛╨▒╨░╨▓╨╕╨╗╨╕ ╨║╨╗╤О╤З ╤Б╨╛╤А╤В╨╕╤А╨╛╨▓╨║╨╕

рдЖрдЬрд╛рд░реА SQL рдкреНрд░рд╢реНрдирд╛рдВрд╕рд╛рдареА рдкрд╛рдХрдХреГрддреА
[explain.tensor.ru рдкрд╣рд╛]

рдЕрд╢рд╛ рдЖрджрд┐рдо рдирдореБрдиреНрдпрд╛рд╡рд░рд╣реА - 8.5x рдЬрд▓рдж рдЖрдгрд┐ 33x рдХрдореА рд╡рд╛рдЪрди. рдкреНрд░рднрд╛рд╡ рдЕрдзрд┐рдХ рд╕реНрдкрд╖реНрдЯ рд╣реЛрдИрд▓, рдкреНрд░рддреНрдпреЗрдХ рдореВрд▓реНрдпрд╛рд╕рд╛рдареА рдЖрдкрд▓реНрдпрд╛рдХрдбреЗ рдЕрдзрд┐рдХ "рддрдереНрдпреЗ" рдЕрд╕рддреАрд▓. fk.

рдореА рд▓рдХреНрд╖рд╛рдд рдШреЗрддреЛ рдХреА рдЕрд╢реА рдЕрдиреБрдХреНрд░рдордгрд┐рдХрд╛ "рдЙрдкрд╕рд░реНрдЧ" рдирд┐рд░реНрджреЗрд╢рд╛рдВрдХ рдореНрд╣рдгреВрди рдХрд╛рд░реНрдп рдХрд░реЗрд▓ рдЗрддрд░ рдкреНрд░рд╢реНрдирд╛рдВрд╕рд╛рдареА рдорд╛рдЧреАрд▓рдкреЗрдХреНрд╖рд╛ рд╡рд╛рдИрдЯ рдирд╛рд╣реА fk, рдЬреЗрдереЗ рдХреНрд░рдорд╡рд╛рд░реА рд▓рд╛рд╡рдд рдЖрд╣реЗ pk рдирд╡реНрд╣рддреЗ рдЖрдгрд┐ рдирд╛рд╣реА (рдЖрдкрдг рдпрд╛рдмрджреНрджрд▓ рдЕрдзрд┐рдХ рд╡рд╛рдЪреВ рд╢рдХрддрд╛ рдЕрдХрд╛рд░реНрдпрдХреНрд╖рдо рдЕрдиреБрдХреНрд░рдордгрд┐рдХрд╛ рд╢реЛрдзрдгреНрдпрд╛рдмрджреНрджрд▓ рдорд╛рдЭреНрдпрд╛ рд▓реЗрдЦрд╛рдд). рд╡рд┐рд╢реЗрд╖рддрдГ, рддреЗ рд╕рд╛рдорд╛рдиреНрдп рдкреНрд░рджрд╛рди рдХрд░реЗрд▓ рд╕реНрдкрд╖реНрдЯ рд╡рд┐рджреЗрд╢реА рдХреА рд╕рдорд░реНрдерди рдпрд╛ рдХреНрд╖реЗрддреНрд░рд╛рджреНрд╡рд╛рд░реЗ.

#2: рдЕрдиреБрдХреНрд░рдордгрд┐рдХрд╛ рдЫреЗрджрдирдмрд┐рдВрджреВ (рдмрд┐рдЯрдореЕрдк рдЖрдгрд┐)

рдЬреЗрд╡реНрд╣рд╛ рдЙрджреНрднрд╡рддреЗ

"NJSC Lyutik" рдЪреНрдпрд╛ рд╡рддреАрдиреЗ рд╕рдорд╛рд░реЛрдк рдЭрд╛рд▓реЗрд▓реНрдпрд╛ "LLC Kolokolchik" рдХреНрд▓рд╛рдпрдВрдЯрд╕рд╛рдареА рд╕рд░реНрд╡ рдХрд░рд╛рд░ рджрд░реНрд╢рд╡рд╛.

рдХрд╕реЗ рдУрд│рдЦрд╛рд╡реЗ

-> BitmapAnd
   -> Bitmap Index Scan
   -> Bitmap Index Scan

рд╢рд┐рдлрд╛рд░рд╕реА

рддрдпрд╛рд░ рд╕рдВрдорд┐рд╢реНрд░ рдирд┐рд░реНрджреЗрд╢рд╛рдВрдХ рджреЛрдиреНрд╣реА рд╕реНрддреНрд░реЛрддрд╛рдВрдЪреНрдпрд╛ рдлреАрд▓реНрдбрджреНрд╡рд╛рд░реЗ рдХрд┐рдВрд╡рд╛ рджреБрд╕рд░реНтАНрдпрд╛рдордзреВрди рд╡рд┐рджреНрдпрдорд╛рди рдлреАрд▓реНрдбрдкреИрдХреА рдПрдХ рд╡рд┐рд╕реНрддреГрдд рдХрд░рд╛.

рдЙрджрд╛рд╣рд░рдг:

CREATE TABLE tbl AS
SELECT
  generate_series(1, 100000) pk      -- 100K "╤Д╨░╨║╤В╨╛╨▓"
, (random() *  100)::integer fk_org  -- 100 ╤А╨░╨╖╨╜╤Л╤Е ╨▓╨╜╨╡╤И╨╜╨╕╤Е ╨║╨╗╤О╤З╨╡╨╣
, (random() * 1000)::integer fk_cli; -- 1K ╤А╨░╨╖╨╜╤Л╤Е ╨▓╨╜╨╡╤И╨╜╨╕╤Е ╨║╨╗╤О╤З╨╡╨╣

CREATE INDEX ON tbl(fk_org); -- ╨╕╨╜╨┤╨╡╨║╤Б ╨┤╨╗╤П foreign key
CREATE INDEX ON tbl(fk_cli); -- ╨╕╨╜╨┤╨╡╨║╤Б ╨┤╨╗╤П foreign key

SELECT
  *
FROM
  tbl
WHERE
  (fk_org, fk_cli) = (1, 999); -- ╨╛╤В╨▒╨╛╤А ╨┐╨╛ ╨║╨╛╨╜╨║╤А╨╡╤В╨╜╨╛╨╣ ╨┐╨░╤А╨╡

рдЖрдЬрд╛рд░реА SQL рдкреНрд░рд╢реНрдирд╛рдВрд╕рд╛рдареА рдкрд╛рдХрдХреГрддреА
[explain.tensor.ru рдкрд╣рд╛]

рдЖрдореНрд╣реА рдирд┐рд░рд╛рдХрд░рдг рдХрд░рддреЛ:

DROP INDEX tbl_fk_org_idx;
CREATE INDEX ON tbl(fk_org, fk_cli);

рдЖрдЬрд╛рд░реА SQL рдкреНрд░рд╢реНрдирд╛рдВрд╕рд╛рдареА рдкрд╛рдХрдХреГрддреА
[explain.tensor.ru рдкрд╣рд╛]

рдпреЗрдереЗ рдлрд╛рдпрджрд╛ рдХрдореА рдЖрд╣реЗ, рдХрд╛рд░рдг рдмрд┐рдЯрдореЕрдк рд╣реАрдк рд╕реНрдХреЕрди рд╕реНрд╡рддрдГрдЪ рдкреНрд░рднрд╛рд╡реА рдЖрд╣реЗ. рдкрдг рдЕрд╕реЛ 7x рдЬрд▓рдж рдЖрдгрд┐ 2.5x рдХрдореА рд╡рд╛рдЪрди.

#3: рдЕрдиреБрдХреНрд░рдордгрд┐рдХрд╛ рдПрдХрддреНрд░ рдХрд░рдгреЗ (рдмрд┐рдЯрдореЕрдкрдУрдЖрд░)

рдЬреЗрд╡реНрд╣рд╛ рдЙрджреНрднрд╡рддреЗ

рдкрд╣рд┐рд▓реНрдпрд╛ 20 рд╕рд░реНрд╡рд╛рдд рдЬреБрдиреНрдпрд╛ "рд╕реНрд╡рддрдГрдЪреНрдпрд╛" рдХрд┐рдВрд╡рд╛ рдкреНрд░рдХреНрд░рд┐рдпреЗрд╕рд╛рдареА рдирд┐рдпреБрдХреНрдд рди рдХреЗрд▓реЗрд▓реНрдпрд╛ рд╡рд┐рдирдВрддреНрдпрд╛, рд╕реНрд╡рддрдГрдЪреНрдпрд╛ рдкреНрд░рд╛рдзрд╛рдиреНрдпрд╛рд╕рд╣ рджрд░реНрд╢рд╡рд╛.

рдХрд╕реЗ рдУрд│рдЦрд╛рд╡реЗ

-> BitmapOr
   -> Bitmap Index Scan
   -> Bitmap Index Scan

рд╢рд┐рдлрд╛рд░рд╕реА

рд╡рд╛рдкрд░рд╛ рдпреБрдирд┐рдпрди [рд╕рд░реНрд╡] рдкреНрд░рддреНрдпреЗрдХ рдХрдВрдбрд┐рд╢рди рдХрд┐рдВрд╡рд╛ рдмреНрд▓реЙрдХрд╕рд╛рдареА рд╕рдмрдХреНрд╡реЗрд░реА рдПрдХрддреНрд░ рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА.

рдЙрджрд╛рд╣рд░рдг:

CREATE TABLE tbl AS
SELECT
  generate_series(1, 100000) pk  -- 100K "╤Д╨░╨║╤В╨╛╨▓"
, CASE
    WHEN random() < 1::real/16 THEN NULL -- ╤Б ╨▓╨╡╤А╨╛╤П╤В╨╜╨╛╤Б╤В╤М╤О 1:16 ╨╖╨░╨┐╨╕╤Б╤М "╨╜╨╕╤З╤М╤П"
    ELSE (random() * 100)::integer -- 100 ╤А╨░╨╖╨╜╤Л╤Е ╨▓╨╜╨╡╤И╨╜╨╕╤Е ╨║╨╗╤О╤З╨╡╨╣
  END fk_own;

CREATE INDEX ON tbl(fk_own, pk); -- ╨╕╨╜╨┤╨╡╨║╤Б ╤Б "╨▓╤А╨╛╨┤╨╡ ╨║╨░╨║ ╨┐╨╛╨┤╤Е╨╛╨┤╤П╤Й╨╡╨╣" ╤Б╨╛╤А╤В╨╕╤А╨╛╨▓╨║╨╛╨╣

SELECT
  *
FROM
  tbl
WHERE
  fk_own = 1 OR -- ╤Б╨▓╨╛╨╕
  fk_own IS NULL -- ... ╨╕╨╗╨╕ "╨╜╨╕╤З╤М╨╕"
ORDER BY
  pk
, (fk_own = 1) DESC -- ╤Б╨╜╨░╤З╨░╨╗╨░ "╤Б╨▓╨╛╨╕"
LIMIT 20;

рдЖрдЬрд╛рд░реА SQL рдкреНрд░рд╢реНрдирд╛рдВрд╕рд╛рдареА рдкрд╛рдХрдХреГрддреА
[explain.tensor.ru рдкрд╣рд╛]

рдЖрдореНрд╣реА рдирд┐рд░рд╛рдХрд░рдг рдХрд░рддреЛ:

(
  SELECT
    *
  FROM
    tbl
  WHERE
    fk_own = 1 -- ╤Б╨╜╨░╤З╨░╨╗╨░ "╤Б╨▓╨╛╨╕" 20
  ORDER BY
    pk
  LIMIT 20
)
UNION ALL
(
  SELECT
    *
  FROM
    tbl
  WHERE
    fk_own IS NULL -- ╨┐╨╛╤В╨╛╨╝ "╨╜╨╕╤З╤М╨╕" 20
  ORDER BY
    pk
  LIMIT 20
)
LIMIT 20; -- ╨╜╨╛ ╨▓╤Б╨╡╨│╨╛ - 20, ╨▒╨╛╨╗╤М╤И╨╡ ╨╕ ╨╜╨╡ ╨╜╨░╨┤╨╛

рдЖрдЬрд╛рд░реА SQL рдкреНрд░рд╢реНрдирд╛рдВрд╕рд╛рдареА рдкрд╛рдХрдХреГрддреА
[explain.tensor.ru рдкрд╣рд╛]

рдЖрдореНрд╣реА рдпрд╛ рд╡рд╕реНрддреБрд╕реНрдерд┐рддреАрдЪрд╛ рдлрд╛рдпрджрд╛ рдШреЗрддрд▓рд╛ рдХреА рдкрд╣рд┐рд▓реНрдпрд╛ рдмреНрд▓реЙрдХрдордзреНрдпреЗ рд╕рд░реНрд╡ 20 рдЖрд╡рд╢реНрдпрдХ рдиреЛрдВрджреА рддрд╛рдмрдбрддреЛрдм рдкреНрд░рд╛рдкреНрдд рдЭрд╛рд▓реНрдпрд╛ рд╣реЛрддреНрдпрд╛, рдореНрд╣рдгреВрди рджреБрд╕рд░рд╛, рдЕрдзрд┐рдХ "рдорд╣рд╛рдЧ" рдмрд┐рдЯрдореЕрдк рд╣реАрдк рд╕реНрдХреЕрдирд╕рд╣, рдЕрдВрдорд▓рд╛рдд рдЖрдгрд▓рд╛ рдЧреЗрд▓рд╛ рдирд╛рд╣реА - рдкрд░рд┐рдгрд╛рдореА 22x рдЬрд▓рдж, 44x рдХрдореА рд╡рд╛рдЪрди!

рдпрд╛ рдСрдкреНрдЯрд┐рдорд╛рдпрдЭреЗрд╢рди рдкрджреНрдзрддреАрдмрджреНрджрд▓ рдЕрдзрд┐рдХ рддрдкрд╢реАрд▓рд╡рд╛рд░ рдХрдерд╛ рдареЛрд╕ рдЙрджрд╛рд╣рд░рдгрд╛рдВрд╡рд░ рд▓реЗрдЦрд╛рдВрдордзреНрдпреЗ рд╡рд╛рдЪрддрд╛ рдпреЗрдИрд▓ PostgreSQL рдЕрдБрдЯреАрдкреЕрдЯрд░реНрди: рд╣рд╛рдирд┐рдХрд╛рд░рдХ рд╕рд╛рдореАрд▓ рд╣реЛрдгреЗ рдЖрдгрд┐ ORs ╨╕ рдкреЛрд╕реНрдЯрдЧреНрд░реЗрдПрд╕рдХреНрдпреВрдПрд▓ рдЕрдБрдЯреАрдкреЕрдЯрд░реНрди: рдирд╛рд╡рд╛рдиреБрд╕рд╛рд░ рд╢реЛрдзрд╛рдЪреНрдпрд╛ рдкреБрдирд░рд╛рд╡реГрддреНрддреА рдкрд░рд┐рд╖реНрдХрд░рдгрд╛рдЪреА рдХрдерд╛, рдХрд┐рдВрд╡рд╛ "рдкреБрдвреЗ рдЖрдгрд┐ рдкреБрдвреЗ рдСрдкреНрдЯрд┐рдорд╛рдЗрдЭ рдХрд░рдгреЗ".

рд╕рд╛рдорд╛рдиреНрдпреАрдХреГрдд рдЖрд╡реГрддреНрддреА рдЕрдиреЗрдХ рдХреА рджреНрд╡рд╛рд░реЗ рдСрд░реНрдбрд░ рдХреЗрд▓реЗрд▓реА рдирд┐рд╡рдб (рдЖрдгрд┐ рдлрдХреНрдд const/NULL рдЪреНрдпрд╛ рдЬреЛрдбреАрд╕рд╛рдареА рдирд╛рд╣реА) рд▓реЗрдЦрд╛рдд рдЪрд░реНрдЪрд╛ рдХреЗрд▓реА рдЖрд╣реЗ SQL HowTo: рдХреНрд╡реЗрд░реАрдордзреНрдпреЗ рдереЗрдЯ рдПрдХ while-loop рд▓рд┐рд╣рд╛, рдХрд┐рдВрд╡рд╛ "Elementary three-way".

#4: рдЖрдкрдг рдЦреВрдк рд╡рд╛рдЪрддреЛ

рдЬреЗрд╡реНрд╣рд╛ рдЙрджреНрднрд╡рддреЗ

рдирд┐рдпрдорд╛рдиреБрд╕рд╛рд░, рдЬреЗрд╡реНрд╣рд╛ рддреБрдореНрд╣реА рд╡рд┐рджреНрдпрдорд╛рди рд╡рд┐рдирдВрддреАрд▓рд╛ тАЬрджреБрд╕рд░рд╛ рдлрд┐рд▓реНрдЯрд░ рд╕рдВрд▓рдЧреНрдитАЭ рдХрд░реВ рдЗрдЪреНрдЫрд┐рдд рдЕрд╕рд╛рд▓ рддреЗрд╡реНрд╣рд╛ рдЕрд╕реЗ рд╣реЛрддреЗ.

"рдЖрдгрд┐ рддреБрдордЪреНрдпрд╛рдХрдбреЗ рддреЗрдЪ рдирд╛рд╣реА, рдкрдг рдореЛрддреА рдмрдЯрдгреЗ рд╕рд╣? рдЪрд┐рддреНрд░рдкрдЯ "рдбрд╛рдпрдордВрдб рд╣рдБрдб"

рдЙрджрд╛рд╣рд░рдгрд╛рд░реНрде, рд╡рд░реАрд▓ рдХрд╛рд░реНрдпрд╛рдд рдмрджрд▓ рдХрд░реВрди, рдкреНрд░рдХреНрд░рд┐рдпреЗрд╕рд╛рдареА рдкрд╣рд┐рд▓реНрдпрд╛ 20 рд╕рд░реНрд╡рд╛рдд рдЬреБрдиреНрдпрд╛ "рдЧрдВрднреАрд░" рд╡рд┐рдирдВрддреНрдпрд╛ рджрд░реНрд╢рд╡рд╛, рддреНрдпрд╛рдВрдЪрд╛ рдЙрджреНрджреЗрд╢ рдХрд╛рд╣реАрд╣реА рдЕрд╕реЛ.

рдХрд╕реЗ рдУрд│рдЦрд╛рд╡реЗ

-> Seq Scan | Bitmap Heap Scan | Index [Only] Scan [Backward]
   && 5 ├Ч rows < RRbF -- ╨╛╤В╤Д╨╕╨╗╤М╤В╤А╨╛╨▓╨░╨╜╨╛ >80% ╨┐╤А╨╛╤З╨╕╤В╨░╨╜╨╜╨╛╨│╨╛
   && loops ├Ч RRbF > 100 -- ╨╕ ╨┐╤А╨╕ ╤Н╤В╨╛╨╝ ╨▒╨╛╨╗╤М╤И╨╡ 100 ╨╖╨░╨┐╨╕╤Б╨╡╨╣ ╤Б╤Г╨╝╨╝╨░╤А╨╜╨╛

рд╢рд┐рдлрд╛рд░рд╕реА

[рдЕрдзрд┐рдХ] рд╡рд┐рд╢реЗрд╖ рддрдпрд╛рд░ рдХрд░рд╛ WHERE рдХреНрд▓реЙрдЬрд╕рд╣ рдЕрдиреБрдХреНрд░рдордгрд┐рдХрд╛ рдХрд┐рдВрд╡рд╛ рдирд┐рд░реНрджреЗрд╢рд╛рдВрдХрд╛рдд рдЕрддрд┐рд░рд┐рдХреНрдд рдлреАрд▓реНрдб рд╕рдорд╛рд╡рд┐рд╖реНрдЯ рдХрд░рд╛.

рдЬрд░ рдлрд┐рд▓реНрдЯрд░рд┐рдВрдЧ рд╕реНрдерд┐рддреА рддреБрдордЪреНрдпрд╛ рдХрд╛рд░реНрдпрд╛рдВрд╕рд╛рдареА "рд╕реНрдерд┐рд░" рдЕрд╕реЗрд▓ - рдореНрд╣рдгрдЬреЗ рд╡рд┐рд╕реНрддрд╛рд░рд╛рдЪрд╛ рд╕рдорд╛рд╡реЗрд╢ рдирд╛рд╣реА рднрд╡рд┐рд╖реНрдпрд╛рддреАрд▓ рдореВрд▓реНрдпрд╛рдВрдЪреА рдпрд╛рджреА - WHERE рдирд┐рд░реНрджреЗрд╢рд╛рдВрдХ рд╡рд╛рдкрд░рдгреЗ рдЪрд╛рдВрдЧрд▓реЗ. рдпрд╛ рд╢реНрд░реЗрдгреАрдордзреНрдпреЗ рд╡рд┐рд╡рд┐рдз рдмреБрд▓рд┐рдпрди/рдПрдирдо рд╕реНрдерд┐рддреА рдЪрд╛рдВрдЧрд▓реНрдпрд╛ рдкреНрд░рдХрд╛рд░реЗ рдмрд╕рддрд╛рдд.

рдЧрд╛рд│рдгреНрдпрд╛рдЪреА рд╕реНрдерд┐рддреА рдЕрд╕рд▓реНрдпрд╛рд╕ рднрд┐рдиреНрди рдореВрд▓реНрдпреЗ рдШреЗрдК рд╢рдХрддрд╛рдд, рдпрд╛ рдлреАрд▓реНрдбрд╕рд╣ рдирд┐рд░реНрджреЗрд╢рд╛рдВрдХ рд╡рд┐рд╕реНрддреГрдд рдХрд░рдгреЗ рдЪрд╛рдВрдЧрд▓реЗ рдЖрд╣реЗ - рдЬрд╕реЗ рдХреА рдмрд┐рдЯрдореЕрдк рдЖрдгрд┐ рд╡рд░реАрд▓ рдкрд░рд┐рд╕реНрдерд┐рддреАрдд.

рдЙрджрд╛рд╣рд░рдг:

CREATE TABLE tbl AS
SELECT
  generate_series(1, 100000) pk -- 100K "╤Д╨░╨║╤В╨╛╨▓"
, CASE
    WHEN random() < 1::real/16 THEN NULL
    ELSE (random() * 100)::integer -- 100 ╤А╨░╨╖╨╜╤Л╤Е ╨▓╨╜╨╡╤И╨╜╨╕╤Е ╨║╨╗╤О╤З╨╡╨╣
  END fk_own
, (random() < 1::real/50) critical; -- 1:50, ╤З╤В╨╛ ╨╖╨░╤П╨▓╨║╨░ "╨║╤А╨╕╤В╨╕╤З╨╜╨░╤П"

CREATE INDEX ON tbl(pk);
CREATE INDEX ON tbl(fk_own, pk);

SELECT
  *
FROM
  tbl
WHERE
  critical
ORDER BY
  pk
LIMIT 20;

рдЖрдЬрд╛рд░реА SQL рдкреНрд░рд╢реНрдирд╛рдВрд╕рд╛рдареА рдкрд╛рдХрдХреГрддреА
[explain.tensor.ru рдкрд╣рд╛]

рдЖрдореНрд╣реА рдирд┐рд░рд╛рдХрд░рдг рдХрд░рддреЛ:

CREATE INDEX ON tbl(pk)
  WHERE critical; -- ╨┤╨╛╨▒╨░╨▓╨╕╨╗╨╕ "╤Б╤В╨░╤В╨╕╤З╨╜╨╛╨╡" ╤Г╤Б╨╗╨╛╨▓╨╕╨╡ ╤Д╨╕╨╗╤М╤В╤А╨░╤Ж╨╕╨╕

рдЖрдЬрд╛рд░реА SQL рдкреНрд░рд╢реНрдирд╛рдВрд╕рд╛рдареА рдкрд╛рдХрдХреГрддреА
[explain.tensor.ru рдкрд╣рд╛]

рдЬрд╕реЗ рдЖрдкрдг рдкрд╛рд╣реВ рд╢рдХрддрд╛, рдпреЛрдЬрдиреЗрддреВрди рдлрд┐рд▓реНрдЯрд░рд┐рдВрдЧ рдкреВрд░реНрдгрдкрдгреЗ рдирд┐рдШреВрди рдЧреЗрд▓реЗ рдЖрд╣реЗ рдЖрдгрд┐ рд╡рд┐рдирдВрддреА рдмрдирд▓реА рдЖрд╣реЗ 5 рдкрдЯ рд╡реЗрдЧрд╡рд╛рди.

#5: рд╡рд┐рд░рд│ рдЯреЗрдмрд▓

рдЬреЗрд╡реНрд╣рд╛ рдЙрджреНрднрд╡рддреЗ

рддреБрдордЪреА рд╕реНрд╡рддрдГрдЪреА рдЯрд╛рд╕реНрдХ рдкреНрд░реЛрд╕реЗрд╕рд┐рдВрдЧ рд░рд╛рдВрдЧ рдмрдирд╡рдгреНрдпрд╛рдЪреЗ рд╡рд┐рд╡рд┐рдз рдкреНрд░рдпрддреНрди, рдЬреЗрд╡реНрд╣рд╛ рдЯреЗрдмрд▓рд╡рд░реАрд▓ рдореЛрдареНрдпрд╛ рд╕рдВрдЦреНрдпреЗрдиреЗ рдЕрдкрдбреЗрдЯреНрд╕ / рд░реЗрдХреЙрд░реНрдб рд╣рдЯрд╡рд▓реНрдпрд╛ рдЬрд╛рддрд╛рдд рддреЗрд╡реНрд╣рд╛ рдореЛрдареНрдпрд╛ рд╕рдВрдЦреНрдпреЗрдиреЗ "рдореГрдд" рд░реЗрдХреЙрд░реНрдбрдЪреА рдкрд░рд┐рд╕реНрдерд┐рддреА рдирд┐рд░реНрдорд╛рдг рд╣реЛрддреЗ.

рдХрд╕реЗ рдУрд│рдЦрд╛рд╡реЗ

-> Seq Scan | Bitmap Heap Scan | Index [Only] Scan [Backward]
   && loops ├Ч (rows + RRbF) < (shared hit + shared read) ├Ч 8
      -- ╨┐╤А╨╛╤З╨╕╤В╨░╨╜╨╛ ╨▒╨╛╨╗╤М╤И╨╡ 1KB ╨╜╨░ ╨║╨░╨╢╨┤╤Г╤О ╨╖╨░╨┐╨╕╤Б╤М
   && shared hit + shared read > 64

рд╢рд┐рдлрд╛рд░рд╕реА

рд╕реНрд╡рд╣рд╕реНрддреЗ рдирд┐рдпрдорд┐рддрдкрдгреЗ рдкрд╛рд░ рдкрд╛рдбрдгреЗ рд╡реНрд╣реЕрдХреНрдпреВрдо [рдкреВрд░реНрдг] рдХрд┐рдВрд╡рд╛ рдкреБрд░реЗрд╢реА рд╡рд╛рд░рдВрд╡рд╛рд░ рдкреНрд░рдХреНрд░рд┐рдпрд╛ рд╕рд╛рдзреНрдп рдХрд░рд╛ рдСрдЯреЛрд╡реНрд╣реЕрдХреНрдпреВрдо рддреНрдпрд╛рдЪреЗ рдкреЕрд░рд╛рдореАрдЯрд░реНрд╕ рдлрд╛рдЗрди-рдЯреНрдпреВрди рдХрд░реВрди, рдпрд╛рд╕рд╣ рдПрдХрд╛ рд╡рд┐рд╢рд┐рд╖реНрдЯ рдЯреЗрдмрд▓рд╕рд╛рдареА.

рдмрд╣реБрддреЗрдХ рдкреНрд░рдХрд░рдгрд╛рдВрдордзреНрдпреЗ, рдЕрд╢рд╛ рд╕рдорд╕реНрдпрд╛ рдмрд┐рдЭрдиреЗрд╕ рд▓реЙрдЬрд┐рдХрд╡рд░реВрди рдХреЙрд▓ рдХреЗрд▓реНрдпрд╛рд╡рд░ рдЦрд░рд╛рдм рдХреНрд╡реЗрд░реА рд▓реЗрдЖрдЙрдЯрдореБрд│реЗ рд╣реЛрддрд╛рдд, рдЬрд╕реЗ рдХреА PostgreSQL рдЕрдБрдЯреАрдкреЕрдЯрд░реНрди: "рдореГрдд" рдЪреА рд▓рдврд╛рдИ.

рдкрд░рдВрддреБ рдЖрдкрдг рд╣реЗ рд╕рдордЬреВрди рдШреЗрддрд▓реЗ рдкрд╛рд╣рд┐рдЬреЗ рдХреА рд╡реНрд╣реЕрдХреНрдпреВрдо рдлреБрд▓ рджреЗрдЦреАрд▓ рдиреЗрд╣рдореАрдЪ рдорджрдд рдХрд░реВ рд╢рдХрдд рдирд╛рд╣реА. рдЕрд╢рд╛ рдкреНрд░рдХрд░рдгрд╛рдВрд╕рд╛рдареА, рдЖрдкрдг рд▓реЗрдЦрд╛рддреАрд▓ рдЕрд▓реНрдЧреЛрд░рд┐рджрдорд╕рд╣ рд╕реНрд╡рдд: рд▓рд╛ рдкрд░рд┐рдЪрд┐рдд рдХреЗрд▓реЗ рдкрд╛рд╣рд┐рдЬреЗ. DBA: рдЬреЗрд╡реНрд╣рд╛ рд╡реНрд╣реЕрдХреНрдпреВрдо рдкрд╛рд╕ рд╣реЛрддреЛ, рддреЗрд╡реНрд╣рд╛ рдЖрдореНрд╣реА рдЯреЗрдмрд▓ рд╕реНрд╡рд╣рд╕реНрддреЗ рд╕реНрд╡рдЪреНрдЫ рдХрд░рддреЛ.

#6: рдирд┐рд░реНрджреЗрд╢рд╛рдВрдХрд╛рдЪреНрдпрд╛ "рдордзреНрдпрднрд╛рдЧреА" рдкрд╛рд╕реВрди рд╡рд╛рдЪрди

рдЬреЗрд╡реНрд╣рд╛ рдЙрджреНрднрд╡рддреЗ

рдЕрд╕реЗ рджрд┐рд╕рддреЗ рдХреА рддреНрдпрд╛рдВрдиреА рдереЛрдбреЗрд╕реЗ рд╡рд╛рдЪрд▓реЗ, рдЖрдгрд┐ рд╕рд░реНрд╡рдХрд╛рд╣реА рдЕрдиреБрдХреНрд░рдорд┐рдд рдХреЗрд▓реЗ рдЧреЗрд▓реЗ, рдЖрдгрд┐ рддреНрдпрд╛рдВрдиреА рдХреЛрдгрд╛рд▓рд╛рд╣реА рдЕрддрд┐рд░рд┐рдХреНрдд рдлрд┐рд▓реНрдЯрд░ рдХреЗрд▓реЗ рдирд╛рд╣реА - рдкрд░рдВрддреБ рддрд░реАрд╣реА, рдЖрдореНрд╣рд╛рд▓рд╛ рдкрд╛рд╣рд┐рдЬреЗ рддреНрдпрд╛рдкреЗрдХреНрд╖рд╛ рд▓рдХреНрд╖рдгреАрдп рдЕрдзрд┐рдХ рдкреГрд╖реНрдареЗ рд╡рд╛рдЪрд▓реА рдЧреЗрд▓реА.

рдХрд╕реЗ рдУрд│рдЦрд╛рд╡реЗ

-> Index [Only] Scan [Backward]
   && loops ├Ч (rows + RRbF) < (shared hit + shared read) ├Ч 8
      -- ╨┐╤А╨╛╤З╨╕╤В╨░╨╜╨╛ ╨▒╨╛╨╗╤М╤И╨╡ 1KB ╨╜╨░ ╨║╨░╨╢╨┤╤Г╤О ╨╖╨░╨┐╨╕╤Б╤М
   && shared hit + shared read > 64

рд╢рд┐рдлрд╛рд░рд╕реА

рд╡рд╛рдкрд░рд▓реЗрд▓реНрдпрд╛ рдирд┐рд░реНрджреЗрд╢рд╛рдВрдХрд╛рдЪреА рд░рдЪрдирд╛ рдЖрдгрд┐ рдХреНрд╡реЗрд░реАрдордзреНрдпреЗ рдирд┐рд░реНрджрд┐рд╖реНрдЯ рдХреЗрд▓реЗрд▓реНрдпрд╛ рдореБрдЦреНрдп рдлреАрд▓реНрдбрд╡рд░ рдмрд╛рд░рдХрд╛рдИрдиреЗ рдирдЬрд░ рдЯрд╛рдХрд╛ - рдмрд╣реБрдзрд╛, рдирд┐рд░реНрджреЗрд╢рд╛рдВрдХ рднрд╛рдЧ рд╕реЗрдЯ рдирд╛рд╣реА. рддреБрдореНрд╣рд╛рд▓рд╛ рдмрд╣реБрдзрд╛ рд╕рдорд╛рди рдЕрдиреБрдХреНрд░рдордгрд┐рдХрд╛ рддрдпрд╛рд░ рдХрд░рдгреНрдпрд╛рдЪреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рдЕрд╕реЗрд▓, рдкрд░рдВрддреБ рдЙрдкрд╕рд░реНрдЧ рдлреАрд▓реНрдбрд╢рд┐рд╡рд╛рдп, рдХрд┐рдВрд╡рд╛ рддреНрдпрд╛рдВрдЪреА рдореВрд▓реНрдпреЗ рдкреБрдиреНрд╣рд╛ рд╕рд╛рдВрдЧрд╛рдпрд▓рд╛ рд╢рд┐рдХрд╛.

рдЙрджрд╛рд╣рд░рдг:

CREATE TABLE tbl AS
SELECT
  generate_series(1, 100000) pk      -- 100K "╤Д╨░╨║╤В╨╛╨▓"
, (random() *  100)::integer fk_org  -- 100 ╤А╨░╨╖╨╜╤Л╤Е ╨▓╨╜╨╡╤И╨╜╨╕╤Е ╨║╨╗╤О╤З╨╡╨╣
, (random() * 1000)::integer fk_cli; -- 1K ╤А╨░╨╖╨╜╤Л╤Е ╨▓╨╜╨╡╤И╨╜╨╕╤Е ╨║╨╗╤О╤З╨╡╨╣

CREATE INDEX ON tbl(fk_org, fk_cli); -- ╨▓╤Б╨╡ ╨┐╨╛╤З╤В╨╕ ╨║╨░╨║ ╨▓ #2
-- ╤В╨╛╨╗╤М╨║╨╛ ╨▓╨╛╤В ╨╛╤В╨┤╨╡╨╗╤М╨╜╤Л╨╣ ╨╕╨╜╨┤╨╡╨║╤Б ╨┐╨╛ fk_cli ╨╝╤Л ╤Г╨╢╨╡ ╨┐╨╛╤Б╤З╨╕╤В╨░╨╗╨╕ ╨╗╨╕╤И╨╜╨╕╨╝ ╨╕ ╤Г╨┤╨░╨╗╨╕╨╗╨╕

SELECT
  *
FROM
  tbl
WHERE
  fk_cli = 999 -- ╨░ fk_org ╨╜╨╡ ╨╖╨░╨┤╨░╨╜╨╛, ╤Е╨╛╤В╤П ╤Б╤В╨╛╨╕╤В ╨▓ ╨╕╨╜╨┤╨╡╨║╤Б╨╡ ╤А╨░╨╜╤М╤И╨╡
LIMIT 20;

рдЖрдЬрд╛рд░реА SQL рдкреНрд░рд╢реНрдирд╛рдВрд╕рд╛рдареА рдкрд╛рдХрдХреГрддреА
[explain.tensor.ru рдкрд╣рд╛]

рд╕рд░реНрд╡ рдХрд╛рд╣реА рдареАрдХ рдЖрд╣реЗ рдЕрд╕реЗ рджрд┐рд╕рддреЗ, рдЕрдЧрджреА рдирд┐рд░реНрджреЗрд╢рд╛рдВрдХрд╛рдЪреНрдпрд╛ рдмрд╛рдмрддреАрддрд╣реА, рдкрд░рдВрддреБ рдХрд╕реЗ рддрд░реА рд╕рдВрд╢рдпрд╛рд╕реНрдкрдж - тАЛтАЛрд╡рд╛рдЪрд▓реЗрд▓реНрдпрд╛ 20 рд░реЗрдХреЙрд░реНрдбрдкреИрдХреА рдкреНрд░рддреНрдпреЗрдХрд╛рд╕рд╛рдареА 4 рдкреГрд╖реНрдареЗ рдбреЗрдЯрд╛ рд╡рдЬрд╛ рдХрд░рд╛рд╡рд╛ рд▓рд╛рдЧрд▓рд╛, рдкреНрд░рддрд┐ рд░реЗрдХреЙрд░реНрдб 32KB - рд╣реЗ рдзрд╛рдбрд╕реА рдирд╛рд╣реА рдХрд╛? рд╣реЛрдп рдЖрдгрд┐ рдЕрдиреБрдХреНрд░рдордгрд┐рдХрд╛ рдирд╛рд╡ tbl_fk_org_fk_cli_idx рд╡рд┐рдЪрд╛рд░рд╛рдХрдбреЗ рдиреЗрддреЛ.

рдЖрдореНрд╣реА рдирд┐рд░рд╛рдХрд░рдг рдХрд░рддреЛ:

CREATE INDEX ON tbl(fk_cli);

рдЖрдЬрд╛рд░реА SQL рдкреНрд░рд╢реНрдирд╛рдВрд╕рд╛рдареА рдкрд╛рдХрдХреГрддреА
[explain.tensor.ru рдкрд╣рд╛]

рдЕрдЪрд╛рдирдХ - рд╡рд╛рдЪрдгреНрдпрд╛рд╕рд╛рдареА 10 рдкрдЯ рдЬрд▓рдж рдЖрдгрд┐ 4 рдкрдЯ рдХрдореА!

рдирд┐рд░реНрджреЗрд╢рд╛рдВрдХрд╛рдВрдЪреНрдпрд╛ рдЕрдХрд╛рд░реНрдпрдХреНрд╖рдо рд╡рд╛рдкрд░рд╛рдЪреНрдпрд╛ рдЕрдзрд┐рдХ рдЙрджрд╛рд╣рд░рдгрд╛рдВрд╕рд╛рдареА, рд▓реЗрдЦ рдкрд╣рд╛ DBA: рдирд┐рд░реБрдкрдпреЛрдЧреА рдЕрдиреБрдХреНрд░рдордгрд┐рдХрд╛ рд╢реЛрдзрд╛.

#7: CTE ├Ч CTE

рдЬреЗрд╡реНрд╣рд╛ рдЙрджреНрднрд╡рддреЗ

рд╡рд┐рдирдВрддреА рдордзреНрдпреЗ "рдлреЕрдЯ" CTE рд╕реНрдХреЛрдЕрд░ рдХреЗрд▓реЗ рд╡реЗрдЧрд╡реЗрдЧрд│реНрдпрд╛ рдЯреЗрдмрд▓реНрд╕рд╡рд░реВрди, рдЖрдгрд┐ рдирдВрддрд░ рддреНрдпрд╛ рджрд░рдореНрдпрд╛рди рдХрд░рд╛рдпрдЪреЗ рдард░рд╡рд▓реЗ JOIN.

рдХреЗрд╕ v12 рдЪреНрдпрд╛ рдЦрд╛рд▓реА рдЕрд╕рд▓реЗрд▓реНрдпрд╛ рдЖрд╡реГрддреНрддреНрдпрд╛рдВрд╕рд╛рдареА рдХрд┐рдВрд╡рд╛ рд╡рд┐рдирдВрддреНрдпрд╛рдВрд╕рд╛рдареА рд╕рдВрдмрдВрдзрд┐рдд рдЖрд╣реЗ WITH MATERIALIZED.

рдХрд╕реЗ рдУрд│рдЦрд╛рд╡реЗ

-> CTE Scan
   && loops > 10
   && loops ├Ч (rows + RRbF) > 10000
      -- ╤Б╨╗╨╕╤И╨║╨╛╨╝ ╨▒╨╛╨╗╤М╤И╨╛╨╡ ╨┤╨╡╨║╨░╤А╤В╨╛╨▓╨╛ ╨┐╤А╨╛╨╕╨╖╨▓╨╡╨┤╨╡╨╜╨╕╨╡ CTE

рд╢рд┐рдлрд╛рд░рд╕реА

рд╡рд┐рдирдВрддреАрдЪреЗ рдХрд╛рд│рдЬреАрдкреВрд░реНрд╡рдХ рд╡рд┐рд╢реНрд▓реЗрд╖рдг рдХрд░рд╛ рдпреЗрдереЗ CTE рдЪреА рдЕрдЬрд┐рдмрд╛рдд рдЧрд░рдЬ рдЖрд╣реЗ? рдЬрд░ рд╣реЛрдп, рддрд░ hstore/json рдордзреНрдпреЗ "рд╢рдмреНрджрдХреЛрд╢" рд▓рд╛рдЧреВ рдХрд░рд╛ рдордзреНрдпреЗ рд╡рд░реНрдгрди рдХреЗрд▓реЗрд▓реНрдпрд╛ рдореЙрдбреЗрд▓рдиреБрд╕рд╛рд░ PostgreSQL рдЕрдБрдЯреАрдкреЕрдЯрд░реНрди: рд╢рдмреНрджрдХреЛрд╢ рд╣рд┐рдЯ рд╣реЗрд╡реА рдЬреЙрдЗрди.

#8: рдбрд┐рд╕реНрдХрд╡рд░ рд╕реНрд╡реЕрдк (рддрд╛рдкрдорд╛рди рд▓рд┐рд╣рд┐рд▓реЗ)

рдЬреЗрд╡реНрд╣рд╛ рдЙрджреНрднрд╡рддреЗ

рдореЛрдареНрдпрд╛ рд╕рдВрдЦреНрдпреЗрдиреЗ рд░реЗрдХреЙрд░реНрдбрдЪреА рдПрдХ-рд╡реЗрд│ рдкреНрд░рдХреНрд░рд┐рдпрд╛ (рд╡рд░реНрдЧреАрдХрд░рдг рдХрд┐рдВрд╡рд╛ рд╡рд┐рд╢рд┐рд╖реНрдЯреАрдХрд░рдг) рдпрд╛рд╕рд╛рдареА рд╡рд╛рдЯрдк рдХреЗрд▓реЗрд▓реНрдпрд╛ рдореЗрдорд░реАрдордзреНрдпреЗ рдмрд╕рдд рдирд╛рд╣реА.

рдХрд╕реЗ рдУрд│рдЦрд╛рд╡реЗ

-> *
   && temp written > 0

рд╢рд┐рдлрд╛рд░рд╕реА

рдЬрд░ рдСрдкрд░реЗрд╢рдирджреНрд╡рд╛рд░реЗ рд╡рд╛рдкрд░рд▓реНрдпрд╛ рдЬрд╛рдгрд╛рд░реНтАНрдпрд╛ рдореЗрдорд░реАрдЪреЗ рдкреНрд░рдорд╛рдг рдкреЕрд░рд╛рдореАрдЯрд░рдЪреНрдпрд╛ рд╕реЗрдЯ рдореВрд▓реНрдпрд╛рдкреЗрдХреНрд╖рд╛ рдЬрд╛рд╕реНрдд рдирд╕реЗрд▓ рдХрд╛рдо_рдореЗрдо, рддреЗ рджреБрд░реБрд╕реНрдд рдХреЗрд▓реЗ рдкрд╛рд╣рд┐рдЬреЗ. рддреБрдореНрд╣реА рдкреНрд░рддреНрдпреЗрдХрд╛рд╕рд╛рдареА рдХреЙрдиреНрдлрд┐рдЧрд░реЗрд╢рдирдордзреНрдпреЗ рддрд╛рдмрдбрддреЛрдм рдХрд░реВ рд╢рдХрддрд╛ рдХрд┐рдВрд╡рд╛ рддреБрдореНрд╣реА рддреЗ рдХрд░реВ рд╢рдХрддрд╛ SET [LOCAL] рд╡рд┐рд╢рд┐рд╖реНрдЯ рд╡рд┐рдирдВрддреА/рд╡реНрдпрд╡рд╣рд╛рд░рд╛рд╕рд╛рдареА.

рдЙрджрд╛рд╣рд░рдг:

SHOW work_mem;
-- "16MB"

SELECT
  random()
FROM
  generate_series(1, 1000000)
ORDER BY
  1;

рдЖрдЬрд╛рд░реА SQL рдкреНрд░рд╢реНрдирд╛рдВрд╕рд╛рдареА рдкрд╛рдХрдХреГрддреА
[explain.tensor.ru рдкрд╣рд╛]

рдЖрдореНрд╣реА рдирд┐рд░рд╛рдХрд░рдг рдХрд░рддреЛ:

SET work_mem = '128MB'; -- ╨┐╨╡╤А╨╡╨┤ ╨▓╤Л╨┐╨╛╨╗╨╜╨╡╨╜╨╕╨╡╨╝ ╨╖╨░╨┐╤А╨╛╤Б╨░

рдЖрдЬрд╛рд░реА SQL рдкреНрд░рд╢реНрдирд╛рдВрд╕рд╛рдареА рдкрд╛рдХрдХреГрддреА
[explain.tensor.ru рдкрд╣рд╛]

рд╕реНрдкрд╖реНрдЯ рдХрд╛рд░рдгрд╛рд╕реНрддрд╡, рдЬрд░ рдлрдХреНрдд рдореЗрдорд░реА рд╡рд╛рдкрд░рд▓реА рдЧреЗрд▓реА рдЖрдгрд┐ рдбрд┐рд╕реНрдХ рдирд╛рд╣реА, рддрд░ рдХреНрд╡реЗрд░реА рдЦреВрдк рд╡реЗрдЧрд╡рд╛рди рд╣реЛрдИрд▓. рддреНрдпрд╛рдЪ рд╡реЗрд│реА, рд▓реЛрдбрдЪрд╛ рднрд╛рдЧ HDD рд╡рд░реВрди рджреЗрдЦреАрд▓ рдХрд╛рдврд▓рд╛ рдЬрд╛рддреЛ.

рдкрд░рдВрддреБ рдЖрдкрд▓реНрдпрд╛рд▓рд╛ рд╣реЗ рд╕рдордЬреВрди рдШреЗрдгреЗ рдЖрд╡рд╢реНрдпрдХ рдЖрд╣реЗ рдХреА рднрд░рдкреВрд░ рдореЗрдорд░реА рд╡рд╛рдЯрдк рдХрд░рдгреЗ рдиреЗрд╣рдореАрдЪ рдХрд╛рд░реНрдп рдХрд░рдгрд╛рд░ рдирд╛рд╣реА - рддреЗ рдкреНрд░рддреНрдпреЗрдХрд╛рд╕рд╛рдареА рдкреБрд░реЗрд╕реЗ рдирд╛рд╣реА.

#9: рдЕрд╕рдВрдмрджреНрдз рдЖрдХрдбреЗрд╡рд╛рд░реА

рдЬреЗрд╡реНрд╣рд╛ рдЙрджреНрднрд╡рддреЗ

рдмреЗрд╕рдордзреНрдпреЗ рдПрдХрд╛рдЪ рд╡реЗрд│реА рдмрд░реЗрдЪ рдХрд╛рд╣реА рдУрддрд▓реЗ рдЧреЗрд▓реЗ, рдкрд░рдВрддреБ рддреЗ рджреВрд░ рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА рддреНрдпрд╛рдВрдЪреНрдпрд╛рдХрдбреЗ рд╡реЗрд│ рдирд╡реНрд╣рддрд╛ ANALYZE.

рдХрд╕реЗ рдУрд│рдЦрд╛рд╡реЗ

-> Seq Scan | Bitmap Heap Scan | Index [Only] Scan [Backward]
   && ratio >> 10

рд╢рд┐рдлрд╛рд░рд╕реА

рд╕рдорд╛рди рдЦрд░реНрдЪ рдХрд░рд╛ ANALYZE.

рдпрд╛ рдкрд░рд┐рд╕реНрдерд┐рддреАрдЪреЗ рдЕрдзрд┐рдХ рддрдкрд╢реАрд▓рд╡рд╛рд░ рд╡рд░реНрдгрди рдХреЗрд▓реЗ рдЖрд╣реЗ PostgreSQL рдЕрдБрдЯреАрдкреЕрдЯрд░реНрди: рдЖрдХрдбреЗрд╡рд╛рд░реА рд╣реЗ рдкреНрд░рддреНрдпреЗрдХ рдЧреЛрд╖реНрдЯреАрдЪреЗ рдкреНрд░рдореБрдЦ рдЕрд╕рддреЗ.

#10: "рдХрд╛рд╣реАрддрд░реА рдЪреВрдХ рдЭрд╛рд▓реА"

рдЬреЗрд╡реНрд╣рд╛ рдЙрджреНрднрд╡рддреЗ

рдкреНрд░рддрд┐рд╕реНрдкрд░реНрдзреА рд╡рд┐рдирдВрддреАрдЪреА рдкреНрд░рддреАрдХреНрд╖рд╛ рдХрд░рдд рдЕрд╕рд▓реЗрд▓рд╛ рд▓реЙрдХ рд╣реЛрддрд╛, рдХрд┐рдВрд╡рд╛ рдкреБрд░реЗрд╢реА CPU/рд╣рд╛рдпрдкрд░рд╡рд╛рдЗрдЬрд░ рд╣рд╛рд░реНрдбрд╡реЗрдЕрд░ рд╕рдВрд╕рд╛рдзрдиреЗ рдирд╡реНрд╣рддреА.

рдХрд╕реЗ рдУрд│рдЦрд╛рд╡реЗ

-> *
   && (shared hit / 8K) + (shared read / 1K) < time / 1000
      -- RAM hit = 64MB/s, HDD read = 8MB/s
   && time > 100ms -- ╤З╨╕╤В╨░╨╗╨╕ ╨╝╨░╨╗╨╛, ╨╜╨╛ ╤Б╨╗╨╕╤И╨║╨╛╨╝ ╨┤╨╛╨╗╨│╨╛

рд╢рд┐рдлрд╛рд░рд╕реА

рдмрд╛рд╣реНрдп рд╡рд╛рдкрд░рд╛ рджреЗрдЦрд░реЗрдЦ рдкреНрд░рдгрд╛рд▓реА рдмреНрд▓реЙрдХрд┐рдВрдЧ рдХрд┐рдВрд╡рд╛ рдЕрд╕рд╛рдорд╛рдиреНрдп рд╕рдВрд╕рд╛рдзрди рд╡рд╛рдкрд░рд╛рд╕рд╛рдареА рд╕рд░реНрд╡реНрд╣рд░. рдЖрдореНрд╣реА рд╢реЗрдХрдбреЛ рд╕рд░реНрд╡реНрд╣рд░рд╕рд╛рдареА рд╣реА рдкреНрд░рдХреНрд░рд┐рдпрд╛ рдЖрдпреЛрдЬрд┐рдд рдХрд░рдгреНрдпрд╛рдЪреНрдпрд╛ рдЖрдордЪреНрдпрд╛ рдЖрд╡реГрддреНрддреАрдмрджреНрджрд▓ рдЖрдзреАрдЪ рдмреЛрд▓рд▓реЛ рдЖрд╣реЛрдд. рдпреЗрдереЗ ╨╕ рдпреЗрдереЗ.

рдЖрдЬрд╛рд░реА SQL рдкреНрд░рд╢реНрдирд╛рдВрд╕рд╛рдареА рдкрд╛рдХрдХреГрддреА
рдЖрдЬрд╛рд░реА SQL рдкреНрд░рд╢реНрдирд╛рдВрд╕рд╛рдареА рдкрд╛рдХрдХреГрддреА

рд╕реНрддреНрд░реЛрдд: www.habr.com

рдПрдХ рдЯрд┐рдкреНрдкрдгреА рдЬреЛрдбрд╛