Чи застосовуються ДЕРЖАВНІ пропозиції в порядку, в якому вони записані?


36

Я намагаюся оптимізувати запит, який заглядає у велику таблицю (37 мільйонів рядків) і у мене є питання про те, який порядок виконуються операціями в запиті.

select 1 
from workdays day
where day.date_day >= '2014-10-01' 
    and day.date_day <= '2015-09-30' 
    and day.offer_id in (
        select offer.offer_day 
        from offer  
        inner join province on offer.id_province = province.id_province  
        inner join center cr on cr.id_cr = province.id_cr 
        where upper(offer.code_status) <> 'A' 
            and province.id_region in ('10' ,'15' ,'21' ,'26' ,'31' , ...,'557') 
            and province.id_cr in ('9' ,'14' ,'20' ,'25' ,'30' ,'35' ,'37')
    )

Чи WHEREвиконуються пропозиції про діапазон дат перед запитом? Чи є хорошим способом спочатку поставити найбільш обмежувальні пропозиції, щоб уникнути великих циклів для інших, щоб зробити це швидше?

Зараз запити займають стільки часу для виконання.

Відповіді:


68

Детальніше про відповідь @ alci:

PostgreSQL не хвилює, в якому порядку ви пишете речі

  • PostgreSQL взагалі не піклується про порядок записів у WHEREпункті, і вибирає індекси та порядок виконання, виходячи лише з оцінки вартості та вибірковості.

  • Порядок запису приєднання також ігнорується до налаштованого join_collapse_limit; якщо є більше приєднань, ніж це, вони виконають їх у порядку, про який вони написані.

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

  • Ніякої гарантії PostgreSQL фактично не виконуватиме частину запиту. Їх можна повністю оптимізувати. Це важливо, якщо ви викликаєте функції із побічними ефектами.

PostgreSQL перетворить ваш запит

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

  • Умови, що знаходяться поза підзапитом, можуть бути висунуті в підзапит, тому вони виконуються як частина підзапиту, не там, де ви їх написали у зовнішньому запиті

  • Терміни в підзапиті можна підтягувати до зовнішнього запиту, тому їх виконання відбувається як частина зовнішнього запиту, а не там, де ви їх написали в підзапиті

  • Підзапит може, і часто є, сплющеним в об'єднання на зовнішній стіл. Ж саме відноситься і до речей , як EXISTSі NOT EXISTSзапити.

  • Перегляди сплющуються у запиті, який використовує подання

  • Функції SQL часто вбудовуються в запит виклику

  • ... і є численні інші перетворення, здійснені на запити, такі як попереднє попереднє оцінювання виразів, де-кореляція деяких підзапитів та всілякі інші хитрості планувального / оптимізатора.

Взагалі PostgreSQL може масово трансформувати і переписувати ваш запит до точки, де кожен з цих запитів:

select my_table.*
from my_table
left join other_table on (my_table.id = other_table.my_table_id)
where other_table.id is null;

select *
from my_table
where not exists (
  select 1
  from other_table
  where other_table.my_table_id = my_table.id
);

select *
from my_table
where my_table.id not in (
  select my_table_id
  from other_table
  where my_table_id is not null
);

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

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

Обмеження

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

  • Планувальник покладається на статистику, що ANALYZEведеться (як правило, через автовакуум). Якщо вони застаріли, вибір плану може бути поганим.

  • Статистика є лише вибіркою, тому вони можуть вводити в оману через ефекти вибірки, особливо якщо взяти занадто малу кількість вибірки. Неправильний вибір плану може призвести.

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

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

  • Будь-який підзапит з а LIMITчи OFFSETне може бути сплющений або підлягає підтягуванню / натисканню. Це не означає, що він буде виконуватися перед усіма частинами зовнішнього запиту, хоча і навіть, що він буде виконаний взагалі .

  • Терміни CTE (пункти WITHзапиту) завжди виконуються в повному обсязі, якщо вони виконуються взагалі. Вони не можуть бути вирівняними, а терміни не можуть бути висунуті вгору або перетягнуті через бар'єрний термін CTE. Умови CTE завжди виконуються перед остаточним запитом. Це не стандартне поведінка SQL , але це документально підтверджено як PostgreSQL робить справи.

  • PostgreSQL має обмежену здатність оптимізувати запити на іноземних таблицях, security_barrierпредставленнях та деяких інших спеціальних відносинах

  • PostgreSQL не буде вбудовувати функцію, записану в чому-небудь, крім звичайного SQL, і не здійснювати підтягування / віджимання між ним та зовнішнім запитом.

  • Планувальник / оптимізатор дійсно немічний щодо вибору індексів вираження та щодо тривіальних відмінностей між типом даних та виразом.

Тонів більше.

Ваш запит

У випадку вашого запиту:

