Я думав про це кілька днів тому після оптимізації SQL. Я думаю, ми можемо погодитися, що SQL є "декларативною мовою" у визначенні Вікіпедії:
Парадигма програмування, яка виражає логіку обчислення, не описуючи її контрольний потік
Якщо ви думаєте, скільки речей робиться за шторами (дивлячись на статистику, вирішуючи, чи корисний індекс, збирається вкладене, об'єднане чи хеш-з'єднання тощо), ми повинні визнати, що ми даємо просто високий рівень логіки, і база даних подбала про всю логіку потоків управління низьким рівнем.
Також у цьому сценарії іноді оптимізатору бази даних потрібні певні «підказки» від користувача, щоб дати найкращі результати.
Ще одне поширене визначення поняття "декларативна" мова (я не можу знайти авторське джерело):
Парадигма програмування, яка виражає бажаний результат обчислення, не описуючи кроки для його досягнення (також скорочено "опишіть що, а не як")
Якщо ми приймаємо це визначення, ми стикаємося з проблемами, описаними ОП.
Перше питання полягає в тому, що SQL дає нам кілька еквівалентних способів визначення "одного і того ж результату". Можливо, це є необхідним злом: чим більше виразної сили ми надаємо мові, тим більше, ймовірно, є різних способів виразити одне і те ж.
Як приклад, мене один раз попросили оптимізувати цей запит:
SELECT Distinct CT.cust_type, ct.cust_type_description
from customer c
INNER JOIN
Customer_type CT on c.cust_type=ct.cust_type;
Оскільки типів було набагато менше, ніж клієнтів, і cust_type
на таблиці клієнтів був індекс , я домігся значного покращення, переписавши його як:
SELECT CT.cust_type, ct.cust_type_description
from Customer_type CT
Where exists ( select 1 from customer c
Where c.cust_type=ct.cust_type);
У цьому конкретному випадку, коли я запитав розробника, чого він хоче досягти, він сказав мені: "Я хотів усіх типів клієнтів, для яких у мене був хоча б один клієнт", це, до речі, саме так можна описати запит оптимізатора.
Отже, якщо я міг знайти еквівалентний та більш ефективний запит, чому оптимізатор не може зробити те саме?
Я найкраще здогадуюсь, що це з двох основних причин:
SQL виражає логіку:
оскільки SQL виражає логіку високого рівня, чи дійсно ми хочемо, щоб оптимізатор "перехитрив" нас та нашу логіку? Я б із захопленням кричав "так", якби не всі часи, мені довелося змусити оптимізатора вибрати найефективніший шлях виконання. Я думаю, що ідея може полягати в тому, щоб оптимізатор зробив все можливе (також переглядаючи нашу логіку), але дати нам "механізм підказки", щоб прийти на допомогу, коли щось зійде з розуму (це було б як би мати колесо + гальмо в автономний автомобіль).
Більше вибору = більше часу
Навіть найкращий оптимізатор RDBMS не перевіряє ВСІ можливі шляхи виконання, оскільки вони повинні бути дуже швидкими: як добре було б оптимізувати запит від 100 мс до 10 мс, якщо мені потрібно витрачати щоразу 100 м на вибір найкращого шляху? І це з оптимізатором, який поважає нашу "логіку високого рівня". Якщо він також повинен перевірити всі еквівалентні запити SQL, час оптимізатора може зрости в кілька разів.
Ще один хороший приклад перезапису запитів, на який насправді не вдається RDBMS (з цієї цікавої публікації в блозі )
SELECT t1.id, t1.value, SUM(t2.value)
FROM mytable t1
JOIN mytable t2
ON t2.id <= t1.id
GROUP BY t1.id, t1.value;
ніж можна записати так (потрібні аналітичні функції)
SELECT id, value, SUM(t1.value) OVER (ORDER BY id)
FROM mytable
select whatever from sometable where FKValue in (select FKValue from sometable_2 where other_value = :param)
. Це має бути тривіально, щоб побачити, як відновити це з aexists
чи ajoin
.