Материал: Using_MySql,_MS_SQL_Server_and_Oracle

Внимание! Если размещение файла нарушает Ваши авторские права, то обязательно сообщите нам

Пример 8: поиск множества минимальных и максимальных значений

В строке 5 запроса 2.1.8.b для Oracle мы используем специальную аналитическую функцию ROW_NUMBER, позволяющую присвоить строке номер на основе вы-

ражения. В нашем случае выражение имеет упрощённый вариант: не указан принцип разбиения строк на группы и перезапуска нумерации — мы лишь просим СУБД пронумеровать строки в порядке их следования в упорядоченной выборке.

Oracle не позволяет в одном и том же запросе как пронумеровать строки, так и наложить условие на выборку на основе этой нумерации, потому мы вынуждены использовать подзапрос (строки 3-6). Альтернативой подзапросу может быть т.н. CTE (Common Table Expression, общее табличное выражение), но

(

о CTE мы поговорим позже{73}.

Частым вопросом относительно решения для Oracle является примени-

мость здесь не функции ROW_NUMBER, а «псевдополя» ROWNUM. Его при-

 

менять нельзя, т.к. нумерация с его использованием происходит до сра-

 

батывания ORDER BY.

 

Исследование 2.1.8.EXP.A. Продемонстрируем результат неверного ис-

 

пользования «псевдополя» ROWNUM вместо функции ROW_NUMBER.

Oracle

Исследование 2.1.8.EXP.A, пример неверного запроса

1SELECT "b name",

2"b quantity"

3

FROM

(SELECT "b name",

4

 

 

"b_quantity",

5

 

 

ROWNUM AS "rn"

6

 

FROM

"books"

7

 

ORDER BY "b_quantity" DESC)

8

WHERE

"rn" = 1

Результатом выполнения этого запроса является:

b_name

b_quantity

Евгений Онегин

2

Такой результат получается потому, что подзапрос (строки 3-7) возвращает следующие данные:

b_name

 

b_quantity

rn

Курс теоретической физики

12

 

6

Искусство программирования

7

 

7

Основание и империя

5

 

3

Сказка о рыбаке и рыбке

3

 

2

Язык программирования C++

3

 

5

Евгений Онегин

2

 

1

Психология программирования

1

 

4

Легко заметить, что нумерация строк произошла до упорядочивания, и первый номер был присвоен книге «Евгений Онегин». При этом вариант с функцией ROW_NUMBER работает корректно.

Работа с MySQL, MS SQL Server и Oracle в примерах © Богдан Марчук Стр: 50/545

Пример 8: поиск множества минимальных и максимальных значений

Решение 2.1.8.С43.

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

MySQL і Решение 2.1.8.С

1

-- Вариант 1: использование MAX

2

SELECT 'b name',

 

3

 

'b quantity'

 

4

FROM

'books'

 

5

WHERE

'b quantity' = (SELECT MAX('b quantity')

6

 

FROM

'books')

MS SQL I Решение 2.1.8.c |

1-- Вариант 1: использование MAX

2SELECT [b name],

3[b quantity]

4

FROM

[books]

 

5

WHERE

[b quantity] = (SELECT MAX([b quantity])

6

 

FROM

[books])

1-- Вариант 2: использование RANK

2SELECT [b name],

3[b_quantity]

4

FROM

(SELECT [b name],

 

5

 

 

[b_quantity],

 

6

 

 

RANK() OVER (ORDER BY [b quantity] DESC) AS [rn]

7

 

FROM

[books]) AS [temporary data]

8

WHERE

[rn] = 1

 

 

Oracle I Решение 2.1.8.С

 

 

1

-- Вариант 1: использование MAX

2

SELECT "b name",

 

3

 

"b_quanti ty"

 

4

FROM

"books"

 

 

5

WHERE

"b quantity" = (SELECT MAX "b quantity"i

6

 

 

FROM

"books")

1

-- Вариант 2: использование RANK

2

SELECT "b name",

 

3

 

"b_quanti ty"

 

4

FROM

(SELECT "b name",

 

5

 

 

"b_quantity",

 

6

 

 

RANK() OVER (ORDER BY "b quantity" DESC) AS "rn"

7

 

FROM

"books")

 

8

WHERE

"rn" = 1

 

 

 

 

 

 

В случае с MySQL доступен только один вариант решения2: подзапросом (строки 5-6) выяснить максимальное количество экземпляров книг и использовать полученное число как условие выборки. Этот же вариант решения прекрасно рабо-

тает в MS SQL Server и Oracle.

MS SQL Server и Oracle поддерживают т.н. «оконные (ранжирующие) функции», позволяющие реализовать второй вариант решения. Обратите внимание на строку 6 этого варианта: MS SQL Server требует явного именования подзапроса, являющегося источником данных, а Oracle не требует (именование подзапроса допустимо, но в данном случае не является обязательным).

2 На самом деле, в MySQL можно эмулировать ранжирующие (оконные) функции. Примеры такой эмуляции представлены в исследовании 2.1.8.EXP.D{48} и решениях 2.2.7.d{119}, 2.2.9.d{139}.

Работа с MySQL, MS SQL Server и Oracle в примерах © Богдан Марчук Стр: 51/545

Пример 8: поиск множества минимальных и максимальных значений

Функция RANK позволяет ранжировать строки выборки (т.е. расставить их на

1-е, 2-е, 3-е, 4-е и так далее места) по указанному условию (в нашем случае — по убыванию количества экземпляров книг). Книги с одинаковыми количествами экземпляров будут занимать одинаковые места, а на первом месте будут книги с максимальным количеством экземпляров. Остаётся только показать эти книги, занявшие первое место.

Для наглядности рассмотрим, что возвращает подзапрос, представленный строками 4-7 второго варианта решения для MS SQL Server и Oracle:

MS SQL Решение 2.1.8.c (фрагмент запроса)

 

1

 

SELECT [b name]

 

 

 

 

2

 

 

[b_quantity]

 

 

 

 

3

 

 

RANK() OVER (ORDER BY [b quantity] DESC) AS [rn]

 

4

 

FROM

[books]

 

 

 

 

 

Oracl

 

 

 

 

 

 

e

 

і

Решение 2.1.8.c (фрагмент запроса)

|

 

 

 

1

 

SELECT "b name"

 

 

 

 

2

 

 

"b quantity"

 

 

 

 

3

 

 

RANK() OVER (ORDER BY "b quantity" DESC) AS "rn"

 

4

 

FROM

"books"

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

b_name

b_quantity

rn

 

Курс теоретической физики

12

1

 

Искусство программирования

7

2

 

Основание и империя

5

3

 

Сказка о рыбаке и рыбке

3

4

 

Язык программирования C++

3

4

 

Евгений Онегин

2

6

 

Психология программирования

1

7

 

Исследование 2.1.8.EXP.B. Что работает быстрее — MAX или RANK? Ис-

пользуем ранее созданную и наполненную данными (десять миллионов записей) таблицу test counts{21} и проверим.

Медианные значения времени после ста выполнений запросов таковы:

 

MS QL Server

Oracle

MAX

0.009

0.341

RANK

6.940

0.818

Вариант с MAX в данном случае работает быстрее. Однако в исследовании 2.2.7. EXP.A{119} будет показана обратная ситуация, когда вариант с ранжированием окажется значительно быстрее варианта с функцией MAX. Таким образом, вновь и

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

Работа с MySQL, MS SQL Server и Oracle в примерах © Богдан Марчук Стр: 52/545

Пример 8: поиск множества минимальных и максимальных значений

Решение 2.1.8.d{43}.

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

1-- Вариант 1: использование ALL и подзапроса

2SELECT 'b name',

3'b_quanti ty'

4

FROM

'books' AS 'ext'

 

 

5

WHERE

'b quantity' > ALL (SELECT

'b quantity'

 

6

 

FROM

'books' AS 'int'

7

 

WHERE

'ext' 'b id'

!=

Решение 2.1.8.d

MS SQL I Решение 2.1.8.d

'b id')

1-- Вариант 1: использование ALL и подзапроса

2SELECT [b name],

3[b_quantity]

4

FROM

[books] AS [ext]

 

5

WHERE

[b quantity] > ALL (SELECT

[b quantity]

6

 

FROM

[books] AS [int]

7

 

WHERE

[ext] [b_id] != [int] [b_id])

1-- Вариант 2: использование общего табличного выражения и RANK

2WITH [ranked]

3AS (SELECT [b name],

4

 

[b_quantity],

5

 

RANK()

6

 

OVER (

7

 

ORDER BY [b quantity] DESC) AS [rank]

8

FROM

[books]),

9[counted]

10AS (SELECT [rank]

11

 

COUNT(*) AS [competitors]

12

FROM

[ranked]

13GROUP BY [rank])

14SELECT [b name],

15[b quantity]

16 FROM [ranked]

17JOIN [counted]

18ON [ranked] [rank] = [counted] [rank]

19WHERE [counted] [rank] = 1

20AND [counted] [competitors] = 1

 

Oracl

і

Решение 2.1.8.d

I

 

e

 

 

 

 

 

 

 

 

1

 

-- Вариант 1: использование ALL и подзапроса

2

 

SELECT "b name"

 

 

3

 

 

 

"b_quanti ty"

 

4

 

FROM

"books" "ext"

 

5

 

WHERE

"b quantity" > ALL (SELECT "b quantity"

6

 

 

 

 

FROM

"books" "int"

7

 

 

 

 

WHERE

"ext" "b id" != "int" "b id"

Работа с MySQL, MS SQL Server и Oracle в примерах © Богдан Марчук Стр: 53/545

Пример 8: поиск множества минимальных и максимальных значений

Oracl

і Решение 2.1.8.d (продолжение) |

e

 

 

 

1

-- Вариант 2: использование общего табличного выражения и RANK

2

WITH "ranked"

 

3

 

AS (SELECT "b name",

4

 

 

"b_quantity",

5

 

 

RANK()

6

 

 

OVER (

7

 

 

ORDER BY "b quantity" DESC) AS "rank"

8

 

FROM

"books" ,

9

 

"counted"

 

10

 

AS (SELECT "rank",

11

 

 

COUNT(*) AS "competitors"

12

 

FROM

"ranked"

13

 

GROUP

BY "rank")

14

SELECT "b name"

 

15

 

"b_quanti ty"

16

FROM

"ranked"

 

17

 

JOIN "counted"

18

 

ON "ranked" "rank" = "counted" "rank"

19

WHERE

"counted" "rank" = 1

20

 

AND "counted" "competitors" = 1

Первые варианты решения для всех трёх СУБД идентичны (только в Oracle здесь для именования таблицы между исходным именем и псевдонимом не должно быть ключевого слова AS). Теперь поясним, как это работает.

Одна и та же таблица books фигурирует в запросе 2.1.8.d дважды — под именем ext (для внешней части запроса) и int (для внутренней части запроса). Это нужно затем, чтобы СУБД могла применить условие выборки, представленное в строке 7: для каждой строки таблицы ext выбрать значение поля b_quantity из всех строк таблицы int кроме той строки, которая сейчас рассматривается в таблице ext.

Это фундаментальный принцип построения т.н. коррелирующих запросов, потому покажем логику работы СУБД графически. Итак, у нас есть семь книг с идентификаторами от 1 до 7:

Строка из таблицы ext

Какие строки анализируются в таблице int

1

2, 3, 4, 5, 6, 7 {т.е. все, кроме 1-й}

2

1, 3, 4, 5, 6, 7 {т.е. все, кроме 2-й}

3

1,2, 4, 5, 6, 7 {т.е. все, кроме 3-й}

4

1,2, 3, 5, 6, 7 {т.е. все, кроме 4-й}

5

1,2, 3, 4, 6, 7 {т.е. все, кроме 5-й}

6

1,2, 3, 4, 5, 7 {т.е. все, кроме 6-й}

7

1,2, 3, 4, 5, 6 {т.е. все, кроме 7-й}

Выбрав соответствующие значения поля b_quantity, СУБД проверяет, чтобы значение, выбранное из таблицы ext было больше каждого из значений, выбранных из таблицы int:

Значение ext.b_quantity

Набор значений int.b_quantity

2

3, 5, 1,3, 12, 7

3

2, 5, 1,3, 12, 7

5

2, 3, 1,3, 12, 7

1

2, 3, 5, 3, 12, 7

3

2, 3, 5, 1, 12, 7

12

2, 3, 5, 1, 3, 7

7

2, 3, 5, 1,3, 12

Работа с MySQL, MS SQL Server и Oracle в примерах © Богдан Марчук Стр: 54/545

Источник: https://studfile.net/preview/16420333/