Чи є SQL декларативним?


22

Я запитую, оскільки стільки питань, які я бачу в SQL, становить: "Це повільно. Як я його пришвидшити"? Або підручники із зазначенням "Роби це так, а не так, як це швидше".

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

Чи не повинен движок SQL не хвилюватись, чи використовувався ви in, existsчи joinвін справді декларативний, чи не повинен він просто дати правильну відповідь у розумний час, якщо це можливо, будь-яким із трьох методів? Цей останній приклад спонукає цей останній пост, який є типом, зазначеним у моєму вступному пункті.

Покажчики

Напевно, найпростіший приклад, який я міг би використати, стосується створення індексу для таблиці. Тут гумка на w3schools.com навіть намагається пояснити це як щось невидиме для користувача, що є там з міркувань продуктивності. Їх опис, здається, ставить індекси SQL в недекларативні табори, і вони регулярно додаються вручну з чисто причин ефективності.

Це так, що їх десь ідеальний БД SQL, який набагато декларативніший за всі інші, але тому що він хороший про це не чує?


@FrustratedWithFormsDesigner: Я точно знаю, що це означає. select whatever from sometable where FKValue in (select FKValue from sometable_2 where other_value = :param). Це має бути тривіально, щоб побачити, як відновити це з a existsчи a join.
Мейсон Уілер

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

Продуктивність - це деталь реалізації. Продуктивність майже будь-якої реалізації IN може бути порівнянною або кращою, ніж EXISTS та JOIN, якщо розробники процесорних запитів вважають, що це пріоритет.
JustinC

1
@JustinC, здається, це більше, ніж деталь, враховуючи перевагу питань, орієнтованих на ефективність, SQL питань та порад щодо нібито декларативної мови?
Paddy3118

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

Відповіді:


21

Теоретично декларативні SQL. Але ви знаєте, що вони кажуть про різницю між теорією та практикою ...

По суті, поняття "декларативне програмування" ніколи не було по-справжньому ефективною, і, ймовірно, ніколи не буде, поки ми не будемо мати компілятор, заснований на AI, який здатний переглядати код і відповісти на запитання "в чому полягає ціль цього коду?" розумно, так само, як і людина, яка це написала. В основі кожної декларативної мови лежить ціла купа імперативного коду, який несанкціоновано намагається вирішити цю проблему без допомоги ШІ.

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


3
Ніколи по-справжньому ефективним? SQL, LINQ, Knockout.js, Prolog, мова ELM. Ви можете перевірити ще раз. Наразі я використовую переважно декларативні технології.
Брайан

5
@brian: І всі вони вироджуються досить швидко, коли трапляються на крайньому випадку, про який ніхто не думав. Я думаю, я мав би сказати, що "ніколи не є дійсно ефективним у загальному випадку ".
Мейсон Уілер

Коли ваш відповідь встановлений для погіршення, бачачи, як він зберігається в базі даних SQL Server? :) Я рідко потрапляю в крайній випадок у будь-якому з них, який не можна було вирішити в рамках. Я бачу, звідки ви приїжджаєте, але крайові випадки насправді не заподіюють мені великого болю за те, наскільки корисно і просто міркувати про 99% декларативного коду. Це як би сказати Clojure або F # погано, оскільки вам довелося використовувати мутаційний тип, щоб вирішити свою проблему.
Брайан

11
@brian: I rarely hit an edge case in any of them that couldn't be solved within the framework.Так, у цьому і полягає вся суть: винаймати спосіб їх вирішення в рамках, тому що рамки недостатньо розумні, щоб вирішити це для вас так, як ви спочатку заявили про це.
Мейсон Уілер

Що щодо вибору ... для оновлення? Здається, імперативна команда.
Jesvin Jose Jose

6

Я думав про це кілька днів тому після оптимізації 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

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