Порядок выполнения операторов UNION, EXCEPT, INTERSECT

В связи с упражнением 6 (SELECT) рейтингового этапа возник вопрос относительно старшинства операций UNION, EXCEPT и INTERSECT. Логический порядок выполнения этих операций, который приводится в книге Мартина Грабера [4] “Справочное руководство по SQL”, выглядит так:

  • UNION, EXCEPT
  • INTERSECT

В предположении, что логический порядок выполнения операций соответствует их старшинству, получается, что старшинство операций UNION и EXCEPT идентично и, следовательно, они должны выполняться в том порядке, в котором записаны, если этот порядок не изменяется скобками. При этом обе операции выполняются раньше, чем INTERSECT, т.е. они старше.

Рассмотрим три простых запроса, которые будем комбинировать различными способами, чтобы убедиться в этом:

--Модели и типы продукции производителя B       
SELECT model, type FROM Product WHERE maker='B';
🚫
[[ error ]]
[[ column ]]
NULL [[ value ]]
modeltype
1121PC
1750Laptop
--Модели ноутбуков  
SELECT model, type FROM Product WHERE type='Laptop';
🚫
[[ error ]]
[[ column ]]
NULL [[ value ]]
modeltype
1298Laptop
1321Laptop
1750Laptop
1752Laptop
--Модели ПК  
SELECT model, type FROM Product WHERE type='PC';
🚫
[[ error ]]
[[ column ]]
NULL [[ value ]]
modeltype
1121PC
1232PC
1233PC
1260PC
2111PC
2112PC

Давайте сначала проверим первое утверждение. Если операция EXCEPT старше операции UNION, то запросы

SELECT model, type FROM Product WHERE maker='B'  
UNION  
SELECT model, type FROM Product WHERE type='Laptop'  
EXCEPT  
SELECT model, type FROM Product WHERE type='PC';
🚫
[[ error ]]
[[ column ]]
NULL [[ value ]]
и
(SELECT model, type FROM Product WHERE maker='B'  
UNION  
SELECT model, type FROM Product WHERE type='Laptop')  
EXCEPT  
SELECT model, type FROM Product WHERE type='PC';
🚫
[[ error ]]
[[ column ]]
NULL [[ value ]]
должны нам дать разные результаты. Однако это не так, и мы получаем один и тот же результирующий набор:

modeltype
1298Laptop
1321Laptop
1750Laptop
1752Laptop

Аналогично, если операция UNION старше операции EXCEPT, то запросы

SELECT model, type FROM Product WHERE type='Laptop'
EXCEPT  
SELECT model, type FROM Product WHERE type='PC'  
UNION  
SELECT model, type FROM Product WHERE maker='B';
🚫
[[ error ]]
[[ column ]]
NULL [[ value ]]
и
(SELECT model, type FROM Product WHERE type='Laptop'  
EXCEPT  
SELECT model, type FROM Product WHERE type='PC')  
UNION  
SELECT model, type FROM Product WHERE maker='B';
🚫
[[ error ]]
[[ column ]]
NULL [[ value ]]
должны нам дать разные результаты. И тут мы получаем одинаковый результат:

modeltype
1121PC
1298Laptop
1321Laptop
1750Laptop
1752Laptop

Итак, операции UNION и EXCEPT эквивалентны по старшинству.

Проверим теперь старшинство операции INTERSECT по отношению к другим операторам (в тестах можно взять любую из них, т.к. они имеют один и тот же порядок).

Если INTERSECT “младше” или эквивалентен UNION, то запросы

SELECT model, type FROM Product WHERE maker='B'  
UNION  
SELECT model, type FROM Product WHERE type='Laptop'  
INTERSECT  
SELECT model, type FROM Product WHERE type='PC';
🚫
[[ error ]]
[[ column ]]
NULL [[ value ]]
и
(SELECT model, type FROM Product WHERE maker='B'  
UNION  
SELECT model, type FROM Product WHERE type='Laptop')  
INTERSECT  
SELECT model, type FROM Product WHERE type='PC';
🚫
[[ error ]]
[[ column ]]
NULL [[ value ]]
должны дать одинаковые результаты. Однако мы получаем разные результирующие наборы. Первый запрос дает

modeltype
1121PC
1750Laptop

в то время как второй

modeltype
1121PC

Вывод. Логический порядок, приведенный в начале статьи не соответствует старшинству операций, и, на мой взгляд, его следует поменять на обратный:

  • INTERSECT
  • UNION, EXCEPT