select 1 
from workdays day
where day.date_day >= '2014-10-01' 
    and day.date_day <= '2015-09-30' 
    and day.offer_id in (
        select offer.offer_day 
        from offer  
        inner join province on offer.id_province = province.id_province  
        inner join center cr on cr.id_cr = province.id_cr 
        where upper(offer.code_status) <> 'A' 
            and province.id_region in ('10' ,'15' ,'21' ,'26' ,'31' , ...,'557') 
            and province.id_cr in ('9' ,'14' ,'20' ,'25' ,'30' ,'35' ,'37')
    )

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

Мабуть, вийде щось на зразок (неперевірене, очевидно):

select 1 
from workdays day
inner join offer on day.offer_id = offer.offer_day
inner join province on offer.id_province = province.id_province  
inner join center cr on cr.id_cr = province.id_cr 
where upper(offer.code_status) <> 'A' 
   and province.id_region in ('10' ,'15' ,'21' ,'26' ,'31' , ...,'557') 
   and province.id_cr in ('9' ,'14' ,'20' ,'25' ,'30' ,'35' ,'37')
   and day.date_day >= '2014-10-01' 
   and day.date_day <= '2015-09-30';

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

Як побачити, що зробив оптимізатор

Ви не можете бачити SQL, за допомогою якого PostgreSQL оптимізує ваш запит, оскільки він перетворює SQL у внутрішнє представлення дерева запитів, а потім змінює його. Ви можете скинути план запитів і порівняти його з іншими запитами.

Немає способу повернути цей план запитів чи внутрішнє дерево плану назад до SQL.

http://explain.depesz.com/ має гідний помічник плану запитів. Якщо ви абсолютно новачок у планах запитів тощо (у такому випадку я вражений, ви зробили це далеко через цю посаду), тоді PgAdmin має графічний переглядач плану запитів, який надає набагато менше інформації, але простіший.

Пов'язане читання:

Можливості віджимання / підтягування та вирівнювання продовжують удосконалюватися в кожному випуску . PostgreSQL, як правило, стосується рішень про підтягування / натискання / вирівнювання, але не завжди, тому періодично доводиться (ab) використовувати CTE або OFFSET 0хак. Якщо ви виявили такий випадок, повідомте про помилку в планувальнику запитів.


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


Вау ... цілком повна відповідь :-) Один із випадків, коли у мене були повільні плани з Postgresql (як і з іншими відомими ДБД, такими як Oracle), - це кореляція між стовпцями або декілька корельованих приєднань. Часто це буде робити вкладені петлі, думаючи, що в цій точці плану є лише кілька рядків, адже насправді їх багато тисяч. Один із способів оптимізації таких запитів - це "встановити enable_nestloop = off;" протягом тривалості запиту.
alci

Я зіткнувся з ситуацією, коли v9.5.5 намагався застосувати TO_DATE до перевірки на те, чи можна його застосувати, у простому запиті з пунктом 7. Замовлення має значення.
користувач1133275

@ user1133275 У такому випадку він працював у вас лише випадково, оскільки розрахунки витрат на розрахунок були однаковими. PostgreSQL все ще може вирішити запуститись to_dateдо перевірки в більш пізній версії або через зміни статистики оптимізатора. Щоб надійно запустити перевірку перед функцією, яка повинна запускатися лише після перевірки, використовуйте CASEоператор.
Крейг Рінгер

одна з найбільших відповідей, яку я коли- небудь бачив на SO! Великі пальці вгору, чоловіче!
62mkv

Я відчував ситуації, коли для додавання простих order byзапитів виконання запиту робилося набагато швидше, ніж якби його не було order by. Це одна з причин, коли я пишу свої запити з приєднаннями таким чином, як якщо б я хотів, щоб вони виконувались - приємно мати великий оптимізатор, але я думаю, що не розумно повністю довіряти свою долю до її результату і писати запити, не замислюючись, як це may beвиконано ... Чудова відповідь !!
Greg0ry

17

SQL - це декларативна мова: ви говорите, що хочете, а не як це зробити. RDBMS вибере спосіб виконання запиту, який називається планом виконання.

Колись (5-10 років тому) спосіб написання запиту мав прямий вплив на план виконання, але в наш час більшість двигунів баз даних SQL використовують оптимізатор на основі витрат для планування. Тобто він оцінить різні стратегії виконання запиту, виходячи з його статистики щодо об’єктів бази даних, і вибере найкращу.

Здебільшого це справді найкращий варіант, але іноді двигун БД буде робити неправильний вибір, в результаті чого запити будуть дуже повільними.


Слід зазначити, що для деяких запитів RDBMS все ще є важливим, але для більш просунутих все, що ви говорите, є правдивим як на практиці, так і в теорії. Коли планувальник запитів вибирає поганий вибір порядку виконання, зазвичай є підказки для запитів, щоб підштовхнути його в більш ефективному напрямку (наприклад, WITH(INDEX(<index>))в MSSQL, щоб змусити вибір індексу для конкретного об'єднання).
Девід Спіллет

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