Является ли SQL декларативным?

22

Я спрашиваю, потому что многие вопросы, которые я вижу в SQL, сводятся к следующему: «Это медленно. Как мне это ускорить»? Или учебники, в которых говорится: «Делай так, а не так, потому что это быстрее».

Мне кажется, что большая часть SQL знает, как будет выполняться выражение, и из этого знания выбирает стили выражения, которые работают лучше. Это не согласуется с одним аспектом декларативного программирования - с тем, чтобы система сама решала, как наилучшим образом выполнить вычисления, просто указав, что должно произвести вычисление.

Разве движок SQL не должен заботиться о том, что вы используете in, existsили joinон действительно декларативный, не должен ли он дать вам правильный ответ в разумные сроки, если это возможно, любым из трех методов? Этот последний пример вызван недавним сообщением того типа, который упоминается в моем первом абзаце.

Индексы

Я предполагаю, что самый простой пример, который я мог бы использовать, связан с созданием индекса для таблицы. Гимф здесь на w3schools.com даже пытается объяснить это как что-то невидимое пользователем, которое существует по соображениям производительности. Похоже, что их описание помещает индексы SQL в недекларативный лагерь, и они обычно добавляются вручную из чисто соображений производительности.

Это тот случай, когда они являются идеальной базой данных SQL, которая гораздо более декларативна, чем все остальные, но потому, что это хорошо, что никто не слышит об этом?

Paddy3118
источник
@FrustratedWithFormsDesigner: я точно знаю, что это значит. select whatever from sometable where FKValue in (select FKValue from sometable_2 where other_value = :param), Это должно быть тривиально, чтобы увидеть, как повторить это с помощью existsили join.
Мейсон Уилер
Используя аналогичные рассуждения, я предполагаю, что регулярные выражения являются более декларативным методом выражения, так как я редко вижу вопросы о производительности, на которые отвечают «вы должны написать это таким образом, чтобы повысить производительность». Я ломаю себе голову и могу наполовину вспомнить какой-то вопрос, связанный с отрицательными утверждениями о заглядывании назад или впереди, в медленном регулярном выражении, где ответом было переписать регулярное выражение другим способом, чтобы сделать то же самое за меньшее время.
Paddy3118
Производительность - это деталь реализации. Производительность почти любой реализации IN могла бы быть сопоставимой или лучше, чем EXISTS и JOIN, если бы разработчики процессора запросов считали это приоритетом.
JustinC
1
@JustinC, кажется, это больше, чем детали, учитывая преобладание вопросов и советов по SQL, ориентированных на производительность, для предположительно декларативного языка?
Paddy3118
Четкого определения декларативного языка программирования не существует, и поэтому говорить о нем бессмысленно. Некоторые языки более высокого уровня, чем другие, вот и все.
садовник

Ответы:

21

SQL теоретически декларативен. Но вы знаете, что они говорят о разнице между теорией и практикой ...

По своей сути концепция «декларативного программирования» никогда не была по-настоящему эффективной, и, вероятно, никогда не будет, пока у нас не будет AI-компилятора, который способен смотреть на код и отвечать на вопрос «каково намерение этого кода?» разумно, так же, как и тот, кто это написал. В основе каждого декларативного языка лежит целый набор императивного кода, который отчаянно пытается решить эту проблему без помощи ИИ.

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

Мейсон Уилер
источник
3
Никогда по-настоящему эффективно? SQL, LINQ, Knockout.js, Пролог, язык 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.Да, в этом весь смысл: нужно искать способ их решения в рамках структуры, потому что структура недостаточно умна, чтобы решить ее для вас так, как вы первоначально объявили ее.
Мейсон Уилер
Как насчет выбора ... для обновления? Кажется, повелительная команда.
Джесвин Хосе
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 выражает высокоуровневую логику, хотим ли мы, чтобы оптимизатор «перехитрил» нас и нашу логику? Я бы с энтузиазмом кричал «да», если бы не все времена, когда мне приходилось заставлять оптимизатор выбирать наиболее эффективный путь выполнения. Я думаю, что идея могла бы состоять в том, чтобы позволить оптимизатору делать все возможное (также пересматривая нашу логику), но дать нам «механизм намека», чтобы прийти на помощь, когда что-то сходит с ума (это было бы похоже на колесо + тормоза в автономный автомобиль).

Больше вариантов = больше времени

Даже самый лучший оптимизатор СУБД не тестирует ВСЕ возможные пути выполнения, поскольку они должны быть очень быстрыми: насколько хорошо было бы оптимизировать запрос от 100 мс до 10 мс, если бы мне приходилось каждый раз тратить 100 мс на выбор наилучшего пути? И это с оптимизатором, уважающим нашу «логику высокого уровня». Если он также должен протестировать все эквивалентные запросы SQL, время оптимизатора может возрасти в несколько раз.

Еще один хороший пример переписывания запросов, который фактически может выполнить ни одна СУБД (из этого интересного сообщения в блоге )

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
Insac
источник
1
Пример переписывания соединения с существующим интересен. Одно из практических правил, которое я стараюсь впечатлить разработчиками SQL, заключается в том, что использование DISTINCT является запахом кода - либо запрос, либо модель данных, скорее всего, ошибочны, и следует искать другой подход.
Дэвид Олдридж