Database Programming & Design

       

Раздел HAVING


Вот запрос, для формулировки которого большинство людей использовало бы раздел HAVING:

Q4: Для каждой детали, поставляемой более чем одним поставщиком, выдать номер детали.

Возможной формулировкой с использованием GHB могла бы быть следующая:

SELECT SP.P# FROM SP GROUP BY SP.P# HAVING COUNT(*) > 1 ;

Результатом такого запроса является таблица

P# ----- P1 P2 P4 P5

Вот формулировка без использования разделов GROUP BY и HAVING:

SELECT DISTINCT SP.P# FROM SP WHERE (SELECT COUNT(*) FROM SP AS SPX WHERE SPX.P# = SP.P#) > 1 ;

Снова эта формулировка длиннее варианта с GBH, но логически они эквивалентны. И опять легко обобщить пример. Если использовать обозначения из предыдущего раздела, то выражение

SELECT R.A FROM R GROUP BY R.A HAVING agg(R.B) comp scalar ;

(где comp является некоторой операцией скалярного сравнения, а scalar - некоторое скалярное выражение) может быть логически преобразовано к эквивалентному выражению

SELECT DISTINCT R.A FROM R WHERE (SELECT agg(R.B) FROM R AS RX WHERE RX.A = R.A) comp scalar ;

Будем называть такого типа преобразования преобразованиями Типа 2. Посмотрим, что произойдет, если исходная формулировка будет включать раздел WHERE.

Q5: Для каждой детали, поставляемой более чем одним поставщиком (кроме поставщика S1), выдать номер детали.

Формулировка с GBH:

SELECT SP.P# FROM SP WHERE SP.P# <> 'S1' GROUP BY SP.P# HAVING COUNT(*) > 1 ;



Формулировка без GBH лишь немного хитрее:

SELECT DISTINCT SP.P# FROM SP WHERE (SELECT COUNT(*) FROM SP AS SPX WHERE SPX.P# = SP.P# AND SPX.S# <> 'S1') > 1 ;

Пример показывает, что преобразование Типа 2 может быть легко обобщено для случая наличия раздела WHERE. Вот несколько более сложный пример:

Q6: Для каждой детали, поставляемой более чем одним поставщиком, выдать номер детали и общее число поставок этой детали.

Формулировка запроса с использованием GBH:

SELECT SP.P#, SUM(SP.QTY) AS TQY FROM SP GROUP BY SP.P# HAVING COUNT(*) > 1 ;

Применяя правила преобразований Типа 1 и 2, получим следующее:


SELECT DISTINCT SP.P#, (SELECT SUM(SPX.QTY) FROM SP AS SPX WHERE SPX.P# = SP.P#) AS TQY FROM SP WHERE (SELECT COUNT(*) FROM SP AS SPX WHERE SPX.P# = SP.P#) > 1 ;

И еще один пример:

Q7: Для каждой детали, поставляемой более чем одним поставщиком, выдать общее число поставок этой детали, но без номера детали.

Вот формулировка с применением GBH:

SELECT SUM(SP.QTY) AS TQY FROM SP GROUP BY SP.P# HAVING COUNT(*) > 1 ;

Преобразованный вариант:

SELECT (SELECT SUM(SPX.QTY) FROM SP AS SPX WHERE SPX.P# = SP.P#) AS TQY FROM SP WHERE (SELECT COUNT(*) FROM SP AS SPX WHERE SPX.P# = SP.P#) > 1 ;

В результате выполнения этих запросов были бы получены следующие результаты:

С GROUP BY Без GROUP BY и HAVING и HAVING

TQY TQY ----- ----- 600 600 1000 1000 500 500 500

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


Содержание раздела