Отдавайте предпочтение стандартным конструкциям SQL
Обоснование
Помимо очевидных преимуществ использования стандартных конструкций, замечу также, что нестандартные конструкции действительно зачастую непредсказуемы! В электронной документации к Microsoft SQL Server имеется следующий комментарий к синтаксису конструкции UPDATE.. FROM..:
“Результаты выполнения оператора UPDATE не определены, если в нем имеется конструкция FROM, способ задания которой не гарантирует, что для каждого экземпляра обновляемого столбца доступно всего одно значение (иными словами, если выражение UPDATE недетерминиро-вано). Например, в приведенном ниже примере оператора UPDATE, условию в конструкции FROM удовлетворяют обе строки таблицы S. Не определено, которая из них будет использована для обновления строки таблицы Т.”
Эта неопределенность пришла на смену прежнему поведению, принятому в семействах Sybase и Ingres, когда оператор UPDATE.. FROM., производил многократные обновления — по одному для каждой присоединяемой строки второй таблицы.
В старых версиях Sybase/SQL Server, если во внедренном запросе строка базовой таблицы фигурировала более одного раза, эта строка и обрабатывалась несколько раз. Реляционные принципы при этом нарушаются полностью, но в конкретной физической реализации добиться такого поведения проще простого. Вот наглядный пример:
CREATE TABLE T1 (х INTEGER NOT NULL);
INSERT INTO T1 VALUES (1);
INSERT INTO T1 VALUES (2);
INSERT INTO T1 VALUES (3);
INSERT INTO T1 VALUES (4);
CREATE TABLE T2 (x INTEGER NOT NULL);
INSERT INTO T2 VALUES (1);
INSERT INTO T2 VALUES (1);
INSERT INTO T2 VALUES (1);
INSERT INTO T2 VALUES (1);
Теперь попробуем обновить Т1, удвоив значения в тех строках, значения в которых совпадают с одним из значений таблицы Т2:
UPDATE T1 SET Т1.х = 2 * Т1.х FROM T2 WHERE T1.x = Т2.х; SELECT * FROM T1; Реальное Ожидаемое X X ======== ======== 16 2 2 2 3 3 4 4
Конструкция FROM возвращает перекрестное произведение (CROSS JOIN), поэтому одна и та же операция над одной и той же строкой повторяется четырежды (1 => 2 => 4 => 8 => 16). Пример очень простой, но суть вы уловили. Есть такие тонкости в произведениях таблиц, которые вместе с мутировавшим синтаксисом T-SQL, приводят к зацикливанию изменений, к таблицам, результат обработки которых зависит от порядка строк, и т.д.
В более поздних версиях SQL Server и Sybase эта проблема решена по-разному. В Sybase, например, в неявном запросе скрыто выполняется оператор “SELECT DISTINCT”. А вот Standard SQL отличается согласованностью и ясностью в отношении псевдонимов, представлений и производных таблиц.
Согласно модели Standard SQL, если бы в операторе UPDATE можно было использовать псевдоним, то это означало бы создание копии содержимого базовой таблицы с этим псевдонимом, которая затем обновлялась бы и удалялась по окончании работы оператора — с базовой таблицей фактически ничего бы не происходило. Если бы в операторе UPDATE можно было использовать конструкцию FROM, это означало бы создание результирующего набора, его обновление и удаление по окончании работы оператора — снова без внесения изменений в базовую таблицу.
Почему вообще существует такой специфический, двусмысленный и противоречащий стандартной модели синтаксис? В оригинальном продукте Sybase физическая модель была такова, что подобное “расширение” реа-лизовывалось относительно легко. Ни стандартов, ни вообще глубокого понимания реляционной модели тогда не было. Программисты привыкли к такому синтаксису, и исправить это было почти невозможно.
В середине 1970-х я жил в Индианаполисе. Моим соседом был выпускник частного колледжа “Дженерал Моторс” (General Motors), работавший на эту компанию. На своей первой должности он занимался отчетами о несчастных случаях на производстве. Как-то вечером за пивом он принялся рассказывать мне реальные истории, случившиеся на разных заводах “Дженерал Моторс”. Проведя на своей должности год, он пришел к выводу, что все несчастные случаи на производстве представляют собой замысловатые попытки самоубийства. Чтобы хоть немного ускорить работу, рабочие изобретали хитроумные приспособления, позволяющие обойти защиту оборудования. Допустим, можно сделать зажим, который будет удерживать один из двух защитных переключателей небольшого штампующего станка. Теперь вы можете одной рукой нажимать вторую кнопку, а другой рукой — быстро вставлять в станок деталь. Конечно, работает эта схема лишь до тех пор, пока в станок не попадает сама эта другая рука. Пренебрежение безопасностью и корректностью ради скорости рано или поздно выходит боком.
Исключения
Ваш SQL продукт может не поддерживать стандартный синтаксис для некоторых операций. Например, в Oracle нет поддержки выражения CASE, зато имеется функция DECODE(), выполняющая примерно те же операции.